//  This may look like C code, but it is really -*- C++ -*-

//  ------------------------------------------------------------------
//  The Goldware Library
//  Copyright (C) 1990-1999 Odinn Sorensen
//  ------------------------------------------------------------------
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Library General Public License for more details.
//
//  You should have received a copy of the GNU Library General Public
//  License along with this program; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
//  MA 02111-1307, USA
//  ------------------------------------------------------------------
//  $Id$
//  ------------------------------------------------------------------
//  Fido/Opus/FTSC (*.MSG) type handling
//  ------------------------------------------------------------------

#include <cstdlib>
#include <gdbgtrk.h>
#include <gdirposx.h>
#include <gstrall.h>
#include <gmofido.h>


//  ------------------------------------------------------------------

void FidoArea::raw_scan(bool __scanpm)
{

    GFTRK("FidoArea::raw_scan");

    int _wasopen = isopen;
    if(not _wasopen)
    {
        isopen++;
        if(ispacked())
        {
            Path tmp;
            strxcpy(tmp, path(), sizeof(Path));
            StripBackslash(tmp);
            const char* newpath = Unpack(tmp);
            if(newpath == NULL)
                packed(false);
            else
            {
                strcpy(tmp, newpath);
                AddBackslash(tmp);
            }
            set_real_path(newpath ? tmp : path());
        }
    }

    uint _active = 0;
    uint32_t* _msgnoptr = NULL;
    uint32_t* _msgndx = Msgn->tag;

    gposixdir d(real_path());
    if(d.ok)
    {
        if(WideDebug)
            WideLog->printf("- %s/*.msg", d.fullpath());
        const gdirentry *de;
        while((de = d.nextentry("*.msg", true)) != NULL)
        {
            if(WideDebug)
                WideLog->printf("- %s", de->name.c_str());
            uint _msgno = (uint)atol(de->name.c_str());
            if(_msgno)
            {
                if((_active % FIDO_SCANBUFSIZE) == 0)
                {
                    _msgndx = Msgn->Resize(_active+FIDO_SCANBUFSIZE);
                    _msgnoptr = _msgndx + _active;
                }
                *_msgnoptr++ = _msgno;
                _active++;
            }
        }
    }
    else
    {
        if(WideDebug)
            WideLog->printf("- Invalid path: %s", real_path());
    }

    // Sort the index
    Msgn->SetCount(_active);
    Msgn->Sort();

    // Get the lastread msgno
    word _lastread = 0;
    int _fh = ::sopen(AddPath(real_path(), wide->fidolastread), O_RDONLY|O_BINARY, WideSharemode, S_STDRD);
    if(_fh != -1)
    {
        lseekset(_fh, wide->userno, sizeof(word));
        read(_fh, &_lastread, sizeof(word));
        ::close(_fh);
    }

    _msgnoptr = _msgndx;

    uint _count = 1;
    uint _lastread_reln = 0;
    uint _firstmsgno = 0;
    uint _lastmsgno = 0;
    uint _lastreadfound = 0;
    FidoHdr _hdr;

    if(_active)
    {

        _firstmsgno = Msgn->at(0);
        _lastmsgno = Msgn->at(_active-1);

        while(1)
        {

            // Set lastread pointer
            if((*_msgnoptr >= _lastread) and (_lastread_reln == 0))
            {
                _lastreadfound = *_msgnoptr;
                _lastread_reln = _count - (*_msgnoptr != _lastread ? 1 : 0);
                break;
            }
            if((++_count) > _active)
                break;
            _msgnoptr++;
        }
    }

    // If the exact lastread was not found
    if(_active and (_lastreadfound != _lastread))
    {

        // Higher than highest or lower than lowest?
        if(_lastread > _lastmsgno)
            _lastread_reln = _active;
        else if(_lastread < _firstmsgno)
            _lastread_reln = 0;
    }

    // Read highwater mark
    data->highwatermark = 0;
    if(isecho() and wide->fidohwmarks)
    {
        _fh = test_open(AddPath(real_path(), "1.msg"), O_RDONLY|O_BINARY, WideSharemode);
        if(_fh != -1)
        {
            read(_fh, &_hdr, sizeof(FidoHdr));
            data->highwatermark = _hdr.replyto;
            ::close(_fh);
        }
    }

    // Update area data
    Msgn->SetCount(_active);
    lastread = _lastread_reln;
    lastreadentry = _lastreadfound;

    if(__scanpm)
    {
        char hdr_by[37];
        int umax = (WidePersonalmail & PM_ALLNAMES) ? WideUsernames : 1;
        bool gotpm = false;

        PMrk->ResetAll();

        for(uint i = lastread+1; i<=Msgn->Count(); i++)
        {
            // Build message filename
            Path _msgfile;
            uint msgno = Msgn->CvtReln(i);
            build_msgname(_msgfile, msgno);

            // Open the message file
            int _fh = test_open(_msgfile, O_RDONLY|O_BINARY, WideSharemode);
            if(_fh >= 0)
            {
                read(_fh, &_hdr, sizeof(FidoHdr));
                strxcpy(hdr_by, _hdr.to, 36);

                for(int u=0; u<umax; u++)
                {
                    if(strieql(WideUsername[u], hdr_by))
                    {
                        gotpm = true;
                        break;
                    }
                }
                if(gotpm)
                {
                    if(not(_hdr.attr & FIDO_RECEIVED))
                        PMrk->Append(msgno);

                    gotpm = false;
                }
                ::close(_fh);
            }
        }
    }

    if(WideDebug)
    {
        WideLog->printf("- %s: t:%u, l:%u, fm:%u, hm:%u, lr:%u, u:%u, pm: %i",
                        echoid(),
                        Msgn->Count(),
                        lastread,
                        _firstmsgno,
                        _lastmsgno,
                        _lastread,
                        fidowide->userno,
                        __scanpm ? (int)PMrk->Count() : -1
                       );
    }

    if(not _wasopen)
    {
        if(ispacked())
        {
            CleanUnpacked(real_path());
        }
        isopen--;
    }

    GFTRK(0);
}


//  ------------------------------------------------------------------

void FidoArea::scan()
{

    GFTRK("FidoArea::scan");

    scan_area();

    GFTRK(0);
}


//  ------------------------------------------------------------------

void FidoArea::scan_area()
{

    GFTRK("FidoArea::scan_area");

    bool was_open = make_bool(data);
    if(not was_open)
        data_open();
    raw_scan(false);
    if(not was_open)
        data_close();

    GFTRK(0);
}


//  ------------------------------------------------------------------

void FidoArea::scan_area_pm()
{

    GFTRK("FidoArea::scan_area_pm");

    bool was_open = make_bool(data);
    if(not was_open)
        data_open();
    raw_scan(true);
    if(not was_open)
        data_close();

    GFTRK(0);
}


//  ------------------------------------------------------------------

void FidoArea::set_highwater_mark()
{

    if(isecho())
    {
        open();
        gmsg hmsg;
        hmsg.msgno = 1;
        if(load_hdr(&hmsg))
        {
            hmsg.link.to_set(Msgn->CvtReln(Msgn->Count()));
            save_hdr(GMSG_UPDATE, &hmsg);
        }
        close();
    }
}


//  ------------------------------------------------------------------

void FidoArea::reset_highwater_mark()
{

    if(isecho())
    {
        open();
        gmsg hmsg;
        hmsg.msgno = 1;
        if(load_hdr(&hmsg))
        {
            hmsg.link.to_set(Msgn->CvtReln(1));
            save_hdr(GMSG_UPDATE, &hmsg);
        }
        close();
    }
}


//  ------------------------------------------------------------------
