//->$.Fax.!Arcfax.Driver.Class1V
// Copyright (c) David Pilling 1998
//



string modemcarrierwaitshort="ATS7=3";

string modemcarrierwaitlong="ATS7=45";

string modemhangup="ATH0S7=45";

int    modemdtespeed=57600;

int    modemusedocumentresolution=0;

string modeminitstring1="ATE1";
string modeminitstring2="ATQ0V1&C1S0=0S10=50";
string modeminitstring3="";



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


int modemrxrate;
int modemtxrate;
int modembits;
int modemstop;
int modemparity;
int modemport;
int modemanswer;
int modemtonedial;
int modemflow;
int modemrings;
int modemmode;
int modemanswermode;



// count or purge buffer

int modem_countpurge(int rxtx,int code)
{
 return(devcon(Device_CountPurge,modemport,rxtx,code));
}


// tell modem to use a given sort of flow control

void modem_setflow(int flow)
{
 devcon(Device_Flow,modemport,flow,1);
 modemflow=flow;
}


// set rx rate

void modem_setrxrate(int rate)
{
 devcon(Device_RxRate,modemport,rate,1);
 modemrxrate=devcon(Device_RxRate,modemport,0,0);
}


// set tx rate

void modem_settxrate(int rate)
{
 devcon(Device_TxRate,modemport,rate,1);
 modemtxrate=devcon(Device_TxRate,modemport,0,0);
}


// set number of data bits

void modem_setbits(int bits)
{
 devcon(Device_DataBits,modemport,bits,1);

 modembits=devcon(Device_DataBits,modemport,0,0);
 modemparity=devcon(Device_ParityBits,modemport,0,0);
 modemstop=devcon(Device_StopBits,modemport,0,0);
}


// set parity bits

void modem_setparity(int parity)
{
 devcon(Device_ParityBits,modemport,parity,1);

 modembits=devcon(Device_DataBits,modemport,0,0);
 modemparity=devcon(Device_ParityBits,modemport,0,0);
 modemstop=devcon(Device_StopBits,modemport,0,0);
}


// set stop bits

void modem_setstop(int stop)
{
 devcon(Device_StopBits,modemport,stop,1);

 modembits=devcon(Device_DataBits,modemport,0,0);
 modemparity=devcon(Device_ParityBits,modemport,0,0);
 modemstop=devcon(Device_StopBits,modemport,0,0);
}


// set to tone/pulse dial

int modem_tonedial(int tonedial)
{
 modemtonedial=tonedial;
 return(modemtonedial);
}


void modem_setanswer(int answer)
{
 modemanswer=answer;
}


void modem_setmode(int mode)
{
 modemmode=mode;
}

   
int modem_cap(string & s)
{
 s="compression,ADPCM,0,0,GSM,1,1;";
 return(VOICE|FAX|DATA);
}


// is the modem on line ?

int modem_online(void)
{
 return(devcon(Device_Status,modemport,0,-1) & SERIAL_DCD);
}



int RETURNOK=1;
int RETURNCONNECT=2;
int RETURNCONNECTIMM=3;
int RETURNCONNOC=4;
int RETURNCONNECTRM=5;
int RETURNNOCHECK=0x20;




int modemechoedcommand(string & s,int result,int attempts,int delay)
{
 int    n;
 int    c;
 string t;
 int    seencode;
 int    nocheck;

 seencode=modemmode;
 nocheck=result & RETURNNOCHECK;
 result=result & 0x1F;

 if(slen(s)==0) return(0);
        
 for(n=0;n<attempts;n++)
 {
  pause(25);
  sprints(s);
  sprints("|M");
  while(1)
  {
   if(!sreadtext(t,delay,100)) break;

   if(s==t || nocheck)
   {
    if(result)
    {
     while(1)
     {
      if(!sreadtext(t,delay,100))
      {
       if(result==RETURNCONNOC || result==RETURNCONNECTRM)
       {
        sprints("|M");
        sreadtext(t,25,100);
        return(2);
       }
       break;
      }
      c=sgetc(2);                                // try to swallow any LF's
      if(result==RETURNOK && t=="OK") return(0);
      else
      if(result==RETURNCONNECT &&   (t/"CONNECT"==0)) return(0);
      else
      if(result==RETURNCONNECTRM && (t/"CONNECT"==0)) return(0);
      else
      if(result==RETURNCONNECTRM && t=="+FCERROR") return(3);
      else
      if(result==RETURNCONNECTIMM)
      {
       if(t/"DATA">=0) 
       {
        seencode=DATA;
        return(seencode);
       }
       else
       if(t/"FAX">=0) seencode=FAX;
       else
       if(t/"VCON">=0) return(VOICE);
       else
       if(t/"CONNECT"==0) return(seencode);
       else
       if(!((t/"RINGING"==0)||(t/"PROTOCOL"==0)||(t/"CARRIER"==0))) 
       {
        logreason(t);
        return(0);
       }
      }
      else
      if(result==RETURNCONNOC)
      {
       if(t/"CONNECT"==0) return(0);
       else
       if(t=="NO CARRIER") return(2);
      }
     }
    }
    else return(0);
    break;
   }
  }
 }

 if(result==RETURNCONNECTIMM) return(0);
 return(1);
}



int modemgetOK(void)
{
 string t;

 if(sreadtext(t,3000,100))
 {
  if(t=="OK") return(0);
 }
 return(1);
}



// put modem off line

void modem_disconnect(void)
{
 devicedtr(1,modemport);
 pause(200);
 devicedtr(0,modemport);

 sprints("+++");
 pause(200);
 modemechoedcommand(modemhangup,RETURNOK,5,500);
}


/*****************************************************************************/
// This section is concerned with Fax handling

int faxmodemretries;
int faxmodemresolution;
int faxmodemmaxbitrate;
int faxmodemminbitrate;
int faxmodemlength;
int faxmodemwidth;
int faxmodemdatacompression;
int faxmodemerrorcorrection;
int faxmodembinaryftp;


string faxmodemid;




/****************************************************************************/
// Fax transmit logic

int CX14400=5;
int CX12200=4;
int CX9600=3;
int CX7200=2;
int CX4800=1;
int CX2400=0;


// Parameters for session

int txresln;             // resolution
int txbitrate;           // bit rate code
int txpagewidth;         // page width code
int txpagelength;        // page height code
int txdatacompression;   // data compression
int txerrorcorrection;   // error correction
int txbinaryftp;         // binary ftp
int txscantime;          // min scan time


// Parameters for document

int txdocpages;
int txdocresolution;
int txdocwidth;


// called by main program, to set parameters for next doc to send

void faxmodem_txparams(int pages,int resolution,int width)
{
 txdocpages=pages;
 txdocresolution=resolution;
 txdocwidth=width;
}


// send data carrier for current tx rate

int txsendmodulation(int tcf)
{
 int error;

 if(txbitrate==CX14400)
 {
  if(tcf) error=modemechoedcommand("AT+FTM=145",RETURNCONNECT,1,500);
  else    error=modemechoedcommand("AT+FTM=146",RETURNCONNECT,1,500);
 }
 else
 if(txbitrate==CX12200)
 {
  if(tcf) error=modemechoedcommand("AT+FTM=121",RETURNCONNECT,1,500);
  else    error=modemechoedcommand("AT+FTM=122",RETURNCONNECT,1,500);
 }
 else
 if(txbitrate==CX9600)
  error=modemechoedcommand("AT+FTM=96",RETURNCONNECT,1,500);
 else
 if(txbitrate==CX7200)
  error=modemechoedcommand("AT+FTM=72",RETURNCONNECT,1,500);
 else
 if(txbitrate==CX4800)
  error=modemechoedcommand("AT+FTM=48",RETURNCONNECT,1,500);
 else
 if(txbitrate==CX2400)
  error=modemechoedcommand("AT+FTM=24",RETURNCONNECT,1,500);

 return(error);
}


// input G3 type scan time, out Class 2 style 

int scantocode(int scan)
{
 if(scan==0x0) return(0x5);
 if(scan==0x1) return(0x1);
 if(scan==0x2) return(0x3);
 if(scan==0x3) return(0x4);
 if(scan==0x4) return(0x7);
 if(scan==0x5) return(0x6);
 if(scan==0x6) return(0x2);
 if(scan==0x7) return(0x0);
 return(0x0);
}


// input Class 2 type scan time, out G3 style

int codetoscan(int code)
{
 if(code==0x0) return(0x7);
 if(code==0x1) return(0x1);
 if(code==0x2) return(0x6);
 if(code==0x3) return(0x2);
 if(code==0x4) return(0x3);
 if(code==0x5) return(0x0);
 if(code==0x6) return(0x5);
 if(code==0x7) return(0x4);
 return(0x7);
}


int codetoscanres(int code,int res)
{
 if(code==2) code=res?1:3;
 if(code==4) code=res?3:5;
 if(code==6) code=res?5:7;
 return(codetoscan(code));
}



int txsendframe(string & s,int first)
{
 int error;

 if(first) error=modemechoedcommand("AT+FTH=3",RETURNCONNECT,1,500);
 else      error=0;

 if(!error)
 {
  ssendframe(s);
  sreadtext(s,3000,500);   // should get 'OK' for last frame
                           // or CONNECT for intermediate frame
 }

 return(error);
}



// Non Standard Facilities

int txgetNSF(string & s)
{
 writetodebug("get NSF");
 return(0);
}


// CFR

int txgetCFR(string & s)
{
 writetodebug("get CFR");
 return(0);
}

// MCF

int txgetMCF(string & s)
{
 writetodebug("get MCF");
 return(0);
}

// RTN

int txgetRTN(string & s)
{
 writetodebug("get RTN");
 return(0);
}

// RTP

int txgetRTP(string & s)
{
 writetodebug("get RTP");
 return(0);
}



// Called Subscriber Identification
// data starts at byte 3
// 20 bytes in reverse order.

int txgetCSI(string & s) 
{
 writetodebug("get CSI");
 return(sgetsid(s,TX,1));
}


// Digital Identification Signal

int txgetDIS(string & s)
{
 int bitrate;
 int c4;
 int c5;

 writetodebug("get DIS");

 c4=schar(s,4);
 c5=schar(s,5);

 txresln=(c4 & 0x40)>>6;

 if(modemusedocumentresolution)
 {
  if(txresln) txresln=txdocresolution;
 }
 else
 {
  if(faxmodemresolution==0) txresln=0;
 }


 bitrate=(c4 & 0x3C)>>2;

 if((bitrate & 0x8) && (faxmodemmaxbitrate>=CX14400)) txbitrate=CX14400;
 else 
 if(bitrate & 0x1) txbitrate=CX9600;
 else
 if(bitrate & 0x2) txbitrate=CX4800;
 else              txbitrate=CX2400;

 txpagewidth=c5 & 0x3;
 if(txpagewidth==0x3) txpagewidth=0x2;

 txpagelength=(c5 & 0xC)>>2;
 if(txpagelength==0x3) txpagelength=0x2;

 txscantime=(c5 & 0x70)>>4;
 txscantime=scantocode(txscantime);

 if(faxmodemdatacompression) txdatacompression=(c4 & 0x80)>>7;
 else                        txdatacompression=0;    // data compression

 txerrorcorrection=0;   // error correction
 txbinaryftp=0;         // binary ftp

 faxtxscan(txbitrate,txscantime);
 faxtxparams(txpagelength,txdatacompression,txerrorcorrection,txbinaryftp);

 return(0);
}


//  Transmitting subscriber identification

int txsendTSI(int first)
{
 string s;
 int    idlen;
 int    i;

 writetodebug("send TSI");

 s=chars(0xFF);
 s=s+chars(0x0 |0x3); // not last frame
 s=s+chars(0x42|0x1); // TSI=X100 0010

 idlen=slen(faxmodemid);
 for(i=idlen-1;i>=0;i--) s=s+chars(schar(faxmodemid,i));
 i=idlen;
 while(i++<20) s=s+chars(32);

 return(txsendframe(s,first));
}


// Digital Command Signal 
// Set up responding to DIS

int txsendDCS(int first)
{
 string s;
 int    bitrate;
 int    scantime;

 writetodebug("send DCS");

 if(txbitrate==CX14400) bitrate=0x8;
 else
 if(txbitrate==CX12200) bitrate=0xA;
 else
 if(txbitrate==CX9600)  bitrate=0x1;
 else
 if(txbitrate==CX7200)  bitrate=0x3;
 else
 if(txbitrate==CX4800)  bitrate=0x2;
 else                   bitrate=0x0;

 scantime=codetoscanres(txscantime,txresln);

 s=chars(0xFF);         
 s=s+chars(0x10|0x3);  // last frame
 s=s+chars(0x82|0x1);  // DCS=X100 0001
 s=s+chars(0);
 s=s+chars(((0x2)+(txresln<<6)+(bitrate<<2)+(txdatacompression<<7)));
 s=s+chars((txpagewidth+(txpagelength<<2)+(scantime<<4)));

 return(txsendframe(s,first));
}


// send training burst of 0's for 1.5s

int txsendTCF(void)
{
 int    error;
 string s;
 int    time;
 int    count;
 int    i;

 writetodebug("send TCF");

      /*       error=modemechoedcommand("AT+FTS=8",RETURNOK,1,100);
 if(!error) */ error=txsendmodulation(1);

 if(!error)
 {
  if(txbitrate==CX14400) count=2700;
  else
  if(txbitrate==CX12200) count=2288;
  else
  if(txbitrate==CX9600) count=1800;
  else
  if(txbitrate==CX7200) count=1350;
  else
  if(txbitrate==CX4800) count=900;
  else
  if(txbitrate==CX2400) count=450;

  for(i=0;i<count;i++) sputc(0);

  sputc(0x10); // DLE
  sputc(0x3);  // ETX

  if(!error) sreadtext(s,3000,100);   // should get 'OK'
 }

 return(error);
}



int txsendMPS(int first)   // Multipage signal
{
 string s;

 writetodebug("send MPS");
 s=chars(0xFF);         // address
 s=s+chars(0x3 |0x10);
 s=s+chars(0x4E|0x1);   // MPS=X111 0010
 return(txsendframe(s,first));
}


int txsendEOP(int first)  //    End-of-procedures
{
 string s;

 writetodebug("send EOP");
 s=chars(0xFF);         // address
 s=s+chars(0x3 |0x10);
 s=s+chars(0x2E|0x1);   // EOP=X111 0100
 return(txsendframe(s,first));
}


int txsendMCF(int first)     //  Message confirmation
{
 string s;

 writetodebug("send MCF");
 s=chars(0xFF);         // address
 s=s+chars(0x3 |0x10);
 s=s+chars(0x8C|0x1);   // MCF=X011 0001
 return(txsendframe(s,first));
}


int txsendEOM(int first)     //  End of message
{
 string s;

 writetodebug("send EOM");
 s=chars(0xFF);         // address
 s=s+chars(0x3 |0x10);
 s=s+chars(0x8E|0x1);   // EOM=X111 0001
 return(txsendframe(s,first));
}




int txsendDCN(int first)     //   Disconnect
{
 string s;

 writetodebug("send DCN"); 
 s=chars(0xFF);         // address
 s=s+chars(0x3 |0x10);
 s=s+chars(0xFA|0x1);   // DCN=X101 1111
 return(txsendframe(s,first));
}



int faxtx_retrain(void) // Retrain stuff
{
 int    error;
 int    retrain;
 int    retraincount;
 string s;
 int    c;
              
 writetodebug("Retrain");

 error=0;
 retrain=1;
 retraincount=0;

 modemechoedcommand(modemcarrierwaitshort,RETURNOK,1,100);

 while(1)
 {
  if(!error) error=txsendTSI(1);
  if(!error) error=txsendDCS(0);
  if(!error) error=txsendTCF();
  if(error)  break;

  while(!error)
  {
   error=modemechoedcommand("AT+FRH=3",RETURNCONNOC,1,600);

   if(!error)
   {
    if(!sgetframe(s,500))
    {
     c=schar(s,2);

     switch(c)
     {

      case 0x20: /* NSF */
                txgetNSF(s);
                break;

      case 0x40: /* CSI */
                if(!txgetCSI(s)) error=1;
                break;

      case 0x44: /* FTT=X010 0010 failure to train */
      case 0x45: /* retrain and bad DCS */
                retraincount++;
                if(((retraincount & 0x1)==0) && txbitrate) txbitrate--;
                break;

      case 0x84: /* CFR=X010 0001 */
      case 0x85:
                txgetCFR(s);
                retrain=0;
                break;
     }

     c=schar(s,1);
     sreadtext(s,3000,100);   // should get 'OK'
     if(c & 0x10) break;      /* last frame */
    }
    else
    {
     error=2;
    }
   }
  }

  if(error==2)
  {
   txbitrate--;
   error=0;
  }
  else
  if(error==1)
  {
   break;
  }
  else
  if(error==0)
  {
   if(!retrain) break;
  }

  if(txbitrate<faxmodemminbitrate)
  {
   logreason("Too slow");
   error=1;
   break;
  }
 }

 modemechoedcommand(modemcarrierwaitlong,RETURNOK,1,100);
 return(error);
}




int faxtx_PhaseB(int restart)
{
 int    error;
 string s;
 int    c;
 int    reply;

 writetodebug("PhaseB");

 reply=3;
 error=0;

 while(!error)
 {
  if(restart)
  {
   error=modemechoedcommand("AT+FRH=3",RETURNCONNOC,1,600);
  }

  if(error==0)
  {
   if(!sgetframe(s,500))
   {
    c=schar(s,2);

    switch(c)
    {
     case 0x20: /* NSF */
              txgetNSF(s);
              break;

     case 0x40: /* CSI */
              if(!txgetCSI(s)) error=1;
              break;

     case 0x80: /* DIS */
              txgetDIS(s);
              break;
    }
    modemgetOK();

    c=schar(s,1);
    if(c & 0x10) break;  /* last frame */
   }
  }
  else
  if(error==2)
  {
   error=0;
  }
  else break;


  if(!(reply--))
  {
   error=1;
   break;
  }
  restart=1;
 }

 if(!error) error=faxtx_retrain();

 return(error);
}




// data transmission phase
// return 0 if OK

int faxtx_PhaseC(int lastdoc)
{
 int    error;
 int    page;
 string s;
 int    c;
 int    confirmed;
 int    confirmcount;

 error=0;

 for(page=0;page<txdocpages && !error;page++)
 {
  error=txsendmodulation(0);
  if(error) break;

  modem_setflow(XONXOFF);
  faxtxpage(page,txpagewidth,txresln);
                           // at this point can be a buffer of stuff   *

  sreadtext(s,6000,100);   // should get 'OK' 

  modem_setflow(RTSCTS);   // so we wait till now to switch off X flow *

  confirmed=0;
  confirmcount=0;


  modemechoedcommand(modemcarrierwaitshort,RETURNOK,1,100);

  while(1)
  {

   if(page<(txdocpages-1)) error=txsendMPS(1);
   else
   {
    if(lastdoc) error=txsendEOP(1);
    else        error=txsendEOM(1);
   }
   if(error) break;


   error=modemechoedcommand("AT+FRH=3",RETURNCONNOC,1,600);
   if(error==0)
   {
    if(!sgetframe(s,500))
    {
     c=schar(s,2);

     switch(c)
     {
       case 0x8C: /* MCF */
                 txgetMCF(s);
                 confirmed=1;
                 break;

       case 0xCC: /* RTP */
                 txgetRTP(s);
                 confirmed=1;
                 error=faxtx_retrain();
                 break;

       case 0x4C: /* RTN */
                 txgetRTN(s);
                 confirmed=1;
                 page--;
                 error=faxtx_retrain();
                 break;

         default:
                 confirmcount++;
                 break;

     }
     modemgetOK();
    }
    else
    {
     confirmcount++;
    } 
   }
   else
   {
    confirmcount++;
   }


   if(confirmed) break;

   if(error==2)
   {
    error=0;
   }
   else
   if(error==1)
   {
    break;
   }

   if(confirmcount>3) 
   {
    error=1;
    break;
   }
  }
  modemechoedcommand(modemcarrierwaitlong,RETURNOK,1,100);
 }
 return(error);
}



int faxtx_PhaseD(void)
{
 int    error;
 string s;
 int    c;
 int    confirmed;

 writetodebug("PhaseD");

 error=txsendDCN(1);

 // then ATH0 is done outside

 return(error);
}



// called by main program to transmit documents
// return 0 if transmission completed correctly

int faxmodem_txdocument(int txdocuments)
{
 int error;
 int doc;

 error=0;

 for(doc=0;doc<txdocuments && !error;doc++)
 {
  error=faxtx_PhaseB(doc>0);
  if(!error)
  {
   faxtxopendocument();
   if(!error) error=faxtx_PhaseC(doc>=(txdocuments-1));
   faxtxclosedocument(error);
  }
 }

 if(!error) error=faxtx_PhaseD();
 modemechoedcommand(modemhangup,RETURNOK,5,500);

 return(error);
}



// called by main program to set up parameters

void faxmodem_params(int res,int rate,int length,int width)
{
 faxmodemresolution=res;
 faxmodemmaxbitrate=rate;
 faxmodemlength=length;
 faxmodemwidth=width;
}


void faxmodem_params2(int data,int error,int binary,int rate)
{
 faxmodemdatacompression=data;
 faxmodemerrorcorrection=error;
 faxmodembinaryftp=binary;
 faxmodemminbitrate=rate;
}


// program the fax modem identifier

void faxmodem_id(string & id)
{
 faxmodemid=id;
}



// set number of page retries

int faxmodem_retries(int retries)
{
 faxmodemretries=retries;
 return(faxmodemretries);
}



/****************************************************************************/
// Fax receive logic
//
//

int rxgooddocument;      // return code from FHNG
int rxresln;             // resolution 0==low 1==high
int rxbitrate;           // bit rate code
int rxV17;               // V17 in use
int rxpagewidth;         // page width code
int rxpagelength;        // page height code
int rxdatacompression;   // data compression
int rxerrorcorrection;   // error correction
int rxbinaryftp;         // binary ftp
int rxscantime;



int rxsendmodulationsub(string & command)
{
 int error;
 int i;

 for(i=0;i<3;i++)
 {
  error=modemechoedcommand(command,RETURNCONNECTRM,1,500);
  if(error!=3) break;
 }

 return(error);
}



int rxsendmodulation(int tcf)
{
 int error;

 if(rxV17)
 {
  if(tcf)
  {
   if(rxbitrate==CX14400)
    error=rxsendmodulationsub("AT+FRM=145");
   else
   if(rxbitrate==CX12200)
    error=rxsendmodulationsub("AT+FRM=121");
   else
   if(rxbitrate==CX9600)
    error=rxsendmodulationsub("AT+FRM=97");
   else
   if(rxbitrate==CX7200)
    error=rxsendmodulationsub("AT+FRM=73");
  }
  else
  {
   if(rxbitrate==CX14400)
    error=rxsendmodulationsub("AT+FRM=146");
   else
   if(rxbitrate==CX12200)
    error=rxsendmodulationsub("AT+FRM=122");
   else
   if(rxbitrate==CX9600)
    error=rxsendmodulationsub("AT+FRM=98");
   else
   if(rxbitrate==CX7200)
    error=rxsendmodulationsub("AT+FRM=74");
  }
 }
 else
 if(rxbitrate==CX9600)
  error=rxsendmodulationsub("AT+FRM=96");
 else
 if(rxbitrate==CX7200)
  error=rxsendmodulationsub("AT+FRM=72");
 else
 if(rxbitrate==CX4800)
  error=rxsendmodulationsub("AT+FRM=48");
 else
 if(rxbitrate==CX2400)
  error=rxsendmodulationsub("AT+FRM=24");

 return(error);
}



//  Called subscriber identification

int rxsendCSI(int first)
{
 string s;
 int    idlen;
 int    i;

 writetodebug("send CSI");

 s=chars(0xFF);
 s=s+chars(0x0 |0x3); // not last frame
 s=s+chars(0x40|0x0); // CSI=0000 0010

 idlen=slen(faxmodemid);
 for(i=idlen-1;i>=0;i--) s=s+chars(schar(faxmodemid,i));
 i=idlen;
 while(i++<20) s=s+chars(32);

 return(txsendframe(s,first));
}



// Digital Identification Signal 

int rxsendDIS(int first)
{
 string s;
 int    bitrate;
 int    scantime;

 writetodebug("send DIS");

 if(faxmodemmaxbitrate>=CX12200) bitrate=0x8;
 else                            bitrate=0x0;

 if(faxmodemmaxbitrate>=CX9600) bitrate|=0x1;
 else
 if(faxmodemmaxbitrate==CX7200) bitrate|=0x3;
 else
 if(faxmodemmaxbitrate==CX4800) bitrate|=0x2;
 else                           bitrate|=0x0;

 scantime=codetoscan(0);

 s=chars(0xFF);         
 s=s+chars(0x10|0x3);  // last frame
 s=s+chars(0x80|0x0);  // DIS=0000 0001
 s=s+chars(0);
 s=s+chars((0x2)+(faxmodemresolution<<6)+(bitrate<<2)+
                                        (faxmodemdatacompression<<7));

 s=s+chars((faxmodemwidth+(faxmodemlength<<2)+(scantime<<4)));

 return(txsendframe(s,first));
}


int rxsendMCF(int first)
{
 int error;
 string s;

 writetodebug("send MCF");
 s=chars(0xFF);
 s=s+chars(0x3 |0x10);
 s=s+chars(0x8C|0x0);   // MCF=X011 0001
 return(txsendframe(s,first));
}


int rxsendCFR(int first)
{
 int error;
 string s;

 writetodebug("send CFR");
 s=chars(0xFF);
 s=s+chars(0x3 |0x10);  // last frame
 s=s+chars(0x84|0x0);   // CFR=X010 0001
 return(txsendframe(s,first));
}


int rxsendFTT(int first)
{
 int error;
 string s;

 writetodebug("send FTT");
 s=chars(0xFF);
 s=s+chars(0x3 |0x10);  // last frame
 s=s+chars(0x44|0x0);   // FTT=X010 0010 failure to train
 return(txsendframe(s,first));
}


int rxgetTSI(string & s)
{
 writetodebug("get TSI");
 return(sgetsid(s,RX,1));
}


int rxgetDCS(string & s)
{
 int bitrate;
 int c4;
 int c5;

 writetodebug("get DCS");

 c4=schar(s,4);
 c5=schar(s,5);

 rxresln=(c4 & 0x40)>>6;
 bitrate=(c4 & 0x3C)>>2;
 
 rxV17=0;

 if(bitrate==0x8) {rxbitrate=CX14400;rxV17=1;}
 else
 if(bitrate==0xA) {rxbitrate=CX12200;rxV17=1;}
 else
 if(bitrate==0x9) {rxbitrate=CX9600;rxV17=1;}
 else
 if(bitrate==0xB) {rxbitrate=CX7200;rxV17=1;}
 else
 if(bitrate==0x1) rxbitrate=CX9600;
 else
 if(bitrate==0x3) rxbitrate=CX7200;
 else
 if(bitrate==0x2) rxbitrate=CX4800;
 else             rxbitrate=CX2400;

 rxpagewidth=c5 & 0x3;
 rxpagelength=(c5 & 0xC)>>2;

 rxscantime=(c5 & 0x70)>>4;
 rxscantime=scantocode(rxscantime);

 rxdatacompression=(c4 & 0x80)>>7;   // data compression

 rxerrorcorrection=0;   // error correction
 rxbinaryftp=0;         // binary ftp

 faxrxscan(rxbitrate,rxscantime);
 faxrxparams(rxpagelength,rxdatacompression,rxerrorcorrection,rxbinaryftp);
 return(0);
}


/* return 1==retrain 0==OK */

int rxgetTCF(void)
{
 int    c;
 int    escape;
 int    good;
 int    bad;
 string s;
 int    error;

 writetodebug("get TCF");

 error=rxsendmodulation(1);
 if(error) return(error);

 escape=0;
 good=0;
 bad=0;

 while(1)
 {
  c=sgetc(500);  // was 100
  if(c<0) break;

  if(c==0x10) escape=1;
  else
  if(escape)
  {
   escape=0;
   if(c==0x3) break;     /* end of stuff */
   else
   if(c!=0x10) c=-1;
  }

  if(c) bad++;
  else  good++;
 }

 sreadtext(s,3000,100);   // should get NO CARRIER

 return(!(good>4*bad));
}



int faxrx_phaseB(void)
{
 int    error;
 string s;
 int    c;
 int    retrain;
 int    attempts;
 int    seenDCS;
 int    repeat;

 writetodebug("PhaseB");

 rxresln=faxmodemresolution;
 rxbitrate=faxmodemmaxbitrate;
 rxpagewidth=faxmodemwidth;
 rxpagelength=faxmodemlength;
 rxdatacompression=faxmodemdatacompression;
 rxscantime=0;


 retrain=1;
 attempts=12;
 seenDCS=0;
 repeat=0;


// modemechoedcommand(modemcarrierwaitshort,RETURNOK,1,500);

 while(retrain)
 {
  if(!seenDCS)
  {
              error=rxsendCSI(repeat);
   if(!error) error=rxsendDIS(0);
   if(error)  break;

  }
  repeat=1;


  while(!error)
  {
   error=modemechoedcommand("AT+FRH=3",RETURNCONNOC,1,500);
   if(error==0)
   {
    if(!sgetframe(s,500))
    {
     c=schar(s,2);

     switch(c)
     {
      case (0x42|0x1): /* TSI X100 0010 */
                      if(!rxgetTSI(s)) error=1;
                      break;

      case       0x82:
      case (0x82|0x1): /* DCS X100 0001 */
                      error=rxgetDCS(s);
                      seenDCS=1;
                      break;
     }

     c=schar(s,1);
     sreadtext(s,3000,100);   // should get 'OK'
     if(c & 0x10) break;      /* last frame */
    }
   }
   else
   {
    if(error==2) error=0;
    break;
   }
  }

  if(error) break;

  if(seenDCS)
  {
   // now expect to get training burst
   modemechoedcommand("AT+FRS=1",RETURNOK,1,100);
   retrain=rxgetTCF();
   if(retrain)
   {
    if(attempts-- && !error) error=rxsendFTT(1);
    else                     error=1;             /* had too many attempts */
    if(error) break;
   }
  }
  else
  {
   if(!(attempts--))
   {
    error=1;
    break;
   }
  }
 }

// modemechoedcommand(modemcarrierwaitlong,RETURNOK,1,500);
 pause(80);
 if(!error) error=rxsendCFR(1);

 return(error);
}




int faxrx_phaseC(void)
{
 int    error;
 string s;
 int    c;
 int    newdocument;
 int    page;
 int    badpage;
 int    receive;

 writetodebug("PhaseC");

 newdocument=1;
 receive=1;

 while(receive)
 {
  error=rxsendmodulation(0);
  if(error) break;

  if(newdocument)
  {
   newdocument=0;
   page=0;
   faxrxopendocument();
  }

  badpage=faxrxpage(page,rxpagewidth,rxresln);
  if(!error) sreadtext(s,3000,100);   // should get NO CARRIER

  while(1)
  {
//   modemechoedcommand("AT+FRS=1",RETURNOK,1,100);
   error=modemechoedcommand("AT+FRH=3",RETURNCONNECT,1,3500);
   if(error) break;

   error=sgetframe(s,500);
   if(!error)
   {
    c=schar(s,2);
    c|=0x1;

    switch(c)
    {
     case (0x4E|0x1): /* MPS X111 0010 */
                     page++;
                     break;

     case (0x2E|0x1): /* EOP X111 0100 */
                     if(!newdocument) faxrxclosedocument(0);
                     newdocument=1;
                     receive=0;
                     break;

     case (0x8E|0x1): /* EOM X111 0001 */
                     if(!newdocument) faxrxclosedocument(0);
                     newdocument=1;
                     break;
    }

    c=schar(s,1);
    sreadtext(s,3000,100);   // should get 'OK'
    if(c & 0x10) break;  /* last frame */
   }
  }

  // send MCF or RTP or RTN */

  if(!error) error=rxsendMCF(1);
  if(error)  break;
 }

 if(!newdocument) faxrxclosedocument(0);
 return(error);
}




// enter having just got a EOP

int faxrx_phaseD(void)
{
 int    error;
 string s;
 int    c;
 int    disco;

 writetodebug("PhaseD");

 disco=0;

 while(!disco)
 {
  error=modemechoedcommand("AT+FRH=3",RETURNCONNECT,1,3500);
  if(error) break;

  error=sgetframe(s,500);

  c=schar(s,2);

  switch(c)
  {
   case (0xFA|0x1): /* DCN X101 1111 */
                   disco=1;
                   break;
                   
   default:
           rxsendMCF(1);
           break;          

  }

  c=schar(s,1);
  if(!error) sreadtext(s,3000,100);   // should get 'OK'
  if(c & 0x10) break;  /* last frame */
 }

 return(error);         
}


int faxrx_phaseE(void)
{
 writetodebug("PhaseE");
 return(modemechoedcommand(modemhangup,RETURNOK,5,500));
}



// return 0   OK
//        !=0 Error

int faxmodem_rxdocument(void)
{
 int error;

            error=faxrx_phaseB();
 if(!error) error=faxrx_phaseC();
 if(!error) error=faxrx_phaseD();
                  faxrx_phaseE();

 return(error);
}


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

// return 0 if OK, 1 on fail

int faxsetup(void)
{
 settxbor(1);
 setrxbor(1);
 return(0);
}



int    voicecompression;
int    voicebps;
int    voicesps;
int    voicesilence;
int    voicemaxmessage;
int    voicedevice;
int    voiceansweringcall;
string voiceanswerbuffer;





int xvoicemodem_device(int dev)
{
 int    xdev;
 string command;
 int    i;

 if(dev==MODEMLS)            xdev=2;
 else
 if(dev==PHONE)              xdev=1;
 else
 if(dev==MIC)                xdev=3;
 else
 if((dev==LINE) || (dev==0)) xdev=0;
 else                        xdev=1;
    

 for(i=0;i<5;i++)
 {
  command="AT#VLS="+itos(xdev);
  modemechoedcommand(command,0,5,500);
  sreadtext(command,600,100);    // expect VCON
  if(!xdev || command=="VCON") break;
 }

 return(0);
}





void voicesetup(void)
{
 string command;

 if(voicesilence) modemechoedcommand("AT#VSD=1",RETURNOK,5,500);
 else             modemechoedcommand("AT#VSD=0",RETURNOK,5,500);

 command="AT#VSP="+itos(voicesilence*10);
 modemechoedcommand(command,RETURNOK,5,500);

 xvoicemodem_device(voicedevice);

/*
 if(voicecompression)
 {
  modemechoedcommand("AT#VSM=128,8000",RETURNOK,5,500);
 }
 else
 {
  modemechoedcommand("AT#VSM=129,8000",RETURNOK,5,500);
  command="AT#VBS="+itos(voicebps);
  modemechoedcommand(command,RETURNOK,5,500);
 }
*/
}






// return 0 on fail or connection type

int modemsetup(void)
{
 int     code;
 string  s;

 code=0;

                modemechoedcommand(modeminitstring1,1,1,100);
           code=modemechoedcommand(modeminitstring2,1,1,100);
 if(!code) code=modemechoedcommand(modeminitstring3,1,1,100);

 if(modemmode==DATA) code=modemechoedcommand("AT+FCLASS=0",RETURNOK,5,100);
 else
 if(modemmode==FAX || modemmode==(FAX|DATA))
                     code=modemechoedcommand("AT+FCLASS=1",RETURNOK,5,100);
 else                code=modemechoedcommand("AT#CLS=8",RETURNOK,5,100);


 if(!code)
 {
  if(modemmode & VOICE) voicesetup();
 }


 if(!code)
 {
  if(modemmode & FAX) code=faxsetup();
 }


 if(!code)
 {
  if(modemmode!=DATA && modemmode!=FAX && (!(modemmode & VOICE)))
                     modemechoedcommand("AT+FAA=1",RETURNOK,1,100);
  else
  if(modemmode==FAX) modemechoedcommand("AT+FAA=0",RETURNOK,1,100);
 }

 return(code);
}



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


int datamodem_getfiles(void)
{
 int code;

 code=ftpgetfiles();

 modem_disconnect();

 return(code);
}


int datamodem_sendfiles(int files)
{
 int code;

 code=ftpsendfiles();

 modem_disconnect();

 return(code);
}



/*****************************************************************************/
// voice modem functions



void voicemodem_params(int compression,int bps,int sps)
{
 voicecompression=compression;
 voicebps=bps;
 voicesps=sps;
}


void voicemodem_params2(int silence,int maxmessage)
{
 voicemaxmessage=maxmessage;
 voicesilence=silence;
}



int voicemodem_device(int dev)
{
 voicedevice=dev;
 if(!dev) xvoicemodem_device(dev);
 return(0);
}


int voicemodempollflag;


void voicemodem_poll(void)
{
 int code;

 if(voicemodempollflag==0)
 {
  voicemodempollflag=1;

  code=getdlet(0);

  if(code!=0x10 && code!=0x3 && code!=-1 && voiceansweringcall)
  {
   voiceanswerbuffer+=chars(code);
   if(code=='c' || code=='a' || code=='d') replaystop();
  }

  voicemodempollflag=0;
 }
}



int voicemodem_play(int start)
{
 string command;
 int    code;

 if(start)
 {
  if(!voiceansweringcall)
  {
   maskdle(1);
   modemechoedcommand("AT#CLS=8",RETURNOK,5,500);
   voicesetup();
  }

  modemechoedcommand("AT#VTX",0,5,500);
  sreadtext(command,600,100);     // expect connect 

  if(voiceansweringcall)
  {
   addpollfunction("voicemodem_poll");
  }
 }
 else
 {
  if(voiceansweringcall)
  {
   rempollfunction("voicemodem_poll");
  }

  sputc(0x10);
  sputc(0x3);

  while(1)
  {
   code=sreadtext(command,1200,100);
   if(!code) break;

   if(command/"VCON">=0) break;

   code=getdle();
   if(code!=0x10 && code!=0x3 && code!=-1 && voiceansweringcall) 
                                           voiceanswerbuffer+=chars(code);
  }

  if(!voiceansweringcall)
  {
   maskdle(0);
   modemsetup();
  }
 }

 return(0);
}


int voicemodem_record(int start)
{
 string command;
 int    code;

 if(start)
 {
  if(!voiceansweringcall)
  {
   modemechoedcommand("AT#CLS=8",RETURNOK,5,500);
   voicesetup();
  }

  modemechoedcommand("AT#VRX",0,5,500);

  sreadtext(command,200,100);   // expect CONNECT
  sgetc(50);                    // swallow trailing LF

 }
 else
 {
  sputc(0x10);
  sputc(0x3);

  while(1)
  {
   code=sreadtext(command,150,100);
   if(!code) break;
   if(command/"VCON">=0) break;
  }

  sreadtext(command,200,100);           // expect VCON

  if(!voiceansweringcall) modemsetup();
 }

 return(0);
}



int gofaxmode(void)
{
 int code;

 modemechoedcommand("AT#CLS=1",RETURNOK,5,100);
 code=modemechoedcommand(modemanswer?"ATA":"ATD",RETURNCONNECTIMM,1,6000);

 if(code==DATA)
 {
  if(!modemechoedcommand("ATO",RETURNCONNECTIMM,1,6000)) code=0;
 }

 if(code==FAX)   return(faxmodem_rxdocument());
 else
 if(code==DATA)  return(datamodem_getfiles());

 return(0);
}



/* default is to do a simple answering machine */

int voicemodem_answer(void)
{
 string command;
 int    dle;
 int    n;
 int    len;
 int    code;

 voiceansweringcall=0;
 maskdle(1);

 if(dle=='c' || dle=='a') return(gofaxmode());

 voiceansweringcall=1;

 while(1)
 {
  code=getdlet(0);

  if(code!=0x10 && code!=0x3 && code!=-1)
  {
   if(code=='h') break;

   writetodebug(chars(code));


  }
 }
 
 maskdle(1);

 voiceansweringcall=0;

 modem_disconnect();

 return(0);
}




/*****************************************************************************/
// High level general modem control functions


// called when we want modem driver to sort out the incoming call
// return 0 on success

int modem_receive(int mode)
{
 int code;

 if(mode & FAX)   code=faxmodem_rxdocument();
 else
 if(mode & VOICE) code=voicemodem_answer();
 else
 if(mode & DATA)  code=datamodem_getfiles();
 else             code=0;

 modemechoedcommand("AT#CLS=0",RETURNOK,5,500);

 modemsetup();

 return(code);
}


int modem_send(int mode,int files)
{
 if(mode & FAX)  return(faxmodem_txdocument(files));
 else
 if(mode & DATA) return(datamodem_sendfiles(files));

 return(1);
}


// return 0 on fail, or connection type

int modem_dial(string number)
{
 string s;
 int    code;

 code=modemsetup();
 if(!code)
 {
  if(modemanswer)   s="ATDR";
  else              s="ATD";

  if(modemtonedial) s=s+"T";
  else              s=s+"P";

  code=modemechoedcommand(s+number,RETURNCONNECTIMM,1,6000);
 }
 return(code);
}


int modem_connect(void)
{
 string s;
 int    code;
 int    dle;

 code=modemsetup();
 if(!code)
 {
  maskdle(1);
  code=modemechoedcommand(modemanswer?"ATA":"ATD",RETURNCONNECTIMM,1,6000);

  if(code==VOICE)
  {
   while(1)
   {
    dle=getdlet(100);
    if(dle=='c') 
    {
     code=FAX;
     modemechoedcommand("AT#CLS=1",RETURNOK,5,100);
     modemechoedcommand(modemanswer?"ATA":"ATD",RETURNCONNECTIMM,1,6000);
     break;
    }
    else
    if(dle=='a')
    {
     code=DATA;
     modemechoedcommand("AT#CLS=0",RETURNOK,5,100);
     modemechoedcommand(modemanswer?"ATA":"ATD",RETURNCONNECTIMM,1,6000);
     break;
    }
    else
    if(dle=='d')
    {
     code=VOICE;
     break;
    }
    else
    if(dle==-1)  break;
   }
  }

  maskdle(0);

  if(code==DATA)
  {
   if(!modemechoedcommand("ATO",RETURNCONNECTIMM,1,6000)) code=0;
  }
 }
 return(code);
}



// enter auto answer mode 
// return 0 on success

int modem_autoanswer(int rings,int mode)
{
 int    code;
 string s;

 code=1;
 modemrings=rings;
 modemanswermode=mode;
 setpoll(0);

 if(rings)
 {
  code=modemsetup();;
 }
 else
 {
                 modemechoedcommand("AT+FCLASS=0",RETURNOK,1,100);
                 modemechoedcommand(modeminitstring1,1,1,100);
            code=modemechoedcommand(modeminitstring2,1,1,100);
  if(!code) code=modemechoedcommand(modeminitstring3,1,1,100);
 }

 setpoll(1);
 return(code);
}



// repeatedly called
// examine serial port for sign of incoming fax
// if returns call type then modem_connect is called
// return 0, carry on polling

int modem_poll(void)
{
 string s;
 int    rings;

// if(bbc_inkey(-2)) return(VOICE);

 if(sreadtext(s,50,100))
 {
  if(s=="RING")
  {
   rings=modemrings-1;

   while(1)
   {
    if(rings<=0) return(FAX|DATA|VOICE);

    if(!sreadtext(s,1000,100)) break;
    else
    if(s=="RING") rings--;
   }
  }
 }
 return(0);
}


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

// cancel modem operation


void modem_interrupt(void)
{
 string s;
 sprints("|M"); 
 while(sreadtext(s,50,100));
}


int modem_voicedial(string & number)
{
 modemechoedcommand((modemtonedial?"ATDT":"ATDP")+number+";",RETURNOK,1,3000);
 modemechoedcommand(modemhangup,RETURNOK,5,500);
 return(0);
}


// called when ArcFax is about to terminate
// good time to hang up line etc.

void modem_terminate(void)
{
 devcon(Device_Select,modemport,0,0);
}


int modem_initiate(int port)
{
 int    code;
 string s;

 modemport=port;
 code=0;

 if(!devcon(Device_Claim,modemport,0,0)) return(2);

 devcon(Device_Select,modemport,1,0);

 devcon(Device_Channel,modemport,0,0);

 modem_setrxrate(modemdtespeed);
 modem_settxrate(modemdtespeed);

 modem_setbits(8);
 modem_setparity(0);
 modem_setstop(1);

 modem_setflow(RTSCTS);

 setpoll(0);

 // Some modems may like a short pause at this point

 pause(150);

                modemechoedcommand(modeminitstring1,1,1,100);
           code=modemechoedcommand(modeminitstring2,1,1,100);
 if(!code) code=modemechoedcommand(modeminitstring3,1,1,100);

 setpoll(1);

 if(code) devcon(Device_Select,modemport,0,0);

 return(code);
}


void main(void)
{

}
