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

unsigned long L4Monitor;
extern struct MHeardEntry *MHeardBase;
extern struct MHeardEntry *MHeardPointer;


#ifdef AmigaOS
#include <exec/memory.h>
#include <clib/exec_protos.h>
#include <inline/exec.h>
extern void *SysBase;
#undef RawDoFmt
#endif /*AmigaOS*/

static int MonCopyCall(const unsigned char *data,const unsigned char *enddata,
                       unsigned char *target,int length)
 {
  const unsigned char *sptr;
  unsigned char *dptr;
  int len;
  sptr=data;
  dptr=target;
  len=0;
  
  if (sptr==NULL)
   {
    *dptr='\0';
    return(0);
   }
  
  while ( (sptr<enddata) ) 
   {
    if (*sptr==' ') break;
    if (len==length) return(1);
    *(dptr++)=*(sptr++);
    len++;
   }
  *dptr='\0';
  return(0);
 }
 
static int MonCopyPath(const unsigned char *data,const unsigned char *enddata,
                        unsigned char *target,int length)
 {
  const unsigned char *sptr;
  unsigned char *dptr;
  int len;
  sptr=data;
  dptr=target;
  len=0;
  
  if (sptr==NULL)
   {
    *dptr='\0';
    return(0);
   }
  
  while ( (sptr<enddata) ) 
   {
    if (len==length) return(1);
    *(dptr++)=*(sptr++);
    len++;
   }
  *dptr='\0';
  return(0);
     
  }

static void MonitorHearing(const unsigned char *data,int length,
                           int type,int hunit)
 {
  int portset,portnumber,ptype;
  
  portset=0;
  portnumber=1;
  ptype=99;

  if ( (type==4) || (type==5) )
   {
   
    /* packet header*/

    const unsigned char *fm=NULL;
    const unsigned char *to=NULL;
    const unsigned char *ctl=NULL;
    const unsigned char *via=NULL;
    
    const unsigned char *enddata;
    
    unsigned char fmcall[10];
    unsigned char tocall[10];
    unsigned char control[32];
    unsigned char path[80];
   
    struct MHeardEntry *mh1,*mh2,*mh3;
    struct MHeardEntry tempmh;
    int i;
    
    
    int rlen;
    const unsigned char *rptr;

    MHeardPointer=NULL;
    enddata=data+length;
    
    if (length>=2)
     {
      if ( (data[1]==':') && (data[0]>='0') && (data[0]<='9') )
       {
        portnumber=data[0]-'0';
        portset=1;
       }    
     }
     
    rlen=length;
    rptr=data;
    
    while (rlen>0)
     {
      if ( (rlen>=4) && (CompareStringLimit("fm ",rptr,3)) )
           {
            fm=rptr+3;
            rptr+=3;
            rlen-=3;
            continue;
           }
      if ( (rlen>=4) && (CompareStringLimit("to ",rptr,3)) )
           {
            to=rptr+3;
            rptr+=3;
            rlen-=3;
            continue;     
           }
      if ( (rlen>=5) && (CompareStringLimit("ctl ",rptr,4)) )
           {
            ctl=rptr+4;
            rptr+=4;
            rlen-=4;
            continue;          
           }
      if ( (rlen>=5) && (CompareStringLimit("via ",rptr,4)) )
           {
            via=rptr+4;
            rptr+=4;
            rlen-=4;
            continue;
           }
      rptr++;
      rlen--;
     }
    if ( (fm==NULL) || (to==NULL) || (ctl==NULL) ) return;
    if (MonCopyCall(fm,enddata,fmcall,10)) return;
    if (MonCopyCall(to,enddata,tocall,10)) return;
    if (MonCopyPath(via,ctl-5,path,80)) return;
    if (MonCopyCall(ctl,enddata,control,32)) return;
    
    mh2=NULL;
      
    for (i=0,mh1=MHeardBase;i<HEARD_ENTRIES;++i,++mh1)
     {
      if (mh1->FromCall[0]=='\0')
       {
        break;
       }
      if (!CompareString(mh1->FromCall,fmcall)) continue;
      if (!CompareString(mh1->ToCall,tocall)) continue;
      if (mh1->TNCNumber!=hunit) continue;
      tempmh=*mh1;
      for (mh3=(mh1-1);mh3>=MHeardBase;--mh3)
       {
        *(mh3+1)=*(mh3);
       }
      mh2=MHeardBase;
      *mh2=tempmh;
      break;
     }    
    if (mh2==NULL)
     {
      for (i=0,mh1=MHeardBase+HEARD_ENTRIES-2;i<(HEARD_ENTRIES-1);++i,--mh1)
       {
        *(mh1+1)=*(mh1);
       }
      mh2=MHeardBase;
      CopyString(mh2->FromCall,fmcall);
      CopyString(mh2->ToCall,tocall);
      mh2->Path[0]='\0';
      mh2->TNCNumber=hunit;
      mh2->PortNumber=portnumber;
      mh2->ControlPackets=0;
      mh2->RRPackets=0;
      mh2->REJPackets=0;
      mh2->IPackets=0;
      mh2->IBytes=0;
      mh2->IConnect=0;
      mh2->FirstTime=CurrentDateStamp;
      mh2->TimeConnect=CurrentDateStamp;
     }

    mh2->Time=CurrentDateStamp;
    CopyString(mh2->Path,path);
    if (CompareStringLimit("RR",control,2))
     {
      ptype=1;
      mh2->RRPackets++;
     }
    if (CompareStringLimit("REJ",control,3))
     {
      mh2->REJPackets++;
      ptype=3;
     }
    if (CompareStringLimit("RNR",control,3))
     {
      mh2->RNRPackets++;
      ptype=2;
     }
    if (CompareStringLimit("SABM",control,4))
     {
      mh2->ControlPackets++;
      ptype=4;
     }
    if (CompareStringLimit("DISC",control,4))
     {
      mh2->ControlPackets++;
      ptype=5;
     }
    if (CompareStringLimit("UA",control,2))
     {
      mh2->ControlPackets++;
      ptype=6;
     }
    if (CompareStringLimit("FRMR",control,4))
     {
      mh2->ControlPackets++;
      ptype=7;
     }
    if (CompareStringLimit("UI",control,2))
     {
      ptype=8;
      ++mh2->IPackets;
     }
    if (control[0]=='I')
     {
      ptype=0;
      ++mh2->IPackets;
      MHeardPointer=mh2;
     }
    if (ptype!=99)
     {
      int pport;

      pport=-1;
      if (portset) pport=portnumber;
      SPY_HandleControl(hunit,pport,ptype,fmcall,tocall,path,control);
     }
   }
   
  if ( type==6) 
   {
    int timeago;
    /* packet info */
    if (MHeardPointer==NULL) return;
    MHeardPointer->IBytes+=length;
    MHeardPointer->IConnect+=length;
    timeago=CurrentDateStamp.ds_Minute-MHeardPointer->TimeConnect.ds_Minute;
    while (timeago<0) timeago+=60*24;
    if (timeago>=20)
     {
      MHeardPointer->TimeConnect=CurrentDateStamp;
      MHeardPointer->IConnect=length;
     }
    MHeardPointer=NULL;
    SPY_HandleInfo(data,length);
   } 
 }

static int OutputL4Call(const unsigned char *call,unsigned char *out,int tab)
 {
  const unsigned char *sptr;
  unsigned char *dptr;
  int i;

  sptr=call;
  dptr=out;


  for (i=0;i<6;++i)
   {
    *(dptr++)=(*(sptr++)&0xfe)>>1;
   }
  if ( ((*sptr&0x1e)==0) || (tab==2) )
   {
    if (tab)
     {
      *(dptr++)='-';
      *(dptr++)='0';
      *(dptr++)='0';
     }
   }
   else
   {
    *(dptr++)='-';
    *(dptr++)=((*sptr>>1)&0xf)/10+'0';
    *(dptr++)=((*sptr>>1)&0xf)%10+'0';
   }
  *(dptr)='\0';
  return(dptr-out);
 }

void QSO_IncomingMonitor(const unsigned char *data,int length,int type,int hunit)
 {
  const unsigned char *prefix;
  const unsigned char *rptr;
  int rlen;
  unsigned char outbuffer[1024];
  unsigned char *optr;


  MonitorHearing(data,length,type,hunit);
  optr=outbuffer;
  prefix=Preferences.Devices[hunit].Prefix;


  if (type==5)
   {
    /* test for L4*/
    const unsigned char *xptr;
    int xlen;

    xptr=data;
    xlen=length;

    while (xlen>=5)
     {
      if (CompareStringLimit("ctl I",xptr,5))
       {
        xptr+=5;
        xlen-=5;
        while (xlen>0)
         {
          if (*xptr==' ') break;
          ++xptr;
          --xlen;
         }

        if (xlen==0) break;
        --xlen;
        ++xptr;
        if (xlen<6) break;
        if (!CompareStringLimit("pid CF",xptr,6)) break;
        xlen-=5;
        xptr+=5;
        L4Monitor=1;
        break;
       }

      if ( (xlen>=6) && (CompareStringLimit("ctl UI",xptr,6)))
       {
        xptr+=6;
        xlen-=6;
        while (xlen>0)
         {
          if (*xptr==' ') break;
          ++xptr;
          --xlen;
         }

        if (xlen==0) break;
        --xlen;
        ++xptr;
        if (xlen<6) break;
        if (!CompareStringLimit("pid CF",xptr,6)) break;
        xlen-=5;
        xptr+=5;
        L4Monitor=2;
        break;
       }

      --xlen;
      ++xptr;
     }
   }

  if (type==4) L4Monitor=0;

  if ( (type<1) || (type>6) ) return;



  if (type!=6)
   {
    const unsigned char *psrc;

    psrc=prefix;
    while (*psrc!='\0') *(optr++)=*(psrc++);
   }

  rptr=data;
  rlen=length;

  switch (type)
   {
    case 6:
      if ( (L4Monitor==1) && (rlen>=20) )
       {
        unsigned char call1[20];
        unsigned char call2[20];
        unsigned char v1[8],v2[8];
        unsigned long level,id1,id2,c1,c2,flag;
        unsigned char *opcode;
        unsigned char flags[8];
        unsigned char *param1,*param2;

        L4Monitor=0;
        OutputL4Call(rptr,call1,0);
        OutputL4Call(rptr+7,call2,0);

        param1="";
        param2="";
        level=*(rptr+14);
        id1=*(rptr+15);
        id2=*(rptr+16);
        c1=*(rptr+17);
        c2=*(rptr+18);
        flag=*(rptr+19);
        switch ( flag & 0xf)
         {
          case 1:
            opcode="ConR";
            break;
          case 2:
            opcode="ConA";
            break;
          case 3:
            opcode="DisR";
            break;
          case 4:
            opcode="DisA";
            break;
          case 5:
            opcode="I";
            RawDoFmt("%03ld",&c2,PutRawDoFmt,v1);
            RawDoFmt("%03ld",&c1,PutRawDoFmt,v2);
            param1=v1;
            param2=v2;
            break;
          case 6:
            opcode="IA";
            RawDoFmt("%03ld",&c2,PutRawDoFmt,v1);
            param1=v1;
            break;
          default:
            opcode="???";

         }
        flags[0]=(flag&0x80)?'C':'-';
        flags[1]=(flag&0x40)?'N':'-';
        flags[2]=(flag&0x20)?'M':'-';
        flags[3]='\0';

         {
          unsigned char *xarray[16];
          xarray[0]=call1;
          xarray[1]=call2;
          xarray[2]=(unsigned char *) level;
          xarray[3]=opcode;
          xarray[4]=param1;
          xarray[5]=param2;
          xarray[6]=flags;
          xarray[7]=(unsigned char *) id1;
          xarray[8]=(unsigned char *) id2;

          RawDoFmt("L4 fm %s to %s lv %3ld %s%s%s (%s) <%03ld/%03ld>\r",xarray,
                   PutRawDoFmt,outbuffer);
          GUI_WriteToQSO(outbuffer,0,StringLength(outbuffer),0x41);
         }
        rlen-=20;
        optr=outbuffer;
        rptr+=20;

        if ( ((flag&0xf)==1) && (rlen>=15) )
         {
          unsigned char *xarray[3];

          OutputL4Call(rptr+1,call1,1);
          OutputL4Call(rptr+8,call2,1);

          c1=*rptr;
          xarray[0]=(unsigned char *) c1;
          xarray[1]=call1;
          xarray[2]=call2;

          RawDoFmt("FSiz %ld %s -> %s\r",xarray,
                   PutRawDoFmt,outbuffer);
          GUI_WriteToQSO(outbuffer,0,StringLength(outbuffer),0x41);
          rlen-=15;
          rptr+=15;
         }
        if ( ((flag&0xf)==2) && (rlen>=1) )
         {
          unsigned char *xarray[1];

          c1=*rptr;
          xarray[0]=(unsigned char *) c1;
          RawDoFmt("FSiz %ld\r",xarray,
                   PutRawDoFmt,outbuffer);
          GUI_WriteToQSO(outbuffer,0,StringLength(outbuffer),0x41);
          rlen-=1;
          rptr+=1;

         }
       }

      if ( (L4Monitor==2) && (rlen>=7) )
       {
        unsigned char call1[20],call2[20],call3[20];
        int tflag;
        unsigned char *term;

        tflag=0;
        L4Monitor=0;
        optr=outbuffer;
        CopyString(optr,"ID : ");
         {
          int i;
          for (i=0;i<6;++i)
           {
            *(optr+5+i)=*(rptr+1+i);
           }
         }
        *(optr+11)='\r';
        GUI_WriteToQSO(optr,0,12,0x41);
        rlen-=7;
        rptr+=7;
        while (rlen>=(6+7+7+1))
         {
          int i;
          unsigned long q;
          term=tflag?"\r":"   ";
          tflag=!tflag;

          for (i=0;i<6;++i)
           {
            call1[i]=*(rptr+7+i);
            if (call1[i]=='\0') call1[i]=' ';

           }
          call1[6]='\0';

          OutputL4Call(rptr,call2,1);
          OutputL4Call(rptr,call3,1);

          q=*(rptr+6+7+7);
          rlen-=6+7+7+1;
          rptr+=6+7+7+1;

          if (CompareString(call2,call3))
           {
            unsigned char *xarray[4];

            xarray[0]=call1;
            xarray[1]=call2;
            xarray[2]=(unsigned char *) q;
            xarray[3]=term;
            RawDoFmt("%s:%s              (%3ld)%s",xarray,PutRawDoFmt,outbuffer);
           }
           else
           {
            unsigned char *xarray[5];
            xarray[0]=call1;
            xarray[1]=call2;
            xarray[2]=call3;
            xarray[3]=(unsigned char *) q;
            xarray[4]=term;
            RawDoFmt("%s:%s via %s (%3ld)%s",xarray,PutRawDoFmt,outbuffer);
           }
          GUI_WriteToQSO(outbuffer,0,StringLength(outbuffer),0x41);

         }
        if (tflag)
         {
          GUI_WriteToQSO("\r",0,1,0x41);
         }
        optr=outbuffer;
        rlen=0;
       }


      L4Monitor=0;

      if ( (Preferences.HuffmanMonitor) && (length!=0) )
       {
        int clen;
        clen=QSO_HuffmanDecompress(rptr,optr,rlen);
        if (clen!=-1)
         {
          unsigned char outhuff[24];
          unsigned long larray[2];
          unsigned long comp;

          comp=(rlen*1000)/clen;
          larray[0]=comp/10;
          larray[1]=comp%10;
          RawDoFmt("Huffman SP (%3ld.%01ld%%)\r",larray,PutRawDoFmt,outhuff);
          GUI_WriteToQSO(outhuff,0,StringLength(outhuff),0x41);
          optr+=clen;
          break;
         }

        clen=QSO_HuffmanDecompressXP(rptr,optr,NULL,rlen);
        if (clen!=-1)
         {
          unsigned char outhuff[24];
          unsigned long larray[2];
          unsigned long comp;

          comp=(rlen*1000)/clen;
          larray[0]=comp/10;
          larray[1]=comp%10;
          RawDoFmt("Huffman XP (%3ld.%01ld%%)\r",larray,PutRawDoFmt,outhuff);
          GUI_WriteToQSO(outhuff,0,StringLength(outhuff),0x41);
          optr+=clen;
          break;
         }
       }

      while (rlen>0)
       {
        *(optr++)=*(rptr++);
        --rlen;
       }
      break;
    case 1:
      CopyString(optr,"TNC Answer:\r");
      optr+=12;
      while (rlen>0)
       {
        *(optr++)=*(rptr++);
        --rlen;
       }
      *(optr)='\0';
      break;
    case 2:
      CopyString(optr,"TNC Error:\r");
      optr+=11;
      while (rlen>0)
       {
        *(optr++)=*(rptr++);
        --rlen;
       }
      *(optr)='\0';
      break;
    default:
      while (rlen>0)
       {
        *(optr++)=*(rptr++);
        --rlen;
       }
      *(optr)='\0';
      break;

   }



  if ( (optr!=outbuffer) && (*(optr-1)!='\r')) *(optr++)='\r';

   {
    int olen;

    olen=optr-outbuffer;

    if (olen==0) return;
    switch (type)
     {
      case 4:
        GUI_WriteToQSO(outbuffer,0,olen,1);
        break;
      case 5:
        GUI_WriteToQSO(outbuffer,0,olen,1);
        break;
      case 6:
        GUI_WriteToQSO(outbuffer,0,olen,2);
        break;
      case 3:
        GUI_WriteToQSO(outbuffer,0,olen,0x42);
        break;
      default:
        break;
     }
    switch (type)
     {
      case 3:
        QSO_OutputEvent(1,outbuffer,NULL);
        break;
      case 1:
        QSO_OutputEvent(-1,outbuffer,NULL);
        break;
      case 2:
        QSO_OutputEvent(-1,outbuffer,NULL);
        break;
     }
   }

 }


int QSO_UpdateTickerLine(unsigned char *Ticker)
 {
  unsigned char tickerline[100];
  unsigned char *tlptr;
  int i;
  int intick;
  struct MHeardEntry *mh1;
  unsigned char calls[10][12];

  tlptr=tickerline;
  intick=0;
  for (i=0,mh1=MHeardBase;i<HEARD_ENTRIES;++i,++mh1)
   {
    int secs;
    int clen,plen;
    int j;
    const unsigned char *sptr;

    if (intick==10) break;
    if (mh1->FromCall[0]=='\0') break;
    secs=MAIN_GetSecondsAgo(&mh1->Time);
    if ( (secs<0) || (secs>5*60) ) continue;
    sptr=mh1->FromCall;
    clen=0;
    while (*sptr!='\0')
     {
      if (*(sptr++)=='-') break;
      ++clen;
     }
    if ( (clen>6) || (clen<=0) ) continue;
    for (j=0;j<8;++j)
     {
      if ( (CompareStringLimit(mh1->FromCall,Preferences.Callsets[j].MyCall,clen)) &&
           ( (Preferences.Callsets[j].MyCall[clen]=='\0') ||
             (Preferences.Callsets[j].MyCall[clen]=='-') ))
       {
        break;
       }
     }
    if (j!=8) continue;
    for (j=0;j<intick;++j)
     {
      if ((CompareStringLimit(mh1->FromCall,calls[j],clen)) &&
          (calls[j][clen]=='\0')) break;
     }
    if (j!=intick) continue;


    CopyString(calls[intick],mh1->FromCall);
    calls[intick][clen]='\0';
    ++intick;

    sptr=mh1->FromCall;
    plen=7-clen;
    while (clen>0)
     {
      *(tlptr++)=*(sptr++);
      --clen;
     }
    while (plen>0)
     {
      *(tlptr++)=' ';
      --plen;
     }
   }
  while (intick<10)
   {
    for (i=0;i<7;++i) *(tlptr++)=' ';
    intick++;
   }
  *tlptr='\0';
  if (CompareString(tickerline,Ticker)) return(0);
  CopyString(Ticker,tickerline);
  return(1);
 }


struct MHeardEntry *QSO_MHeardEntry(unsigned long linenr)
 {
  struct MHeardEntry *mh;
  if (MHeardBase==NULL) return(NULL);
  mh=MHeardBase+linenr;
  if (mh->FromCall[0]=='\0') return(NULL);
  return(mh);
 }

int QSO_MHeard_Init(void)
 {
  MHeardBase=AllocVec(sizeof(struct MHeardEntry)*HEARD_ENTRIES,0x10000);
  if (MHeardBase==NULL) return(0);
  MHeardPointer=NULL;
  return(1);
 }

void QSO_MHeard_Deinit(void)
 {
  if (MHeardBase==NULL) return;
  FreeVec(MHeardBase);
  MHeardBase=NULL;
  return;
 }

int QSO_MHeardLine(int linenr,unsigned char *outline)
 {
  struct MHeardEntry *mh1,*mh2;

  if (MHeardBase==NULL) return(-1);

  mh1=MHeardBase+linenr;
  if (mh1->FromCall[0]=='\0') return(-1);

  for (mh2=MHeardBase;mh2<mh1;++mh2)
   {
    if (CompareString(mh1->FromCall,mh2->FromCall)) return(0);
   }

   {
    unsigned long outarray[4];
    outarray[0]=(unsigned long) mh1->FromCall;
    outarray[1]=mh1->Time.ds_Minute/60;
    outarray[2]=mh1->Time.ds_Minute%60;
    outarray[3]=mh1->Time.ds_Tick/50;
    RawDoFmt("%-9s %02ld:%02ld:%02ld       ",outarray,PutRawDoFmt,outline);
   }
  return(1);
 }

int QSO_MHeardLineLong(int linenr,unsigned char *outline)
 {
  struct MHeardEntry *mh1;

  if (MHeardBase==NULL) return(-1);

  mh1=MHeardBase+linenr;
  if (mh1->FromCall[0]=='\0') return(-1);

   {
    unsigned long outarray[16];

    outarray[0]=(unsigned long) mh1->FromCall;
    outarray[1]=(unsigned long) mh1->ToCall;
    outarray[2]=mh1->Time.ds_Minute/60;
    outarray[3]=mh1->Time.ds_Minute%60;
    outarray[4]=mh1->Time.ds_Tick/50;
    outarray[5]=mh1->ControlPackets;
    outarray[6]=mh1->RRPackets;
    outarray[7]=mh1->REJPackets;
    outarray[8]=mh1->RNRPackets;
    outarray[9]=mh1->IPackets;
    outarray[10]=mh1->IBytes;
    outarray[11]=mh1->TNCNumber;
    outarray[12]=mh1->PortNumber;
    outarray[13]=(unsigned long) ((mh1->Path[0]=='\0')?"":"via");
    outarray[14]=(unsigned long) mh1->Path;
    RawDoFmt("%-9s <-> %-9s %02ld:%02ld:%02ld %5ld %6ld %6ld %6ld %6ld %8ld\r  TNC %01ld:%01ld  %s %s\r",
    outarray,PutRawDoFmt,outline);
   }
  return(1);
 }
