//  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$
//  ------------------------------------------------------------------
//  JAM msgbase implementation, scanning.
//  ------------------------------------------------------------------

#include <cerrno>
#include <gdbgerr.h>
#include <gmemdbg.h>
#include <gdbgtrk.h>
#include <gstrall.h>
#include <gcrcall.h>
#include <gmojamm.h>


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

void JamArea::data_open()
{

    wide = jamwide;
    data = jamdata + (jamdatano++);
    data->fhjhr = data->fhjdt = data->fhjdx = data->fhjlr = data->fhjhw = -1;
    data->islocked = false;
    data->timesposted = 0;
    data->lastpos = 0;
}


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

void JamArea::data_close()
{

    jamdatano--;
}


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

int JamArea::test_open(const char* file)
{

    GFTRK("JamArea::test_open");

    int fh;
    long tries = 0;

    do
    {

        fh = ::sopen(file, O_RDWR|O_CREAT|O_BINARY, WideSharemode, S_STDRW);
        if(fh == -1)
        {

            // Tell the world
            if((errno != EACCES) or (PopupLocked(++tries, false, file) == false))
            {
                WideLog->ErrOpen();
                raw_close();
                WideLog->printf("! A JAM msgbase file could not be opened.");
                WideLog->printf(": %s.", file);
                WideLog->ErrOSInfo();
                OpenErrorExit();
            }
        }
    }
    while(fh == -1);

    // Remove the popup window
    if(tries)
        PopupLocked(0, 0, NULL);

    GFTRK(0);

    return fh;
}


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

void JamArea::raw_open()
{

    GFTRK("JamArea::raw_open");

    Path file;
    sprintf(file, "%s.jhr", real_path());
    data->fhjhr = test_open(file);
    sprintf(file, "%s.jdx", real_path());
    data->fhjdx = test_open(file);
    sprintf(file, "%s.jlr", real_path());
    data->fhjlr = test_open(file);
    if(not just_scanning)
    {
        sprintf(file, "%s.jdt", real_path());
        data->fhjdt = test_open(file);
        if(not jamwide->smapihw)
        {
            sprintf(file, "%s.cmhw", real_path());
            data->fhjhw = ::sopen(file, O_RDWR|O_BINARY, WideSharemode, S_STDRW);
        }
    }

    GFTRK(0);
}


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

void JamArea::raw_close()
{

    GFTRK("JamArea::raw_close");

    if(data->fhjlr != -1)
    {
        ::close(data->fhjlr);
        data->fhjlr = -1;
    }
    if(data->fhjdx != -1)
    {
        ::close(data->fhjdx);
        data->fhjdx = -1;
    }
    if(data->fhjdt != -1)
    {
        ::close(data->fhjdt);
        data->fhjdt = -1;
    }
    if(data->fhjhr != -1)
    {
        ::close(data->fhjhr);
        data->fhjhr = -1;
    }
    if(data->fhjhw != -1)
    {
        ::close(data->fhjhw);
        data->fhjhw = -1;
    }

    GFTRK(0);
}


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

void JamArea::open_area()
{

    GFTRK("JamArea::open_area");

    // Open the msgbase files
    raw_open();

    // Read the header info
    memset(&data->hdrinfo, 0, sizeof(JamHdrInfo));
    read(data->fhjhr, &data->hdrinfo, sizeof(JamHdrInfo));

    if(not jamwide->smapihw and (data->fhjhw != -1))
    {
        lseek(data->fhjhw, 0, SEEK_SET);
        read(data->fhjhw, &data->highwater, sizeof(int32_t));
    }
    else
        data->highwater = -1;

    // Is the signature invalid?
    if (memcmp(data->hdrinfo.signature, JAM_SIGNATURE, 4))
    {
        // Initialize header info
        memcpy(data->hdrinfo.signature, JAM_SIGNATURE, 4);
        time32_t a  = gtime(NULL);
        struct tm tp;
        ggmtime(&tp, &a);
        tp.tm_isdst = -1;
        time32_t b  = gmktime(&tp);
        data->hdrinfo.datecreated = a + a - b;
        data->hdrinfo.passwordcrc = 0xFFFFFFFFL;
        data->hdrinfo.basemsgnum  = 1;

        // Write header info
        lseekset(data->fhjhr, 0);
        write(data->fhjhr, &data->hdrinfo, sizeof(JamHdrInfo));
    }

    // Adjust base msg number if necessary
    if(data->hdrinfo.basemsgnum == 0)
        data->hdrinfo.basemsgnum = 1;

    // Seek to beginning of the .JLR
    lseekset(data->fhjlr, 0);

    // Search for the userid (not the usercrc?!)
    data->lastpos = 0;
    int founduser = false;
    while(read(data->fhjlr, &data->lastrec, sizeof(JamLast)) == sizeof(JamLast))
    {
        if(data->lastrec.usercrc == wide->usercrc)
        {
            founduser = true;
            break;
        }
        data->lastpos++;
    }

    // If user was not found, init the lastread with our values
    if(not founduser)
    {
        data->lastrec.usercrc = wide->usercrc;
        data->lastrec.userid  = wide->userid;
        data->lastrec.lastread = 0;
        data->lastrec.highread = 0;
    }

    data->timesposted = 0;

    GFTRK(0);
}


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

void JamArea::raw_scan(int __keep_index, int __scanpm)
{

    GFTRK("JamRawScan");

    // Open the msgbase if it wasn't already
    int _was_open = isopen;
    if(not _was_open)
    {
        if(not __keep_index or __scanpm)
            just_scanning = true;
        if(ispacked())
        {
            const char* newpath = Unpack(path());
            if(newpath == NULL)
                packed(false);
            set_real_path(newpath ? newpath : path());
        }
        isopen++;
        data_open();
        open_area();
        just_scanning = false;
    }

    // Get some sizes
    int32_t _jdxlen = filelength(data->fhjdx);
    uint _jdxsize  = (uint)_jdxlen;
    uint _jdxtotal = _jdxsize / sizeof(JamIndex);

    // (Re)Allocate message index
    if(__keep_index)
        Msgn->Resize(_jdxtotal);

    // Allocate buffer to hold .JDX data
    JamIndex* _jdxbuf = (JamIndex*)throw_malloc(_jdxsize+1);

    // Read the entire .JDX file into memory
    lseekset(data->fhjdx, 0);
    read(data->fhjdx, _jdxbuf, _jdxsize);

    // Variables for the loop
    uint _active = 0;
    uint _firstmsgno = 0;
    uint _lastmsgno = 0;
    uint _lastreadfound = 0;
    uint _msgno = data->hdrinfo.basemsgnum;
    uint _total = data->hdrinfo.basemsgnum + _jdxtotal;
    uint _lastread = data->lastrec.lastread;
    uint _lastread_reln = 0;
    uint32_t* _msgndxptr = Msgn->tag;
    JamIndex* _jdxptr = _jdxbuf;

    // Fill message index
    while(_msgno < _total)
    {
        if(_jdxptr->hdroffset != 0xFFFFFFFFL)
        {
            _active++;
            if(not _firstmsgno)
                _firstmsgno = _msgno;
            if(__keep_index)
                *_msgndxptr++ = _msgno;
            if((_msgno >= _lastread) and (_lastread_reln == 0))
            {
                _lastreadfound = _msgno;
                _lastread_reln = (uint)(_active - (_msgno != _lastread ? 1 : 0));
            }
            _lastmsgno = _msgno;
        }
        _jdxptr++;
        _msgno++;
    }

    // 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;
    }

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

    // Scan for personal mail
    if(__scanpm)
    {
        INam uname;
        int umax = (WidePersonalmail & PM_ALLNAMES) ? WideUsernames : 1;
        dword* ucrc = (dword*)throw_calloc(umax, sizeof(dword));
        for(int uc=0; uc<umax; uc++)
        {
            jamstrlwr(strcpy(uname, WideUsername[uc]));
            ucrc[uc] = strCrc32(uname, NO, CRC32_MASK_CCITT);
        }
        PMrk->ResetAll();
        uint n = lastread + 1;
        uint cnt = Msgn->Count();
        int gotpm = false;
        while(n <= cnt)
        {
            JamIndex* idx = _jdxbuf + (uint)(Msgn->at(n-1) - data->hdrinfo.basemsgnum);
            for(int u=0; u<umax; u++)
            {
                if(idx->usercrc == ucrc[u])
                {
                    gotpm = true;
                    break;
                }
            }
            if(gotpm)
            {
                JamHdr hdr;
                lseekset(data->fhjhr, idx->hdroffset);
                read(data->fhjhr, &hdr, sizeof(JamHdr));
                if(not (hdr.attribute & JAMATTR_READ))
                {
                    if(not (hdr.attribute & JAMATTR_DELETED))
                    {
                        PMrk->Append(hdr.messagenumber);
                    }
                }
                gotpm = false;
            }
            n++;
        }
        throw_free(ucrc);
    }

    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,
                        data->lastpos,
                        __scanpm ? (int)PMrk->Count() : -1
                       );
    }

    // Free the .JDX buffer
    throw_free(_jdxbuf);

    // Close the msgbase again if we opened it in here
    if(not _was_open)
    {
        raw_close();
        data_close();
        if(ispacked())
        {
            CleanUnpacked(real_path());
        }
        isopen--;
    }

    GFTRK(0);
}


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

void JamArea::scan()
{

    GFTRK("JamArea::scan");

    raw_scan(true);

    GFTRK(0);
}


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

void JamArea::scan_area()
{

    GFTRK("JamArea::scan_area");

    raw_scan(false);

    GFTRK(0);
}


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

void JamArea::scan_area_pm()
{

    GFTRK("JamArea::scan_area_pm");

    raw_scan(true, true);
    Msgn->Reset();

    GFTRK(0);
}


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