/*->c.replay */

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <locale.h>
#include <time.h>

#include "h.os"
#include "h.wimp"
#include "h.wimpt"
#include "h.werr"
#include "h.flex"


#include "h.Drawlevel0"


#include "h.def"
#include "h.wos"
#include "h.main"
#include "h.mym"
#include "h.serial"
#include "h.ram"
#include "h.file"
#include "h.pr"
#include "h.strdef"
#include "h.key"
#include "h.script"
#include "h.dir"
#include "h.batch"
#include "h.fsx"
#include "h.timex"
#include "h.fax"
#include "h.view"
#include "h.tiff"
#include "h.buffer"
#include "h.etc"
#include "h.xext"
#include "h.err"
#include "h.state"
#include "h.dbug"


#include "h.code"

#include "h.text"
#include "h.config"


#include "h.replay"


/****************************************************************************/

int modemtype;  /* is this a voice modem? */


int voicebps;
int voicesps;
int voicesdet;
int voicemax;
int voicecomp; /* is the actual value - modemcode, not an index */



char voicecompressionname[MAXVNAMES][16];
int  voicecompressionmodemcode[MAXVNAMES];
int  voicecompressionourcode[MAXVNAMES];


int voicenames;  /* how many types of compression */



int voiceindex(int code)
{
 int i;

 for(i=0;i<voicenames;i++)
   if(voicecompressionmodemcode[i]==code) break;

 if(i<voicenames) return(i);
 else             return(-1);
}




/* called when modem driver loaded */

/* capacity,<value>,<value>;capacity,<value>,<value>; etc. */


#define MAXCAPS 1
#define MAXARG  8

typedef int (*tagfn)(char ** argv);


typedef struct capstr
{
 char * tag;
 int    argn;
 tagfn  tagf;

} capstr;



/* return 0 if OK */

static int compression(char ** argv)
{
 strcpy(voicecompressionname[voicenames],argv[1]);
 sscanf(argv[2],"%d",&voicecompressionmodemcode[voicenames]);
 sscanf(argv[3],"%d",&voicecompressionourcode[voicenames]);
 voicenames++; 

 return(0);
}


static capstr captab[MAXCAPS]=
{
 "compression",3,compression,


};


void voicemodeminit(void)
{
 char   caps[1024];
 char * p;
 char * argv[MAXARG];
 int    argn;
 int    target;
 int    error;
 int    ch;
 int    i;

 target=0;
 i=0;
 error=0;

 voicenames=0;


 if(xexec3("modem_cap",caps,&modemtype))
 {
  argn=0;
  p=caps;
  argv[argn]=p;
  while((ch=*p++)!=0)
  {
   if(ch==',' || ch==';') 
   {
    *(p-1)=0;
    argn++;
    if(argn==1) /* find name */
    {
     for(i=0;i<MAXCAPS;i++)
      if(!cstrcmp(argv[0],captab[i].tag)) break;

     if(i>=MAXCAPS)
     {
      error=1;
      break;
     }

     target=captab[i].argn+1;

    }
    else
    if(argn==target)  /* do it */
    {
     if(captab[i].tagf) error=captab[i].tagf(argv);
     if(error) break;

     if(ch==';') argn=0;
     else        argn=1;
    }
    else
    if(ch==';') 
    {
     error=1;
     break;
    }

    argv[argn]=p;
   }
  }
 }
 else modemtype=AFAX;

 if(error) errorbox("{ER08}");
}


/*****************************************************************************/

#define REPBUFFSIZE 0x80000



typedef struct sndhdr
{
 char name[16];
 int  bps;
 int  sps;
 int  compression;
 int  type;
 int  spare[16];

} sndhdr;




static int    repspeed;           /* real variables              */
static int    reppause;           /* paused                      */
static int    repframe;           /* record ?                    */
static int    repframeno;         /* total length  in bytes      */
static int    repbytes;           /* current point in bytes      */

static int    crepspeed;          /* what the display is showing */
static int    creppause;
static int    crepframeno;
static int    crepframe;
static int    crepbytes;


static char   repname[256];       /* name of what we are replaying */
static int    replength;          /* total length bytes            */
static int    wasvolatile;


static int    repsrc=LINE;
static int    repdest=MODEMLS;

static int    repmode;

static int    stopflag;
static int    fileposn;


static int    repbn;              /* number of file in batch */
                                  /* -1 == none              */
static int    repview;            /* and which one           */



#define RNONE   0
#define RPLAY   1
#define RRECORD 2



/* addresses of sprites */


#define FDIGIT4  0
#define FDIGIT3  1
#define FDIGIT2  2
#define FDIGIT1  3
#define FDIGIT0  4

#define BDIGIT4  5
#define BDIGIT3  6
#define BDIGIT2  7
#define BDIGIT1  8
#define BDIGIT0  9

#define PAUSE    10
#define RFRAME   11


static char fmap[5]={13,14,39,40,41};

static char rsnames[12][12];   /* second 12 is indi space */


static void replaysetup(void)
{
 int i;

 for(i=FDIGIT4;i<=FDIGIT0;i++)
 {
  strcpy(rsnames[i],"lcd0");
  setspa(whandle[REPLAY],fmap[i],12,rsnames[i]);
 }

 for(i=BDIGIT4;i<=BDIGIT0;i++)
 {
  strcpy(rsnames[i],"lcd0");
  setspa(whandle[REPLAY],7+i-BDIGIT4,12,rsnames[i]);
 }

 for(i=15;i<=24;i++) seti(whandle[REPLAY],i,0x70000000,0xF0000000);

 strcpy(rsnames[PAUSE],"p1");
 setspa(whandle[REPLAY],12,12,rsnames[PAUSE]);

 strcpy(rsnames[RFRAME],"");
 setspa(whandle[REPLAY],25,12,rsnames[RFRAME]);

 repspeed=0;
 crepspeed=0; 
 reppause=creppause=0;
 crepframeno=0;
 repframe=crepframe=0;
 repbytes=crepbytes=0;
 fileposn=repbytes+sizeof(sndhdr);
}


static char * replaystr[4]={"p1","p2","p3","p4"};

static void replayupdate(void)
{
 int nr;
 int nc;
 int r;
 int c;
 int ir;
 int ic;
 int i;
 int effreppause;

 effreppause=reppause;
 if(!effreppause)
 {
  if(repmode==RNONE) 
  {
   if(repbytes==(replength-sizeof(sndhdr))) effreppause=3;
   else                                     effreppause=2;
  }
 }


 if(whandle[REPLAY])
 {
  if(effreppause!=creppause)
  {
   strcpy(rsnames[PAUSE],replaystr[effreppause]);
   seti(whandle[REPLAY],12,0,0);
  }

  if(repframe!=crepframe)
  {
   strcpy(rsnames[RFRAME],repframe?"f":"");
   seti(whandle[REPLAY],25,0,0);
  }

  if(repbytes!=crepbytes) /* things get complicated */
  {
   r=repbytes/10;
   c=crepbytes/10;

   for(i=0;i<5;i++)
   {
    nr=r/10;
    nc=c/10;

    ir=r-nr*10;
    ic=c-nc*10;

    if(ir!=ic)
    {
     rsnames[BDIGIT0-i][3]='0'+ir;
     seti(whandle[REPLAY],11-i,0,0);
    }

    r=nr;
    c=nc;

    if(r==c) break;
   }
  }


  if(repframeno!=crepframeno) /* things get complicated */
  {
   r=repframeno/10;
   c=crepframeno/10;

   for(i=0;i<5;i++)
   {
    nr=r/10;
    nc=c/10;

    ir=r-nr*10;
    ic=c-nc*10;

    if(ir!=ic)
    {
     rsnames[FDIGIT0-i][3]='0'+ir;
     seti(whandle[REPLAY],fmap[FDIGIT0-i],0,0);
    }

    r=nr;
    c=nc;

    if(r==c) break;
   }
  }


  if(crepspeed!=repspeed)
  {
   r=repspeed;
   c=crepspeed;

   for(i=0;i<10;i++)
   {
    r-=10;
    c-=10;

    if((r<0 && c>=0) || (c<0 && r>=0))
    {
     seti(whandle[REPLAY],i+15,r>=0?0xB0000000:0x70000000,0xF0000000);
    }
    else
    if(c<=0 && r<=0) break;
   }
  }
 }

 crepspeed=repspeed; 
 creppause=effreppause;
 crepframeno=repframeno;
 crepframe=repframe;
 crepbytes=repbytes;
}

/*****************************************************************************/

/* int voicemodem_device(int dev); */

static int setdevice(int dev)
{
 xexec2("voicemodem_device",&dev,NULL,NULL);

 return(0);
}

/* int voicemodem_record(int start,int src) */

static void repstartrecording(void) 
{
 int start;
 stopflag=0;
 repmode=RRECORD;
 start=1;
 xexec2("voicemodem_record",&start,NULL,NULL);
}


static void repstoprecording(void)
{
 int start;
 start=0;
 xexec2("voicemodem_record",&start,NULL,NULL);
 repmode=RNONE;
}


static void updaterecbytes(void)
{
 repbytes=fileposn-sizeof(sndhdr);
 repframeno=repbytes;
 replength=fileposn;
}


static os_error * recordfilelo(char * name,int start,
                                        int * posn,int max,int usebn)
{
 os_error * err;
 int        fh;
 int        c;
 buffer     bf;
 int        escape;
 int        time;


 setdbdataphase(1,1);

 escape=0;
 time=clock()+max*100;


 err=fs_open(name,'u',&fh);
 if(!err && fh)
 {
  err=fs_seek(fh,start);
  if(!err)
  {
   *posn=start;
   err=bf_alloc(&bf,REPBUFFSIZE,fh,0);
   if(!err)
   {
    while(!stopflag)
    {
     c=getbyte();
     if(c!=-1)
     {
      if(escape)
      {
       escape=0;
       if(c==ETX) break;

       bf_put(&bf,DLE); /* added */
       bf_put(&bf,c);
      }
      else
      if(c==DLE) escape=1;
      else
      {
       bf_put(&bf,c);
      }
      (*posn)++;
     }
     else
     {
      if(max && time<clock()) break;
      pollzt();
     }
    }
    bf_dump(&bf);
    bf_dealloc(&bf);
   }
  }
  err=fs_setfileextent(fh,*posn);
  err=fs_close(fh,err);

  if(repbn>=0 && usebn)
  {
   view=repview;
   stat(name,&vtable[repview][repbn].stat);
   viewupdatetexticon(repbn);
  }
 }

 setdbdataphase(0,1);

 return(err);

 USE(name);
}



static os_error * recordfile(void)
{
 os_error * err;
 int        start;

 err=NULL;


 if(activestate() && !onlinestate())
 {
  if(activemodem())
  {
   setdevice(repsrc);
 
   repstartrecording();

   start=repbytes+sizeof(sndhdr);

   err=recordfilelo(repname,start,&fileposn,0,1);

   repstoprecording();

   updaterecbytes();

   setdevice(0);

   replayupdate();

   clearmodem();
  }
 }
 return(err);
}



static os_error * voicecreate(char * name)
{
 os_error * err;
 sndhdr     snd;
 int        fh;

 memset(&snd,0,sizeof(sndhdr));
 strcpy(snd.name,"ArcFax");
 snd.bps=voicebps;
 snd.sps=voicesps;
 snd.compression=voicecomp;


 err=fs_open(name,'w',&fh);
 if(!err && fh)
 {
  fs_write(fh,&snd,sizeof(sndhdr));
  err=fs_close(fh,err);
  setftype(name,ASND);
 }

 return(err);
}




static void rxcancel(void)
{
 stopflag=1;
}


/* recordmessage(voicemaxmessage,LINE); */

int recordmessage(int fp)
{
 os_error * err;
 char       name[256];
 int        posn;
 int        max;
 int        bn;

 max=stack[fp];

 genbatchname(RXBFILE,name,ASND);

 err=voicecreate(name);
 if(!err)
 {
  setfaxcancelhandler(rxcancel);

  repstartrecording(); 

  recordfilelo(name,sizeof(sndhdr),&posn,max,0);

  repstoprecording();

  clearfaxcancelhandler();

  bn=addtorxbatch(ASND,name,0,0)-1;

  vtable[RXBFILE][bn].tsum.xres=voicebps;
  vtable[RXBFILE][bn].tsum.maxwidth=voicecomp;
  vtable[RXBFILE][bn].tsum.npages=voicesps;

  writetolog("{LOG11} %s {LOG14}\n",leaf(name));

  batchpop();
 }

 return(err!=NULL);
}



/*****************************************************************************/

/* int voicemodem_play(int start,int dest)  */

static void repstartplaying(void)
{
 int start;
 stopflag=0;
 start=1;
 xexec2("voicemodem_play",&start,NULL,NULL);
 repmode=RPLAY;
}


static void repstopplaying(void)
{
 int start;
 repmode=RNONE;
 start=0;
 xexec2("voicemodem_play",&start,NULL,NULL);
}


static void updaterepbytes(void)
{
 repbytes=fileposn-sizeof(sndhdr);
}


static os_error * playfilelo(char * name,int start,int length,int * posn)
{
 os_error * err;
 int        fh;
 int        c;
 buffer     bf;

 setdbdataphase(1,0);
 setblockmode(); 

 err=fs_open(name,'r',&fh);
 if(!err && fh)
 {
  err=fs_seek(fh,start);
  if(!err)
  {
   *posn=start;
   err=bf_alloc(&bf,REPBUFFSIZE,fh,length);
   if(!err)
   {
    while(!stopflag)
    {
     bf_get(&bf,&c);
     if(c==EOF) break;

     (*posn)++;
   /*  if(c==DLE) outbyteb(DLE);   removed */

     outbyteb(c);
    }
    bf_dealloc(&bf);
   }
  }
  err=fs_close(fh,err);
 }

 clrblockmode();
 setdbdataphase(0,0);

 return(err);
}



static os_error * playfile(void)
{
 os_error * err;
 int        start;
 int        length;
 static int norecurse;

 err=NULL;

 if(norecurse) return(err);
 norecurse=1;

 if(activestate() && !onlinestate())
 {
  if(activemodem())
  {
   setdevice(repdest);


   repstartplaying();


   start=repbytes+sizeof(sndhdr);
   length=replength-(repbytes+sizeof(sndhdr));


   err=playfilelo(repname,start,length,&fileposn);


   repstopplaying();


   setdevice(0);
  

   if(!stopflag)
   {
    repbytes=replength-sizeof(sndhdr);
    fileposn=replength;
    replayupdate();
   }

   clearmodem();
  }
 }

 norecurse=0;

 return(err);
}



static os_error * reppreamble(void)
{
 fstat fx;

 repframeno=0;
 repbytes=0;
 fileposn=sizeof(sndhdr);

 if(repname[0])
 {
  stat(repname,&fx);
  replength=fx.length;
  repframeno=replength-sizeof(sndhdr);
  if(repframeno<0) repframeno=0;
 }

 return(NULL);
}


static void startit(void)
{
 if(repmode==RNONE)
 {
  if(repname[0])
  {
   stopflag=0;
   if(repframe) recordfile();
   else         playfile();
  }
 }
}


static void stopit(void)
{
 if(repmode==RPLAY || repmode==RRECORD)
 {
  stopflag=1;
 }
}


static void fullstop(void)
{
 repframe=0;
 stopit();
}


static int repsrcicon[5]=
{
 0,   /* 0       */
 27,  /* 1 Mic   */
 0,   /* 2       */
 28,  /* 3 Line  */
 32,  /* 4 Phone */
};


static int repdesticon[6]=
{
 0,   /* 0          */
 0,   /* 1          */
 29,  /* 2 ModemLS  */
 31,  /* 3 Line     */
 0,   /* 4          */
 30,  /* 5 Computer */
};


/* clicks on window */

void replayclick(void)
{
 switch(icon)
 {
  case 28:    /* src line  */
          if(repsrc!=LINE)
          {
           deselect(ewindow,repsrcicon[repsrc]);
           repsrc=LINE;
           select(ewindow,repsrcicon[repsrc]);
          }
          break;

  case 27:    /* src mic   */
          if(repsrc!=MIC)
          {
           deselect(ewindow,repsrcicon[repsrc]);
           repsrc=MIC;
           select(ewindow,repsrcicon[repsrc]);
          }
          break;

  case 32:    /* src phone */
          if(repsrc!=PHONE)
          {
           deselect(ewindow,repsrcicon[repsrc]);
           repsrc=PHONE;
           select(ewindow,repsrcicon[repsrc]);
          }
          break;

  case 31:    /* dest line     */
          if(repdest!=LINE)
          {
           deselect(ewindow,repdesticon[repdest]);
           repdest=LINE;
           select(ewindow,repdesticon[repdest]);
          }
          break;

  case 29:    /* dest modem    */
          if(repdest!=MODEMLS)
          {
           deselect(ewindow,repdesticon[repdest]);
           repdest=MODEMLS;
           select(ewindow,repdesticon[repdest]);
          }
          break;

  case 30:    /* dest computer */
          if(repdest!=COMPUTERLS)
          {
           deselect(ewindow,repdesticon[repdest]);
           repdest=COMPUTERLS;
           select(ewindow,repdesticon[repdest]);
          }
          break;

  case 33:   /* stop */
          fullstop();
          break;

  case  6:  /* play */
          startit();
          break;


  case 37: /* pause */
          if(repmode!=RNONE || reppause)
          {
           if(!reppause) stopit();
           reppause^=1;
           if(!reppause) startit();
          }
          break;

  case 35:
          if(repmode==RNONE) repframe^=1;
          break;

  case  1: /* rewind */
          fullstop();
          if(repmode==RNONE)
          {
           if(repbytes>0) 
           {
            repbytes-=1000;
            if(repbytes<0) repbytes=0;
           }
           fileposn=repbytes+sizeof(sndhdr);
          }
          break;

  case  2: /* FF */
          fullstop();
          if(repmode==RNONE)
          {
           if(repbytes<repframeno)
           {
            repbytes+=1000;
            if(repbytes>repframeno) repbytes=repframeno;
           }
           fileposn=repbytes+sizeof(sndhdr);
          }
          break;

  case  3: /* goto end */
          fullstop();
          if(repmode==RNONE)
          {
           repbytes=repframeno;
           fileposn=repbytes+sizeof(sndhdr);
          }
          break;

  case 26: /* goto start */
          fullstop();
          if(repmode==RNONE)
          {
           repbytes=0;
           fileposn=repbytes+sizeof(sndhdr);
          }
          break;

 case  42:
          menuwindow(setsave(SAVEVOICE));
          break;

 }
 replayupdate();
}




void replayzero(void)
{
 static int repz;


 if(repz) return;

 repz=1;

 if(repmode==RPLAY)
 {
  updaterepbytes();
 }
 else
 if(repmode==RRECORD)
 {
  updaterecbytes();
 }

 repz=0;

 replayupdate();
}





void replayclose(void)
{
 if(whandle[REPLAY])
 {
  stopit();
  closedownt(REPLAY);
  remzeroevent(REPLAYZERO);
 }

 if(wasvolatile)
 {
  remove(repname);
  wasvolatile=0;
 }
}




void replay(char * name,int xvolatile,int play)
{
 int   handle;

 repbn=-1;

 replayclose();

 if(name) 
 {
  strcpy(repname,name);
  handle=createwindowsub(REPLAY,leaf(repname));
 }
 else 
 {
  repname[0]=0;
  handle=createwindowsub(REPLAY,"<none>");
 }

 if(!handle) return;

 reppreamble();

 replaysetup();

 wasvolatile=xvolatile;

 select(handle,repsrcicon[repsrc]);
 select(handle,repdesticon[repdest]);

 popup(handle,0);
 addzeroevent(REPLAYZERO);

 if(play) playfile();
}


void repvn(int view,int i)
{
 char       string[FSMAXPATH];

 sprintf(string,"%s.%s",xvname(view),vtable[view][i].name);
 replay(string,0,1);

 repbn=i;
 repview=view;
}


static void txcancel(void)
{
 stopflag=1;
}


/* int replaymessage(string & name,int dest); */

int replaymessage(int fp)
{
 os_error * err;
 char       name[256];
 char     * p;
 int        posn;
 fstat      fs;


 p=stringptr(stack[fp]);
 if(leaf(p)!=p) strcpy(name,p);
 else 
 {
  sprintf(name,"%s.%s",xvname(TXBFILE),p);
 }

 setfaxcancelhandler(txcancel);

 repstartplaying();

 stat(name,&fs);
 err=playfilelo(name,sizeof(sndhdr),fs.length-sizeof(sndhdr),&posn);

 repstopplaying();

 clearfaxcancelhandler();

 return(err!=NULL);
}



void replaystop(int fp)
{
 stopflag=1;
}


/*****************************************************************************/

int savevoice(char * name)
{
 os_error * err;

 repbn=-1;

 err=voicecreate(name);
 if(!err)
 {
  replay(name,0,0);
  return(1);
 }

 return(0);
}


/* save ended on a batch window  */
/* passed a leaf name - you hope */

int savevoicebatch(char * name)
{
 int code;

 char string[256];
 sprintf(string,"%s.%s",xvname(view),name);

 code=savevoice(string);

 repview=view;

 if(view==TXBFILE) repbn=addtotxbatch(ASND,string,0,0)-1;
 else              repbn=addtorxbatch(ASND,string,0,0)-1;

 return(code);
}


os_error * checksnd(tiffsum * ts,char * filename)
{
 os_error * err;
 sndhdr     snd;
 int        fh;

 err=fs_open(filename,'r',&fh);
 if(!err && fh)
 {
  memset(&snd,0,sizeof(sndhdr));
  err=fs_read(fh,&snd,sizeof(sndhdr));
  if(!err)
  {
   if(strcmp(snd.name,"ArcFax")) err=&err_notavoice;
   else
   {
    ts->npages=snd.sps;
    ts->xres=snd.bps;
    ts->maxwidth=snd.compression;
   }
  }
  err=fs_close(fh,err);
 }

 return(err);
}


os_error * loadsnd(char * filename)
{
 os_error * ep;
 tiffsum    tsum;

 ep=checksnd(&tsum,filename);
 if(!ep)
 {
  replay(filename,0,0);
 }

 report(ep);
 return(ep);
}


