//  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, save/delete.
//  ------------------------------------------------------------------

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


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

void JamArea::lock()
{

    GFTRK("JamArea::lock");

    if(not data->islocked and WideCanLock)
    {

        long _tries = 0;

        // Try to get the lock
        while(::lock(data->fhjhr, 0, 1) == -1)
        {

            // Tell the world
            if(PopupLocked(++_tries, true, real_path()) == false)
            {
                WideLog->ErrLock();
                raw_close();
                WideLog->printf("! A JAM msgbase file could not be locked.");
                WideLog->printf(": %s.jhr.", real_path());
                WideLog->ErrOSInfo();
                LockErrorExit();
            }
        }

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

        data->islocked = true;
    }

    // Load fresh copy of the header info
    lseekset(data->fhjhr, 0);
    read(data->fhjhr, &data->hdrinfo, sizeof(JamHdrInfo));

    if(not jamwide->smapihw)
    {
        if(data->fhjhw == -1)
        {
            Path file;
            sprintf(file, "%s.cmhw", real_path());
            data->fhjhw = ::sopen(file, O_RDWR|O_BINARY, WideSharemode, S_STDRW);
        }
        if(data->fhjhw != -1)
        {
            lseek(data->fhjhw, 0, SEEK_SET);
            read(data->fhjhw, &data->highwater, sizeof(int32_t));
        }
        else
            data->highwater = -1;
    }
    else
        data->highwater = -1;

    GFTRK(0);
}


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

void JamArea::unlock()
{

    GFTRK("JamArea::unlock");

    if(data->islocked)
    {
        ::unlock(data->fhjhr, 0, 1);
        data->islocked = false;
    }

    GFTRK(0);
}


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

void JamArea::add_subfield(JamHdr& __hdr, byte*& __subfield, word __loid, word __hiid, char* __data, uint32_t __maxlen)
{

    uint32_t _datlen = strlen(__data);
    __subfield = (byte*)throw_realloc(__subfield, (uint)__hdr.subfieldlen+sizeof(JamSubFieldHdr)+_datlen);
    JamSubField* _subfieldptr = (JamSubField*)(__subfield + (uint)__hdr.subfieldlen);
    _subfieldptr->loid = __loid;
    _subfieldptr->hiid = __hiid;
    _subfieldptr->datlen = MinV(_datlen, __maxlen);
    memcpy(_subfieldptr->buffer, __data, _subfieldptr->datlen);
    __hdr.subfieldlen += sizeof(JamSubFieldHdr) + _subfieldptr->datlen;
}


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

void JamArea::save_message(int __mode, gmsg* __msg, JamHdr& __hdr)
{

    int _was_locked = data->islocked;
    if(not _was_locked)
        lock();

    // Reset header
    memset(&__hdr, 0, sizeof(JamHdr));

    // Init paragraph class
    GParagraph _para;
    if(WideDispsoftcr)
        _para.softcr = SOFTCR;

    // Set default msg text offset and length
    __hdr.offset = __msg->txtstart;
    __hdr.txtlen = __msg->txtlength;

    // Get message text size
    char* _txtcpy = NULL;
    if(__msg->txt and (__mode & GMSG_TXT))
    {

        // Work on a copy of the original msg text
        _txtcpy = throw_strdup(__msg->txt);

        // Convert message text to a paragraph list
        _para.ConvertText(_txtcpy, strlen(_txtcpy));

        // Mark control lines and calculate pure message text size
        __hdr.txtlen = _para.CheckCtrlLines();
    }

    // Determine where to write the message text
    if((__mode & GMSG_NEW) or (__hdr.txtlen > __msg->txtlength))
        __hdr.offset = filelength(data->fhjdt);

    byte* _subfield = NULL;
    char _buf[1024];
    __hdr.subfieldlen = __msg->jam.subfieldlen;

    if(__mode & GMSG_TXT)
    {

        // Build the subfields
        __hdr.subfieldlen = 0;

        // Process addressing kludges
        int _line = 0;
        GParaData* _pdptr = _para.paraidx;
        while(_line < _para.lines)
        {
            if(_pdptr->control > CTRL_KLUDGE)
            {
                word fmpt, topt;
                char buf1[201], buf2[201];
                switch(_pdptr->control)
                {
                case CTRL_INTL:
                    fmpt = __msg->orig.point;
                    topt = __msg->dest.point;
                    sscanf(_pdptr->text+6, "%200s %200s", buf1, buf2);
                    __msg->dest.set(buf1);
                    __msg->orig.set(buf2);
                    __msg->orig.point = fmpt;
                    __msg->dest.point = topt;
                    break;
                case CTRL_FMPT:
                    __msg->orig.point = atow(_pdptr->text+6);
                    break;
                case CTRL_TOPT:
                    __msg->dest.point = atow(_pdptr->text+6);
                    break;
                case CTRL_MSGID:
                    __msg->msgid.reset(_pdptr->text+8, __msg->odom);
                    break;
                }
            }
            _pdptr++;
            _line++;
        }

        if(*__msg->by)
            add_subfield(__hdr, _subfield, JAMSUB_SENDERNAME, 0, __msg->by, JAMSUB_SENDERNAME_LEN);

        if(__msg->orig.net)
        {
            __msg->orig.make_string(_buf, __msg->odom);
            add_subfield(__hdr, _subfield, JAMSUB_OADDRESS, 0, _buf, JAMSUB_OADDRESS_LEN);
        }

        if(*__msg->to)
            add_subfield(__hdr, _subfield, JAMSUB_RECEIVERNAME, 0, __msg->to, JAMSUB_RECEIVERNAME_LEN);

        if(__msg->dest.net)
        {
            __msg->dest.make_string(_buf, __msg->ddom);
            add_subfield(__hdr, _subfield, JAMSUB_DADDRESS, 0, _buf, JAMSUB_DADDRESS_LEN);
        }

        if(*__msg->re)
            add_subfield(__hdr, _subfield, JAMSUB_SUBJECT, 0, __msg->re, JAMSUB_SUBJECT_LEN);

        // Convert kludges
        _line = 0;
        _pdptr = _para.paraidx;
        while(_line < _para.lines)
        {
            if(_pdptr->control > CTRL_KLUDGE)
            {
                uint _offset = 0;
                word _loid = 0;
                uint32_t _maxlen = 0;
                switch(_pdptr->control)
                {
                case CTRL_INTL:
                case CTRL_FMPT:
                case CTRL_TOPT:
                    // Strip these. Data is stored in the address subfields
                    break;
                case CTRL_MSGID:
                    _loid = JAMSUB_MSGID;
                    _maxlen = JAMSUB_MSGID_LEN;
                    _offset = 8;
                    strxcpy(__msg->msgids, _pdptr->text+_offset, sizeof(__msg->msgids));
                    break;
                case CTRL_REPLY:
                    _loid = JAMSUB_REPLYID;
                    _maxlen = JAMSUB_REPLYID_LEN;
                    _offset = 8;
                    strxcpy(__msg->replys, _pdptr->text+_offset, sizeof(__msg->replys));
                    break;
                case CTRL_PID:
                    _loid = JAMSUB_PID;
                    _maxlen = JAMSUB_PID_LEN;
                    _offset = 6;
                    break;
                case CTRL_VIA:
                    _loid = JAMSUB_TRACE;
                    _maxlen = JAMSUB_TRACE_LEN;
                    _offset = 5;
                    break;
                case CTRL_SEENBY:
                    _loid = JAMSUB_SEENBY2D;
                    _maxlen = JAMSUB_SEENBY2D_LEN;
                    _offset = 9;
                    break;
                case CTRL_SEENBY1:
                    _loid = JAMSUB_SEENBY2D;
                    _maxlen = JAMSUB_SEENBY2D_LEN;
                    _offset = 10;
                    break;
                case CTRL_PATH:
                    _loid = JAMSUB_PATH2D;
                    _maxlen = JAMSUB_PATH2D_LEN;
                    _offset = 7;
                    break;
                case CTRL_FLAGS:
                    _loid = JAMSUB_FLAGS;
                    _maxlen = JAMSUB_FLAGS_LEN;
                    _offset = 7;
                    break;
                default:
                    _loid = JAMSUB_FTSKLUDGE;
                    _maxlen = JAMSUB_FTSKLUDGE_LEN;
                    _offset = 1;
                }
                if(_offset)
                    add_subfield(__hdr, _subfield, _loid, 0, _pdptr->text+_offset, _maxlen);
            }
            _pdptr++;
            _line++;
        }
    }

    JamIndex _idx;
    bool was_deleted = false;
    if(__mode & GMSG_NEW)
    {
        __msg->msgno = data->hdrinfo.basemsgnum + (filelength(data->fhjdx)/sizeof(JamIndex));
        __hdr.messagenumber = __msg->msgno;
        _idx.hdroffset = filelength(data->fhjhr);
    }
    else
    {
        __hdr.messagenumber = __msg->msgno;
        lseekset(data->fhjdx, __hdr.messagenumber-data->hdrinfo.basemsgnum, sizeof(JamIndex));
        read(data->fhjdx, &_idx, sizeof(JamIndex));
        if(_idx.hdroffset != 0xFFFFFFFFL)
        {
            lseekset(data->fhjhr, _idx.hdroffset);
            JamHdr oldhdr;
            read(data->fhjhr, &oldhdr, sizeof(JamHdr));
            was_deleted = make_bool(oldhdr.attribute & JAMATTR_DELETED);
            if(oldhdr.subfieldlen != __hdr.subfieldlen)
            {
                oldhdr.attribute |= JAMATTR_DELETED;
                oldhdr.txtlen = 0;
                lseekset(data->fhjhr, _idx.hdroffset);
                write(data->fhjhr, &oldhdr, sizeof(JamHdr));
                _idx.hdroffset = filelength(data->fhjhr);
            }
        }
    }

    if(_idx.hdroffset != 0xFFFFFFFFL)
    {

        memcpy(__hdr.signature, JAM_SIGNATURE, 4);
        __hdr.revision = JAM_REVISION;

        __hdr.replyto   = __msg->link.to();
        __hdr.reply1st  = __msg->link.first();
        __hdr.replynext = __msg->link.next();

        __hdr.cost      = __msg->cost;
        __hdr.timesread = __msg->timesread;
        __hdr.passwordcrc = 0xFFFFFFFFL;

        __hdr.msgidcrc = strCrc32(jamstrlwr(strcpy(_buf, __msg->msgids)), NO, CRC32_MASK_CCITT);
        __hdr.replycrc = strCrc32(jamstrlwr(strcpy(_buf, __msg->replys)), NO, CRC32_MASK_CCITT);
        _idx.usercrc   = strCrc32(jamstrlwr(strcpy(_buf, __msg->to)),     NO, CRC32_MASK_CCITT);

        __hdr.datewritten   = __msg->written;
        __hdr.datereceived  = __msg->received;
        __hdr.dateprocessed = __msg->arrived;

        __hdr.attribute |= __msg->attr.loc() ? JAMATTR_LOCAL       : 0;
        __hdr.attribute |= __msg->attr.trs() ? JAMATTR_INTRANSIT   : 0;
        __hdr.attribute |= __msg->attr.pvt() ? JAMATTR_PRIVATE     : 0;
        __hdr.attribute |= __msg->attr.rcv() ? JAMATTR_READ        : 0;
        __hdr.attribute |= __msg->attr.snt() ? JAMATTR_SENT        : 0;
        __hdr.attribute |= __msg->attr.k_s() ? JAMATTR_KILLSENT    : 0;
        __hdr.attribute |= __msg->attr.a_s() ? JAMATTR_ARCHIVESENT : 0;
        __hdr.attribute |= __msg->attr.hld() ? JAMATTR_HOLD        : 0;
        __hdr.attribute |= __msg->attr.cra() ? JAMATTR_CRASH       : 0;
        __hdr.attribute |= __msg->attr.imm() ? JAMATTR_IMMEDIATE   : 0;
        __hdr.attribute |= __msg->attr.dir() ? JAMATTR_DIRECT      : 0;
        __hdr.attribute |= __msg->attr.zon() ? JAMATTR_GATE        : 0;
        __hdr.attribute |= __msg->attr.frq() ? JAMATTR_FILEREQUEST : 0;
        __hdr.attribute |= __msg->attr.att() ? JAMATTR_FILEATTACH  : 0;
        __hdr.attribute |= __msg->attr.tfs() ? JAMATTR_TRUNCFILE   : 0;
        __hdr.attribute |= __msg->attr.kfs() ? JAMATTR_KILLFILE    : 0;
        __hdr.attribute |= __msg->attr.rrq() ? JAMATTR_RECEIPTREQ  : 0;
        __hdr.attribute |= __msg->attr.cfm() ? JAMATTR_CONFIRMREQ  : 0;
        __hdr.attribute |= __msg->attr.orp() ? JAMATTR_ORPHAN      : 0;
        __hdr.attribute |= __msg->attr.lok() ? JAMATTR_LOCKED      : 0;
        __hdr.attribute |= __msg->attr.del() ? JAMATTR_DELETED     : 0;

        if(isnet())
            __hdr.attribute |= JAMATTR_TYPENET;
        else if(isecho())
            __hdr.attribute |= JAMATTR_TYPEECHO;
        else
            __hdr.attribute |= JAMATTR_TYPELOCAL;

        lseekset(data->fhjhr, _idx.hdroffset);
        write(data->fhjhr, &__hdr, sizeof(JamHdr));

        // Only write subfields if the msg body is written
        if(__mode & GMSG_TXT)
            write(data->fhjhr, _subfield, (uint)__hdr.subfieldlen);

        // Delete msg if requested
        if(__mode & GMSG_DELETE)
        {
            if(not was_deleted)
                data->hdrinfo.activemsgs--;
            __hdr.attribute |= JAMATTR_DELETED;
            _idx.usercrc = 0xFFFFFFFFL;
            if(wide->harddelete)
                _idx.hdroffset = 0xFFFFFFFFL;
        }

        // Write msg index
        lseekset(data->fhjdx, __hdr.messagenumber-data->hdrinfo.basemsgnum, sizeof(JamIndex));
        write(data->fhjdx, &_idx, sizeof(JamIndex));

        // Free subfield buffer
        throw_release(_subfield);

        // Update the header info
        if((__mode & GMSG_NEW) or (was_deleted and not __msg->attr.del()))
            data->hdrinfo.activemsgs++;
        data->hdrinfo.modcounter++;
        lseekset(data->fhjhr, 0);
        write(data->fhjhr, &data->hdrinfo, sizeof(JamHdrInfo));

        // If message text is used
        if(__mode & GMSG_TXT)
        {

            char* _txt = (char*)throw_malloc((uint)(__hdr.txtlen+256));

            // Copy text paragraphs, excluding kludges
            int _line = 0;
            char* _tptr = _txt;
            GParaData* _pdptr = _para.paraidx;
            while(_line < _para.lines)
            {
                if(_pdptr->control < CTRL_KLUDGE)
                {
                    memcpy(_tptr, _pdptr->text, _pdptr->length);
                    _tptr += _pdptr->length;
                    *_tptr++ = CR;
                }
                _pdptr++;
                _line++;
            }
            *_tptr = NUL;

            // Seek to start position of the message text
            lseekset(data->fhjdt, __hdr.offset);

            // Write the message text
            write(data->fhjdt, _txt, (uint)__hdr.txtlen);

            // Release the memory we have used
            throw_free(_txtcpy);
            throw_free(_txt);
        }

        // Update internals if new
        if(__mode & GMSG_NEW)
        {

            // Count our msgs
            data->timesposted++;

            // Update internal array
            Msgn->Append(__msg->msgno);
        }

        // Adjust the highwatermark if required
        if(jamwide->smapihw and __msg->attr.uns())
        {
            if(data->hdrinfo.highwatermark >= __msg->msgno)
                data->hdrinfo.highwatermark = __msg->msgno - 1;
        }
        else if((data->highwater != -1) and (data->fhjhw != -1))
        {
            if(data->highwater >= __msg->msgno)
            {
                data->highwater = __msg->msgno - 1;
                lseek(data->fhjhw, 0, SEEK_SET);
                write(data->fhjhw, &data->highwater, sizeof(int32_t));
            }
        }
    }
    else
    {
        scan();
    }

    if(not _was_locked)
    {
        lseekset(data->fhjhr, 0);
        write(data->fhjhr, &data->hdrinfo, sizeof(JamHdrInfo));
        unlock();
    }

    GFTRK(0);
}


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

void JamArea::save_hdr(int __mode, gmsg* __msg)
{

    GFTRK("JamArea::save_hdr");

    JamHdr _hdr;
    save_message(__mode | GMSG_HDR, __msg, _hdr);
}


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

void JamArea::save_msg(int __mode, gmsg* __msg)
{

    GFTRK("JamArea::save_msg");

    JamHdr _hdr;
    save_message(__mode | GMSG_HDRTXT, __msg, _hdr);
}


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

void JamArea::del_msg(gmsg* __msg)
{

    GFTRK("JamArea::del_msg");

    JamHdr _hdr;
    save_message(GMSG_HDR | GMSG_DELETE, __msg, _hdr);
}


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

void JamArea::new_msgno(gmsg* __msg)
{

    GFTRK("JamArea::new_msgno");

    __msg->msgno = data->hdrinfo.basemsgnum + (filelength(data->fhjdx)/sizeof(JamIndex));

    GFTRK(0);
}


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

void JamArea::update_timesread(gmsg* msg)
{

    GFTRK("JamArea::update_timesread");

    lock();

    lseekset(data->fhjdx, msg->msgno-data->hdrinfo.basemsgnum, sizeof(JamIndex));

    JamIndex idx;
    read(data->fhjdx, &idx, sizeof(JamIndex));

    JamHdr hdr;
    lseekset(data->fhjhr, idx.hdroffset);
    read(data->fhjhr, &hdr, sizeof(JamHdr));

    hdr.timesread = msg->timesread;

    lseekset(data->fhjhr, idx.hdroffset);
    write(data->fhjhr, &hdr, sizeof(JamHdr));

    data->hdrinfo.modcounter++;
    lseekset(data->fhjhr, 0);
    write(data->fhjhr, &data->hdrinfo, sizeof(JamHdrInfo));

    unlock();

    GFTRK(0);
}


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