//  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$
//  ------------------------------------------------------------------
//  PCBoard msgbase handling.
//  ------------------------------------------------------------------


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

#include <gdbgerr.h>
#include <gdbgtrk.h>
#include <gstrall.h>
#include <gutlmisc.h>

#include <gmopcbd.h>


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

void PcbArea::lock()
{

    GFTRK("PcbLock");

    if(not data->islocked)
    {
        if(WideCanLock)
        {
            long _tries = 0;
            while(::lock(data->fhmsg, 16, 6) == -1)
            {
                if(PopupLocked(++_tries, true, real_path()) == false)
                {
                    WideLog->ErrLock();
                    raw_close();
                    WideLog->printf("! A PCBoard msgbase file could not be locked.");
                    WideLog->printf(": %s.", real_path());
                    WideLog->ErrOSInfo();
                    LockErrorExit();
                }
            }
            if(_tries)
                PopupLocked(0, 0, NULL);
        }
        lseekset(data->fhmsg, 0);
        read(data->fhmsg, &data->base, sizeof(PcbBase));
        lseekset(data->fhmsg, 0);
        memcpy(data->base.locked, "LOCKED", 6);
        write(data->fhmsg, &data->base, sizeof(PcbBase));
        data->base.highmsgno = B2L(data->base.highmsgno);
        data->base.lowmsgno  = B2L(data->base.lowmsgno);
        data->base.active    = B2L(data->base.active);
        data->islocked = true;
    }

    GFTRK(0);
}


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

void PcbArea::unlock()
{

    GFTRK("PcbUnlock");

    if(WideCanLock and data->islocked)
        ::unlock(data->fhmsg, 16, 6);
    lseekset(data->fhmsg, 0);
    memset(data->base.locked, ' ', 6);
    data->base.highmsgno = L2B(data->base.highmsgno);
    data->base.lowmsgno  = L2B(data->base.lowmsgno);
    data->base.active    = L2B(data->base.active);
    write(data->fhmsg, &data->base, sizeof(PcbBase));
    data->base.highmsgno = B2L(data->base.highmsgno);
    data->base.lowmsgno  = B2L(data->base.lowmsgno);
    data->base.active    = B2L(data->base.active);
    data->islocked = false;

    GFTRK(0);
}


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

void PcbArea::save_message(int __mode, gmsg* __msg, PcbHdr& __hdr)
{

    // Lock and refresh base
    int _was_locked = data->islocked;
    if(not _was_locked)
        lock();

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

    // Find msgno and index offset
    PcbIdx _idx;
    uint32_t oldmsgno = __msg->msgno;
    int32_t oldtxtstart = __msg->txtstart;
    int32_t oldtxtlength = __msg->txtlength;
    if(__mode & GMSG_TXT)
    {
        if(data->base.lowmsgno == 0)
            data->base.lowmsgno = 1;
        data->base.highmsgno++;
        data->base.active++;
        __msg->msgno = data->base.highmsgno;
        Msgn->Append(__msg->msgno);
    }
    int32_t _idxoffset = (__msg->msgno-data->base.lowmsgno)*sizeof(PcbIdx);
    __hdr.blocks = (byte)__msg->txtblocks;
    _idx.num = __msg->msgno;
    _idx.reserved[0] = _idx.reserved[1] = _idx.reserved[2] = 0;

    // Convert msgno and replylinks
    __hdr.msgno = L2B(__msg->msgno);
    __hdr.refno = L2B(__msg->link.to());

    // Convert attributes
    char _status = __msg->pcboard.status;
    int _pvt = __msg->attr.pvt();
    int _rcv = __msg->attr.rcv();
    if(_rcv)
    {
        switch(_status)
        {
        case '$':
            __hdr.status = '#';
            break;
        case '!':
            __hdr.status = '#';
            break;
        case '%':
            __hdr.status = '^';
            break;
        case '~':
            __hdr.status = '`';
            break;
        case '*':
            __hdr.status = '+';
            break;
        case ' ':
            __hdr.status = '-';
            break;
        default:
            __hdr.status = _pvt ? '+' : '-';
        }
    }
    else
    {
        if(_status and not _pvt)
            __hdr.status = _status;
        else if(_pvt)
            __hdr.status = '*';
        else
            __hdr.status = ' ';
    }
    _idx.status = __hdr.status;
    __hdr.activestatus = (__mode & GMSG_DELETE) ? '\xE2' : '\xE1';
    __hdr.echoed = ((isnet() or isecho()) and not isinternet()) ? 'E' : ' ';

    // Convert dates and times
    char _dtbuf[9];
    struct tm _tm;
    ggmtime(&_tm, &__msg->written);
    memcpy(__hdr.date, strftimei(_dtbuf, 9, __msg->attr.uns() ? "%d-%m-%y" : "%d-%m\xC4%y", &_tm), 8);
    memcpy(__hdr.time, strftimei(_dtbuf, 6, "%H:%M", &_tm), 5);

    _idx.date = (word)YMD2JDN(1900+_tm.tm_year, _tm.tm_mon+1, _tm.tm_mday);

    if (__msg->link.first())
    {
        __hdr.hasreply = 'R';
        ggmtime(&_tm, &__msg->pcboard.reply_written);
        int _year = _tm.tm_year % 100;
        __hdr.replydate = L2B((10000L*_year) + (100L*(_tm.tm_mon+1)) + _tm.tm_mday);
        memcpy(__hdr.replytime, strftimei(_dtbuf, 6, "%H:%M", &_tm), 5);
    }
    else
    {
        __hdr.hasreply = ' ';
        __hdr.replydate = L2B(0);
        memcpy(__hdr.replytime, "     ", 5);
    }

    // Convert names, subject and password
    char tobuf[150];
    char bybuf[150];
    strcpy(tobuf, __msg->to);
    strcpy(bybuf, __msg->by);
    int _tolen = strlen(tobuf);
    int _bylen = strlen(bybuf);
    int _relen = strlen(__msg->re);
    int _pwlen = strlen(__msg->pcboard.password);
    memset(__hdr.destname, ' ', 25);
    memset(__hdr.origname, ' ', 25);
    memset(__hdr.subject,  ' ', 25);
    memset(__hdr.password, ' ', 12);
    strncpy(__hdr.destname, tobuf, MinV(_tolen, 25));
    strncpy(__hdr.origname, bybuf, MinV(_bylen, 25));
    strncpy(__hdr.subject,  __msg->re, MinV(_relen, 25));
    strncpy(__hdr.password, __msg->pcboard.password, MinV(_pwlen, 12));
    memcpy(_idx.to, __hdr.destname, 25);
    memcpy(_idx.from, __hdr.origname, 25);
    if(isnet())
    {
        char toaddr[40];
        char byaddr[40];
        __msg->dest.make_string(toaddr);
        strcpy(byaddr, " [");
        __msg->orig.make_string(byaddr+2);
        strcat(byaddr, "]");
        sprintf(tobuf+strlen(tobuf), "@%s%s%s%s",
                toaddr,
                __msg->attr.cra() ? " +C" : "",
                __msg->attr.dir() ? " +D" : "",
                byaddr
               );
        _tolen = strlen(tobuf);
        _bylen = strlen(bybuf);
    }

    // Determine size of the msg text including extended headers
    uint _txtlen = 0;
    if(__mode & GMSG_TXT)
    {
        __msg->txtlength = _txtlen = strlen(__msg->txt);
        if((_tolen > 25) or isnet())
        {
            __msg->txtlength += sizeof(PcbExtHdr);
            __hdr.exthdrflags |= 0x01;
        }
        if((_bylen > 25) or isnet())
        {
            __msg->txtlength += sizeof(PcbExtHdr);
            __hdr.exthdrflags |= 0x02;
        }
        if(_relen > 25)
        {
            __msg->txtlength += sizeof(PcbExtHdr);
            __hdr.exthdrflags |= 0x04;
        }

        // Translate msg text to PCB linefeeds if running non-foreign system
        if(not wide->foreign)
            strchg(__msg->txt, 0x0D, 0xE3);

        // Calculate new number of blocks
        __hdr.blocks = (byte)(1 + (__msg->txtlength/128) + ((__msg->txtlength%128)?1:0));

        // Determine where to write the message text
        __msg->txtstart = filelength(data->fhmsg);
        __msg->txtblocks = __hdr.blocks;
    }

    // Write index
    _idx.offset = (__mode & GMSG_DELETE) ? -__msg->txtstart : __msg->txtstart;
    lseekset(data->fhidx, _idxoffset);
    write(data->fhidx, &_idx, sizeof(PcbIdx));

    // Write header
    lseekset(data->fhmsg, AbsV(_idx.offset));
    write(data->fhmsg, &__hdr, sizeof(PcbHdr));

    // Writing msg text?
    if(__mode & GMSG_TXT)
    {

        uint _txtlenwritten = 0;

        // Write extended headers
        if((_tolen > 25) or isnet())
        {
            PcbExtHdr _ehdr;
            _ehdr.id = 0x40FF;
            _ehdr.colon = ':';
            _ehdr.status = 'N';
            _ehdr.separator = wide->foreign ? '\x0D' : '\xE3';
            memcpy(_ehdr.function, "TO     ", 7);
            memset(_ehdr.desc, ' ', 60);
            memcpy(_ehdr.desc, tobuf, MinV(60,_tolen));
            write(data->fhmsg, &_ehdr, sizeof(PcbExtHdr));
            _txtlenwritten += sizeof(PcbExtHdr);
            if(_tolen > 60)
            {
                memcpy(_ehdr.function, "TO2    ", 7);
                memset(_ehdr.desc, ' ', 60);
                memcpy(_ehdr.desc, tobuf+60, MinV(60,(_tolen-60)));
                write(data->fhmsg, &_ehdr, sizeof(PcbExtHdr));
                _txtlenwritten += sizeof(PcbExtHdr);
            }
        }
        if((_bylen > 25) or isnet())
        {
            PcbExtHdr _ehdr;
            _ehdr.id = 0x40FF;
            _ehdr.colon = ':';
            _ehdr.status = 'N';
            _ehdr.separator = wide->foreign ? '\x0D' : '\xE3';
            memcpy(_ehdr.function, "FROM   ", 7);
            memset(_ehdr.desc, ' ', 60);
            memcpy(_ehdr.desc, bybuf, MinV(60,_bylen));
            write(data->fhmsg, &_ehdr, sizeof(PcbExtHdr));
            _txtlenwritten += sizeof(PcbExtHdr);
            if(_bylen > 60)
            {
                memcpy(_ehdr.function, "FROM2  ", 7);
                memset(_ehdr.desc, ' ', 60);
                memcpy(_ehdr.desc, bybuf+60, MinV(60,(_bylen-60)));
                write(data->fhmsg, &_ehdr, sizeof(PcbExtHdr));
                _txtlenwritten += sizeof(PcbExtHdr);
            }
        }
        if(_relen > 25)
        {
            PcbExtHdr _ehdr;
            _ehdr.id = 0x40FF;
            memcpy(_ehdr.function, "SUBJECT", 7);
            _ehdr.colon = ':';
            memset(_ehdr.desc, ' ', 60);
            memcpy(_ehdr.desc, __msg->re, MinV(60,_relen));
            _ehdr.status = 'N';
            _ehdr.separator = wide->foreign ? '\x0D' : '\xE3';
            write(data->fhmsg, &_ehdr, sizeof(PcbExtHdr));
            _txtlenwritten += sizeof(PcbExtHdr);
        }

        // Special handling of netmails
#if 0
        if(isnet())
        {

            // Write destination address in netmail for FidoPCB
            char _abuf[40], _sbuf[45];
            sprintf(_sbuf, "(%s)%c",
                    __msg->dest.make_string(_abuf),
                    wide->foreign ? '\x0D' : '\xE3'
                   );
            uint _sbuflen = strlen(_sbuf);
            write(data->fhmsg, _sbuf, _sbuflen);
            _txtlenwritten += _sbuflen;

            // Write flags
            int _hld = __msg->attr.hld();
            int _imm = __msg->attr.imm();
            int _cra = __msg->attr.cra();
            int _zon = __msg->attr.zon();
            if(_hld or _imm or _cra or _zon)
            {
                strcpy(_sbuf, "(" /*)*/);
                if(_hld)
                    strcat(_sbuf, "HOLD,");
                if(_imm)
                    strcat(_sbuf, "IMM,");
                if(_cra)
                    strcat(_sbuf, "CRASH,");
                if(_zon)
                    strcat(_sbuf, "INTL,");
                _sbuf[strlen(_sbuf)] = /*(*/ ')';
                _sbuflen = strlen(_sbuf);
                write(data->fhmsg, _sbuf, _sbuflen);
                _txtlenwritten += _sbuflen;
            }
        }
#endif

        // Write message text
        write(data->fhmsg, __msg->txt, _txtlen);
        _txtlenwritten += _txtlen;

        // Write remainder of the last message block (if any)
        uint _remainlen = ((__hdr.blocks-1)*128) - _txtlenwritten;
        if(_remainlen)
        {
            byte _remainrecord[128];
            memset(_remainrecord, ' ', 128);
            write(data->fhmsg, _remainrecord, _remainlen);
        }

        // Translate back
        if(not wide->foreign)
            strchg(__msg->txt, 0xE3, 0x0D);
    }

    if(not (__mode & GMSG_DELETE))
    {
        // Set the mail waiting flag
        int _status = true;
        // Reset it if the msg is being received
        if((__mode & GMSG_UPDATE) and __msg->attr.rcv())
            _status = false;
        // Don't touch the flag if the msg was already received
        if(not (_status and __msg->attr.rcv()))
            pcbwide->user->update_mail_waiting(__msg->to, board(), _status);
    }

    if((__mode & GMSG_TXT) and (__mode & GMSG_UPDATE))
    {
        __msg->msgno = oldmsgno;
        __msg->txtstart = oldtxtstart;
        __msg->txtlength = oldtxtlength;
        del_msg(__msg);
    }

    // Unlock and update base
    if(not _was_locked)
        unlock();

    GFTRK(0);
}


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

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

    GFTRK("PcbSaveHdr");

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


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

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

    GFTRK("PcbSaveMsg");

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


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

void PcbArea::del_msg(gmsg* __msg)
{

    GFTRK("PcbDelMsg");

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


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

void PcbArea::new_msgno(gmsg* msg)
{

    GFTRK("PcbNewMsgno");

    msg->msgno = data->base.highmsgno + 1;

    GFTRK(0);
}


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

void PcbArea::update_timesread(gmsg*)
{

}


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

