//  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, "%s %s", 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);
}


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