/*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 "QSO.h"
#include "global.h"
#include "Preferences.h"
#include "GUIProtos.h"


#ifdef AmigaOS

#include <exec/memory.h>

#include <clib/exec_protos.h>

#ifdef __GNUC

#include <inline/exec.h>

#endif /* __GNUC */

#else /* AmigaOS*/

#include  "amigaemu.h"

#endif /* Amiga OS*/

struct SpyLocal
 {
  unsigned long    Station;
  struct Spy      *Spy;
  unsigned char   *FrameCollector;
    signed short  *CollectorLength;
    signed short  *CollectorCRC;
    signed long    ReceiveCount;
    signed long    InCollector;
    signed long    OtherReceive;
    signed long    ThisReceive;
 };


/* Prototypes for static functions */

static void SPY_SpyControl(const unsigned char *control,struct Spy *spy,
                        signed long ptype,unsigned long station);
static void SPY_LoadLocal(struct SpyLocal *sloc,struct Spy *spy,unsigned long station);
static void SPY_SaveLocal(struct SpyLocal *sloc);
static struct Spy *SPY_GetSpyPointer(unsigned long spynr);
static void SPY_LinkReset(struct Spy *spy);
static signed long SPY_GetSpyNumber(struct Spy *spy);
static unsigned long SPY_UnloadCollector(unsigned char *framecollector,
                                   signed short *collectorlength,
                                   signed long channelnr,
                                   unsigned long station,
                                   int mustunload,
                                   signed long receivecount,
                                   signed long unloaduntil,
                                   signed long *incollector);
static void SPY_InfoReceived(const unsigned char *data,
                             signed long length,signed long channel,
                             unsigned long station);
static void SPY_SpyInfo(const unsigned char *data,struct Spy *spy,
                        unsigned long length,unsigned long station,
                        unsigned long inumber);
static void SPY_HandleInfoSpy(const unsigned char *data,unsigned long len,
                              signed long channel,unsigned long style);


/* externals */

static unsigned long SpyINumber;
static struct Spy *SpyPointer;
static unsigned long SpyStation;


static void SPY_HandleInfoSpy(const unsigned char *data,unsigned long len,
                              signed long channel,
                              unsigned long station)
 {
  GUIMAIN_PutQSO(data,len,channel,2-station);
 }

static void SPY_IncomingHook1(const unsigned char *data,int channel,
                              int len)
 {
  SPY_HandleInfoSpy(data,len,channel,0); 
 }

static void SPY_IncomingHook2(const unsigned char *data,int channel,
                              int len)
 {
  SPY_HandleInfoSpy(data,len,channel,1);
 }

static void SPY_InfoReceived(const unsigned char *data,
                             signed long length,signed long channel,
                             unsigned long station)
 {
  struct Spy *spy;

  spy=SPY_GetSpyPointer(-channel);

  while (1)
   {
    switch (spy->CompressedMode)
     {



      case 1: /* normal decompress*/
        {
         signed long xlen;
         unsigned char Temporary[264];

         if ((xlen=QSO_HuffmanDecompress(data,Temporary,length))==-1)
          {
           spy->CompressedMode=0;
           spy->AutoCompressFollow=0;
           break;
          }
         SPY_HandleInfoSpy(Temporary,xlen,channel,station);
         return;
        }



      case 2: /* XP decompress*/
        {
         if (station)
          {

           if (QSO_DecompressXPStream(data,spy->XPDecompress2,
                                  SPY_IncomingHook2,NULL,length,channel)==-1)
            {
             spy->CompressedMode=0;
             spy->AutoCompXPFollow=0;
             break;
            }
           return;                                                             
          }
          else
          {
           if (QSO_DecompressXPStream(data,spy->XPDecompress1,
                                  SPY_IncomingHook1,NULL,length,channel)==-1)
           {
            spy->CompressedMode=0;
            spy->AutoCompXPFollow=0;
            break;
           }
           return;
          }
        }




      default: /* uncompressed - autodetection of compression */
       {
        unsigned char Temporary[264];
        if ( (Preferences.SpyAutoCompress!=0) &&
             (Preferences.SpyAutoCompressFol!=0) )
         {

          /* Compressor autodetection - try to decompress SP */
          if (QSO_HuffmanDecompress(data,Temporary,length)!=-1)
           {
            spy->AutoCompressFollow++;
            if (spy->AutoCompressFollow>=Preferences.SpyAutoCompressFol)
             {
              spy->AutoCompressFollow=0;
              spy->AutoCompXPFollow=0;
              spy->CompressedMode=1;
              break;
             }
           }
           
          /* Compressor autodetection - try to decompress XP */

          if (QSO_HuffmanDecompressXP(data,Temporary,NULL,length)!=-1)
           {
            spy->AutoCompXPFollow++;
            if (spy->AutoCompXPFollow>=Preferences.SpyAutoCompressFol)
             {
              unsigned long i;
              spy->AutoCompXPFollow=0;
              spy->AutoCompressFollow=0;
              spy->CompressedMode=2;
              for (i=0;i<260;++i) spy->XPDecompress1[i]=0;
              for (i=0;i<260;++i) spy->XPDecompress2[i]=0;
              break;
             }
           }
         }
        
        /* Compressor autodetection - scan for //COMP 1*/
        
        if ( (length>=9) && (CompareStringLimit(data,"//COMP 1\r",9)==0) )
         {
          spy->AutoCompXPFollow=0;
          spy->AutoCompressFollow=Preferences.SpyAutoCompressFol;          
         }      
         
         
        /* Compressor autodetection - scan for #COMP:1# */
        if ( ( (length>=9) && (CompareStringLimit(data,"#COMP:1#\r",9)==0) ) ||
             ( (length>=10) && (CompareStringLimit(data,"\r#COMP:1#\r",10)==0) ) )
         {
          spy->AutoCompXPFollow=Preferences.SpyAutoCompressFol;
          spy->AutoCompressFollow=0;
         }                                

        /* no auto decompress*/
        SPY_HandleInfoSpy(data,length,channel,station);

        return;
       }
     }
   }
 }

static void SPY_SpyInfo(const unsigned char *data,struct Spy *spy,
                        unsigned long length,unsigned long station,
                        unsigned long inumber)
 {
  struct SpyLocal sloc;

  unsigned short infocrc;
  const unsigned char *sptr;
  unsigned long i;

  SPY_LoadLocal(&sloc,spy,station);

  infocrc=0x4791;
  sptr=data;
  for (i=0;i<length;++i,++sptr)
   {
    infocrc=  ((((*sptr)<<8)^0x5500) | (*sptr)) ^
              ( ((infocrc<<4) | (infocrc>>12)) ^ 0x55aa );
   }

  if (sloc.ReceiveCount==-1) sloc.ReceiveCount=inumber;
  if  ( ( sloc.ReceiveCount==inumber) ||
        ( *(sloc.CollectorCRC+inumber)!=infocrc))
   {
    unsigned char *dptr;
    if ( (*(sloc.CollectorLength+inumber)!=-1) ||
         ( (sloc.ThisReceive!=inumber) && (sloc.ThisReceive==sloc.ReceiveCount)) ||
         ( ((inumber+1)%8) == sloc.ReceiveCount) )
     {
      signed long until;

      until=-1;

      if ( (*(sloc.CollectorLength+inumber)==-1) &&
           ( ((inumber+1)%8) !=sloc.ReceiveCount) )
       {
        until=sloc.ThisReceive;
       }

      SPY_UnloadCollector(sloc.FrameCollector,sloc.CollectorLength,
                          -SPY_GetSpyNumber(spy),station,TRUE,
                          sloc.ReceiveCount,until,&sloc.InCollector);
      sloc.ReceiveCount=inumber;
     }

    sloc.InCollector++;
    *(sloc.CollectorLength+inumber)=length;
    *(sloc.CollectorCRC+inumber)=infocrc;
    sptr=data;
    dptr=sloc.FrameCollector+inumber*256;
    for (i=0;i<length;++i) *(dptr++)=*(sptr++);
   }
  sloc.ReceiveCount=SPY_UnloadCollector(sloc.FrameCollector,
                     sloc.CollectorLength,-SPY_GetSpyNumber(spy),
                     station,FALSE,sloc.ReceiveCount,-1,&sloc.InCollector);
  SPY_SaveLocal(&sloc);
  if (ActiveChannel==(-SPY_GetSpyNumber(spy))) GUI_RefreshConnectHead();
  return;
 }

void SPY_HandleInfo(const unsigned char *data,unsigned long length)
 {
  if (SpyPointer==NULL) return;
  SPY_SpyInfo(data,SpyPointer,length,SpyStation,SpyINumber);
  SpyPointer=NULL;

 }

static unsigned long SPY_UnloadCollector(unsigned char *framecollector,
                                   signed short *collectorlength,
                                   signed long channelnr,
                                   unsigned long station,
                                   int mustunload,
                                   signed long receivecount,
                                   signed long unloaduntil,
                                   signed long *incollector)
 {

  signed long rcount=receivecount;


  unsigned long i;
  for (i=0;i<8;++i)
   {
    if ( (*(collectorlength+rcount)!=-1) || (mustunload))
     {
      if ( *(collectorlength+rcount)==-1)
       {
        /* missed frame*/
       }
       else
       {
        SPY_InfoReceived(framecollector+(rcount*256),
                         *(collectorlength+rcount),channelnr,station);
        *(collectorlength+rcount)=-1;
        if (incollector!=NULL) (*incollector)--;
       }
      ++rcount;
      rcount=rcount%8;
      if (rcount==unloaduntil) return(rcount);
     }
     else
     {
      return(rcount);
     }

   }
  return(rcount);
 }

static void SPY_SpyControlFlush(struct Spy *spy)
 {
  signed long spychannel;

  spychannel=SPY_GetSpyNumber(spy);

  if (spy->ReceiveCount1!=-1) SPY_UnloadCollector(spy->FrameCollector,
                               spy->CollectorLength1,spychannel,0,
                               TRUE,spy->ReceiveCount1,-1,NULL);

  if (spy->ReceiveCount2!=-1) SPY_UnloadCollector(spy->FrameCollector2,
                               spy->CollectorLength2,spychannel,1,
                               TRUE,spy->ReceiveCount2,-1,NULL);
 }

 
static void SPY_SpyControl(const unsigned char *control,struct Spy *spy,
                        signed long ptype,unsigned long station)
 {
  struct SpyLocal sloc;
  const unsigned char *ackptr;


  ackptr=NULL;
  SPY_LoadLocal(&sloc,spy,station);
  switch (ptype)
   {
    case 0:
     {
      if (*control!='I') return;
      if (*(control+1)=='\0') return;
      if ( (*(control+2)<'0') || (*(control+2)>'7')) return;
      SpyINumber=*(control+2)-'0';
      SpyStation=station;
      SpyPointer=spy;
      ackptr=control+1;
      break;
     }
    case 1:
      ackptr=control+2;
      break;
    case 2:
      ackptr=control+3;
      break;
    case 3:
      ackptr=control+3;
      break;
    case 4:
     {
      SPY_SpyControlFlush(spy);
      SPY_LinkReset(spy);
      spy->ReceiveCount1=0;
      spy->ReceiveCount2=0;
      spy->OtherReceive1=0;
      spy->OtherReceive2=0;
      if (ActiveChannel==(-SPY_GetSpyNumber(spy))) GUI_RefreshConnectHead();
      return;
     }

    case 5:
    case 7:
     {
      SPY_SpyControlFlush(spy);
      SPY_LinkReset(spy);
      if (ActiveChannel==(-SPY_GetSpyNumber(spy))) GUI_RefreshConnectHead();
      return;
     }
    default:
      return;
   }
  if (ackptr==NULL) return;
  if ( (*ackptr<'0') || (*ackptr>'7')) return;

  sloc.OtherReceive=*ackptr-'0';
  SPY_SaveLocal(&sloc);
  if (ActiveChannel==(-SPY_GetSpyNumber(spy))) GUI_RefreshConnectHead();
  return;
 }

static void SPY_LoadLocal(struct SpyLocal *sloc,struct Spy *spy,unsigned long station)
 {
  sloc->Station=station;
  sloc->Spy=spy;
  
  if (!sloc->Station)
   {
    sloc->FrameCollector=spy->FrameCollector;
    sloc->CollectorLength=spy->CollectorLength1;
    sloc->CollectorCRC=spy->CollectorCRC1;
    sloc->ReceiveCount=spy->ReceiveCount1;
    sloc->InCollector=spy->InCollector1;
    sloc->ThisReceive=spy->OtherReceive1;
    sloc->OtherReceive=spy->OtherReceive2;   
   }
   else
   {
    sloc->FrameCollector=spy->FrameCollector2;
    sloc->CollectorLength=spy->CollectorLength2;
    sloc->CollectorCRC=spy->CollectorCRC2;
    sloc->ReceiveCount=spy->ReceiveCount2;
    sloc->InCollector=spy->InCollector2;
    sloc->ThisReceive=spy->OtherReceive2;
    sloc->OtherReceive=spy->OtherReceive1;
   }
 }
 
static void SPY_SaveLocal(struct SpyLocal *sloc)
  {
   struct Spy *spy;
   
   spy=sloc->Spy;
   
   if (!sloc->Station) 
    {
     spy->ReceiveCount1=sloc->ReceiveCount;
     spy->InCollector1=sloc->InCollector;
     spy->OtherReceive1=sloc->ThisReceive;
     spy->OtherReceive2=sloc->OtherReceive;
    }
    else
    {
     spy->ReceiveCount2=sloc->ReceiveCount;
     spy->InCollector2=sloc->InCollector;
     spy->OtherReceive2=sloc->ThisReceive;
     spy->OtherReceive1=sloc->OtherReceive;   
    }
  }


static struct Spy *SPY_GetSpyPointer(unsigned long spynr)
 {
  struct Spy *spy;
  
  spy=(struct Spy *) (((struct QSO *) Spys)+(spynr-1));
  return(spy);
 } 

static signed long SPY_GetSpyNumber(struct Spy *spy)
 {
  unsigned long spynr;

  spynr= (((struct QSO *) spy)-((struct QSO *)Spys))+1;
  return(spynr);
 }

static void SPY_LinkReset(struct Spy *spy)
 {
  unsigned long i;
  
  for (i=0;i<8;++i) spy->CollectorLength1[i]=-1;
  for (i=0;i<8;++i) spy->CollectorLength2[i]=-1;
  for (i=0;i<8;++i) spy->CollectorCRC1[i]=-1;
  for (i=0;i<8;++i) spy->CollectorCRC2[i]=-1;
  spy->AutoCompXPFollow=0;
  spy->AutoCompressFollow=0;   
  for (i=0;i<260;++i) spy->XPDecompress1[i]=0;
  for (i=0;i<260;++i) spy->XPDecompress2[i]=0;
  spy->ReceiveCount1=-1;
  spy->ReceiveCount2=-1;
  spy->OtherReceive1=-1;
  spy->OtherReceive2=-1;
  spy->InCollector1=0;
  spy->InCollector2=0;
  return; 
 }

void SPY_ClearSpy(signed long spynr)
 {
  struct Spy *spy;
  
  if ( (spynr<=0) || (spynr>MaxSpy) ) return;
 
  spy=SPY_GetSpyPointer(spynr);
  MAIN_CleanupChannel(-spynr);
  GUI_RefreshPacketHead();
  GUI_RefreshConnectHead();
  return; 
 }


int SPY_SetSpy(signed long spynr,unsigned long unit,unsigned long port,
               unsigned long compr,const unsigned char *call1,
               const unsigned char *call2)
               
 {
  struct Spy *spy;
  
  if ( (spynr<=0) || (spynr>MaxSpy) ) return(-1);
    
  spy=SPY_GetSpyPointer(spynr);
  if (spy->LinkStatus!=0) return(-1);
  
  spy->LinkStatus=1;
  CopyStringUpper(spy->MyCall,call1);
  CopyStringUpper(spy->Call,call2);
  spy->HardwareUnit=unit;
  spy->Port=port;
  spy->CompressedMode=0;
  if (spy->FrameCollector==NULL)
   {
    spy->FrameCollector=AllocVec(16*256,0);
    if (spy->FrameCollector==NULL)
     {
      SPY_ClearSpy(spynr);
     }   
   }
  spy->FrameCollector2=spy->FrameCollector+(8*256);
  SPY_LinkReset(spy);
  if (compr)
   {
    spy->AutoCompressFollow=Preferences.SpyAutoCompressFol;
    spy->AutoCompXPFollow=Preferences.SpyAutoCompressFol;
   } 
   
  GUI_RefreshPacketHead();
  GUI_RefreshConnectHead();
  return(0);
 }

void SPY_HandleControl(signed long unit,signed long port,signed long ptype,
                       const unsigned char *from,const unsigned char *to,
                       const unsigned char *path,const unsigned char *control)
 {
  unsigned long i;
  struct Spy *spy;
  
  for (i=1;i<=MaxSpy;++i)
   {
    spy=SPY_GetSpyPointer(i);
    if (spy->LinkStatus==0) continue;
    if (spy->HardwareUnit!=unit) continue;
    if ( (port!=-1) && (spy->Port!=port)) continue;
    if ( (CompareString(from,spy->MyCall)) &&
         (CompareString(to,spy->Call)) )
     {
      SPY_SpyControl(control,spy,ptype,0);
     }
     else 
     {
      if ( (CompareString(to,spy->MyCall)) &&
           (CompareString(from,spy->Call)) )
       {
        SPY_SpyControl(control,spy,ptype,1);
       } 
     }  
   }
 
 }
