/*ProfiPacket - packet radio terminal program
  Copyright (C) 1999  Alexander Feigl

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program 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 General Public License for more details.

  You should have received a copy of the GNU 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

  Author:

  Alexander Feigl
  Burachstraße 51

  D-88250 Weingarten

  Mail : Alexander.Feigl@gmx.de
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "global.h"
#include "QSO.h"
#include "GUIProtos.h"
#include "Util.h"

static const unsigned char Compreset_String[]="#COMP:RES#\r";


int QSO_DecompressXPStream(const unsigned char *stream,unsigned char *smemory,
                           void (*func)(const unsigned char *c,int chan,int len),
                           struct QSO *qso,signed long length,int channel)
 {
  const unsigned char *sptr;
  unsigned char tempo[512];

  unsigned long slen;

  sptr=stream;
  slen=length;

  while (slen>0)
   {
    int pass2;

    /* parse for a compression reset */

    if (smemory[1]==0xff)
     {
      if (*sptr=='\r') smemory[1]=0;
     }
     else
     {
      if (*sptr==Compreset_String[smemory[1]])
       {
        ++smemory[1];
        if (smemory[1]>=11)
         {
          smemory[1]=0;
          QSO_ControlXPCompress(channel,6);
          ++sptr;--slen;
          continue;
         }

       }
       else
       {
        if (*sptr=='\r')
         {
          smemory[1]=0;
         }
         else
         {
          if ( (smemory[1]==5) && (*sptr=='C') )
           {
            ++sptr;--slen;
            continue;
           }
           else
           {
            smemory[1]=0xff;
           }
         }
       }
     }

    /* stream parsing */

    if (smemory[0]==0)
     {
      --slen;
      smemory[2]=*(sptr++);
      smemory[0]=1;
      continue;
     }

    pass2=0;

    while (1)
     {
      if (
           (
            (smemory[2]==0xff) &&
            (smemory[0]>=2) &&
            (smemory[0]>=(smemory[3]+2))
           ) ||
           (
            (smemory[2]!=0xff) &&
            ((smemory[2]+1)<=smemory[0])
           )
         )
         {
          /* put stream */
          int dlen;

          if (smemory[2]!=0)
           {
            if ((dlen=QSO_HuffmanDecompressXP(smemory+2,tempo,qso,smemory[0]))
                     ==-1)
             {
              smemory[0]=0;
              return(-1);
             }
            if (dlen!=0)
             {
              func(tempo,channel,dlen);
             }

           }
          smemory[0]=0;
          break;
         }
      if (pass2) break;
      pass2=1;
      slen--;
      smemory[2+smemory[0]]=*(sptr++);
      smemory[0]++;
     }
   }
  return(0);
 }


#define XPCOMPRESS_ON 0
#define XPCOMPRESS_OFF 1
#define XPCOMPRESS_ONACK 2
#define XPCOMPRESS_OFFACK 3
#define XPCOMPRESS_ONACKRCV 4
#define XPCOMPRESS_OFFACKRCV 5
#define XPCOMPRESS_RESETLOCAL 6
#define XPCOMPRESS_RESETREMOTE 7

#define XPCOMPRESS_SPECIAL 128


void QSO_ControlXPCompress(unsigned long channel,unsigned long action)
 {
  struct QSO *qso;

  if ( (channel<=0) || (channel>MaxChannel)) return;
  if (HuffmanSpeedup==NULL) return;
  qso=QSOs+(channel-1);
  switch (action)
   {
    case XPCOMPRESS_ON:
      QSO_ForceTransmit(channel);
      QSO_SendData(11,channel,"\r#COMP:ON#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode|=1<<COMPRESSFLAG_SEND_XPCOMPRESS;
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_OFF:
      QSO_ForceTransmit(channel);
      QSO_SendData(12,channel,"\r#COMP:OFF#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_ONACK:
      QSO_ForceTransmit(channel);
      QSO_SendData(10,channel,"\r#COMP:1#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode|=1<<COMPRESSFLAG_SEND_XPCOMPRESS;
      qso->CompressedMode|=1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS;
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_OFFACK:
      QSO_ForceTransmit(channel);
      QSO_SendData(10,channel,"\r#COMP:0#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_ONACKRCV:
      qso->CompressedMode|=1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS;
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_OFFACKRCV:
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_RESETLOCAL:
      qso->XPDecompress[0]='\0';
      qso->XPDecompress[1]='\0';
      qso->XPDecompress[2]='\0';
      qso->XPDecompress[3]='\0';
      break;
    case XPCOMPRESS_RESETREMOTE:
       {
        unsigned long cmode;

        QSO_ForceTransmit(channel);
        cmode=qso->CompressedMode;
        qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
        QSO_SendData(12,channel,"\r#COMP:RES#\r");
        QSO_ForceTransmit(channel);
        qso->CompressedMode=cmode;
        break;
       }
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_ON:
      QSO_ForceTransmit(channel);
      QSO_SendData(12,channel,"\r#COMPC:ON#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode|=1<<COMPRESSFLAG_SEND_XPCOMPRESS;
      qso->CompressedMode|=1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS;
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      UTIL_RandomizeBytes(qso->XPacketKey,128);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_OFF:
      QSO_ForceTransmit(channel);
      QSO_SendData(13,channel,"\r#COMPC:OFF#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_ONACK:
      QSO_ForceTransmit(channel);
      QSO_SendData(11,channel,"\r#COMPC:1#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode|=1<<COMPRESSFLAG_SEND_XPCOMPRESS;
      qso->CompressedMode|=1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS;
      qso->CompressedMode|=1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS;
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
       {
        int i;
        for (i=0;i<128;++i) qso->XPacketKey[i]=0;
       }
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_OFFACK:
      QSO_ForceTransmit(channel);
      QSO_SendData(11,channel,"\r#COMPC:0#\r");
      QSO_ForceTransmit(channel);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_ONACKRCV:
      QSO_ForceTransmit(channel);
       {
        unsigned char packet[130];
        int i;

        packet[0]=0xff;
        packet[1]=0x7f;
        for (i=0;i<128;++i) packet[i+2]=qso->XPacketKey[i];

        qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
        qso->CompressedMode|=1<<COMPRESSFLAG_XPTABLE_COMPLETE;
        QSO_SendData(129,channel,packet);
        QSO_ForceTransmit(channel);
       }
      qso->CompressedMode|=1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS;
      qso->CompressedMode|=1<<COMPRESSFLAG_SEND_XPCOMPRESS;
      qso->CompressedMode|=1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS;
      QSO_FlushXPacketDelay(channel);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_OFFACKRCV:
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_RECEIVE_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_SPECIAL_XPCOMPRESS);
      qso->CompressedMode&=~(1<<COMPRESSFLAG_XPTABLE_COMPLETE);
      QSO_FlushXPacketDelay(channel);
      GUI_RefreshConnectHead();
      break;
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_RESETLOCAL:
      qso->XPDecompress[0]='\0';
      qso->XPDecompress[1]='\0';
      qso->XPDecompress[2]='\0';
      qso->XPDecompress[3]='\0';
      break;
    case XPCOMPRESS_SPECIAL+XPCOMPRESS_RESETREMOTE:
       {
        unsigned long cmode;

        QSO_ForceTransmit(channel);
        cmode=qso->CompressedMode;
        qso->CompressedMode&=~(1<<COMPRESSFLAG_SEND_XPCOMPRESS);
        QSO_SendData(13,channel,"\r#COMPC:RES#\r");
        QSO_ForceTransmit(channel);
        qso->CompressedMode=cmode;
        break;
       }
   }
 }

/* HandleXPLine : Checks a line for XP standard compression control or
   for XP/PP standard name control / requestname tags. Returns
   0=None 1=compression control  2=name control 3=requestname */

int QSO_HandleXPLine(int channel,const unsigned char *line)
 {
  struct QSO *qso;

  if ( (channel<=0) || (channel>MaxChannel)) return(0);
  qso=QSOs+(channel-1);

  if (line[0]!='#') return(0);

  if (CompareStringLimit(line,"#COMP",5))
   {
    if (CompareString(line,"#COMP:ON#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_ONACK);
      return(1);
     }

    if (CompareString(line,"#COMP:1#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_ONACKRCV);
      return(1);
     }

    if (CompareString(line,"#COMP:OFF#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_OFFACK);
      return(1);
     }

    if (CompareString(line,"#COMP:0#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_OFFACKRCV);
      return(1);
     }

    if (CompareString(line,"#COMPC:ON#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_SPECIAL+XPCOMPRESS_ONACK);
      return(1);
     }

    if (CompareString(line,"#COMPC:1#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_SPECIAL+XPCOMPRESS_ONACKRCV);
      return(1);
     }

    if (CompareString(line,"#COMPC:OFF#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_SPECIAL+XPCOMPRESS_OFFACK);
      return(1);
     }

    if (CompareString(line,"#COMPC:0#"))
     {
      QSO_ControlXPCompress(channel,XPCOMPRESS_SPECIAL+XPCOMPRESS_OFFACKRCV);
      return(1);
     }

   }

  if (CompareStringLimit(line,"#NAM#",5))
   {
    const unsigned char *sptr;
    sptr=line+5;
    while (*sptr==' ') ++sptr;
    if (qso->User==NULL) return(2);
    CopyStringLimit(qso->User->Name,sptr,30);
    CopyStringLimit(qso->Name,sptr,30);
#ifdef AmigaOS
    MAIN_ConvertToAmiga(qso->Name);
#endif
    QSO_ChangeUser(qso->User);
    GUI_RefreshConnectHead();
    return(2);
   }

  if (CompareStringLimit(line,"#QTH#",5))
   {
    const unsigned char *sptr;
    sptr=line+5;
    while (*sptr==' ') ++sptr;
    if (qso->User==NULL) return(2);
    CopyStringLimit(qso->User->QTH,sptr,30);
    QSO_ChangeUser(qso->User);
    return(2);
   }

  if (CompareStringLimit(line,"#LOC#",5))
   {
    const unsigned char *sptr;
    unsigned char tempor[8];
    sptr=line+5;
    while (*sptr==' ') ++sptr;
    if (qso->User==NULL) return(2);
    CopyStringLimit(tempor,sptr,7);
    if (QSO_ValidateLocator(tempor)) return(2);
    CopyString(qso->User->Locator,tempor);
    QSO_ChangeUser(qso->User);
    return(2);
   }

  if (CompareStringLimit(line,"#REQUESTNAME:",13))
   {
    const unsigned char *sptr;
    int i,nrq;
    sptr=line+13;
    while (*sptr==' ') ++sptr;
    i=0;
    nrq=0;
    while (*sptr!='#')
     {
      if ( (*sptr!='+') && (*sptr!='-')) return(3);


      if (*sptr=='+')
       {
        nrq|=1<<i;
       }
      ++i;
      ++sptr;
     }
    if (*(sptr+1)!='\0') return(3);
    if (nrq!=0)
     {
      QSO_HandleNameRequest(channel,nrq);
     }
    return(3);
   }
  return(0);
 }

