//->$.Fax.!Arcfax.Driver.Class2
// Copyright (c) David Pilling 1994
//




string modemhangup="ATH0";

int    modemdtespeed=57600;

int    modemusedocumentresolution=0;



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




// The first section of the modem driver, contains general low level
// modem functions


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="";
 return(FAX|DATA);
}


// is the modem on line ?

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





int modemechoedcommand(string & s,int ok)
{
 int    n;
 string t;
//         return(0);

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

   if(s==t)
   {
    if(ok)
    {
     pause(5);
     while(1)
     {
      if(!sreadtext(t,100,100)) break;
      if(t=="OK") return(0);
     }
    }
    else return(0);
    break;
   }
  }
 }
 return(1);
}



// put modem off line

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

 sprints("+++");
 pause(200);
 modemechoedcommand("ATH0",1);
}

/*****************************************************************************/
// 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;
int faxcurrentresolution;


string faxmodemid;




// return parameter or -1 if error 

int fxgetparam(string & s)
{
 int n;
 int c;
 int i;

 i=0;

 while(1)
 {
  c=schar(s,i++);

  if(c>='0' && c<='9') break;
  else
  if(c==0) return(-1);
 }

 n=c-'0';

 while(1)
 {
  c=schar(s,i);

  if(c>='0' && c<='9') n=n*10+c-'0';
  else                 break;

  i++;
 }

 s=s<<i;

 return(n);
}


void faxdisconnect(void)
{
 modemechoedcommand("AT+FK",1);
}


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

// Parameters for session

int txptr;
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;


// Transmit page transfer status

void faxtxFPTS(string & s)
{
 txptr=fxgetparam(s);
}

int txgooddocument;

void faxtxFHNG(string & s)
{
 int n;
 string temp;

 n=s/"+FHNG";
 if(n) s=s<<n;
 temp=s;

 txgooddocument=fxgetparam(s);
 if(txgooddocument) logreason(temp);
}


// current session capabilities

void faxtxFDCS(string & s)
{
 txresln=fxgetparam(s);
 txbitrate=fxgetparam(s);
 txpagewidth=fxgetparam(s);
 txpagelength=fxgetparam(s);
 txdatacompression=fxgetparam(s);
 txerrorcorrection=fxgetparam(s);
 txbinaryftp=fxgetparam(s);
 txscantime=fxgetparam(s);
 faxtxscan(txbitrate,txscantime);
 faxtxparams(txpagelength,txdatacompression,txerrorcorrection,txbinaryftp);
}


// remote station capabilities

void faxtxFDIS(string & s)
{
 faxtxFDCS(s);
}


// called station ID
// return 1 if OK, else 0

int faxtxFCSI(string & s)
{
 int code;
 s=s<<6;
 faxtxid(s);
 code=txidok(s);

 if(!code) 
 {
  faxdisconnect();
  logreason("Bad ID");
 }

 return(code);
}


// get an xon character

int faxtxgetxon(void)
{
 int c;

 while(1)
 {
  c=sgetc(100);
  if(c==-1) return(1);
  else
  if(c==('Q'-64)) return(0);
 }
}



int faxtxspeedcheck(void)
{
 if(txbitrate<faxmodemminbitrate)
 {
  logreason("Too slow");
  return(1);
 }
 else
 {
  return(0);
 }
}


// 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;
}


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

int faxmodem_txdocument(int txdocuments)
{
 int    doc;
 int    docok;
 int    page;
 int    hangup;
 int    pgerror;
 int    retrain;
 int    speedfail;
 string result;
 string s;

 docok=1;
 hangup=0;
 retrain=0;
 pgerror=0;
 speedfail=0;
 txgooddocument=1;

 for(doc=0;doc<txdocuments && !hangup;doc++)
 {
  faxtxopendocument();

  for(page=0;page<txdocpages && !hangup;page++)
  {
   retrain=0;
   if(modemechoedcommand("AT+FDT",0)) break;

   while(1)
   {
    if(!sreadtext(s,6000,100)) break;

    if(s/"+FDCS"==0)
    {
     faxtxFDCS(s);
     speedfail=faxtxspeedcheck();
    }
    else
    if(s/"+FDIS"==0)
    {
     faxtxFDIS(s);
     speedfail=faxtxspeedcheck();
    }
    else
    if(s/"CONNECT"==0) break;
    else
    if(s/"+FHNG">=0)
    {
     hangup=1;
     faxtxFHNG(s);
     if(!txgooddocument) txgooddocument=1; //can get FHNG 0 without sending fax
     break;
    }
   }

   if(s/"CONNECT"!=0) break;
   
   faxtxgetxon();

   if(speedfail)
   {
    sputc(0x10);sputc(0x3);
    sreadtext(s,6000,100);
    faxdisconnect();
    pgerror=1;
   }
   else
   {
    modem_setflow(XONXOFF);

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

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

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


    if(page<(txdocpages-1))  modemechoedcommand("AT+FET=0",0);
    else                    
    {
     if(doc<(txdocuments-1)) modemechoedcommand("AT+FET=1",0);
     else                    modemechoedcommand("AT+FET=2",0);
    }
   }


   while(1)
   {
    if(!sreadtext(s,3000,100)) break;

    if(s/"+FPTS"==0)
    {
     faxtxFPTS(s);
    }
    else
    if(s/"+FHNG">=0)
    {
     faxtxFHNG(s);
     hangup=1;
     break;
    }
    else
    if(s=="OK") break;
   }


   switch(txptr)
   {
    case 2:           /* bad page */
    case 3:
            retrain=1;
            break;
    default:
            break;
   }

  }

  faxtxclosedocument(!(page>=txdocpages)||(hangup && txgooddocument)||pgerror);
  if(page<txdocpages) break;
 }

 if(retrain && !hangup)
 {
  modemechoedcommand("AT+FDT",0);

  while(1)
  {
   if(!sreadtext(s,6000,100)) break;

   if(s/"CONNECT"==0)
   {
    faxtxgetxon();
    sputc(0x10);sputc(0x3);
   }
   else
   if(s/"+FHNG">=0)
   {
    hangup=1;
    faxtxFHNG(s);
    break;
   }
  }
 }

 return(txgooddocument);
}


int faxmodemsetparams(void)
{
 string s;
 s="AT+FDCC=";
 s=s+itos(faxcurrentresolution);
 s=s+",";
 s=s+itos(faxmodemmaxbitrate);
 s=s+",";
 s=s+itos(faxmodemwidth);
 s=s+",";
 s=s+itos(faxmodemlength);
 s=s+",";
 s=s+itos(faxmodemdatacompression);
 s=s+",0,0,0";
 return(modemechoedcommand(s,1));
}

// 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;
}


int faxmodemsetid(void)
{
 string s;
 s="AT+FLID="+"\""+faxmodemid+"\"";
 return(modemechoedcommand(s,1));
}


// program the fax modem identifier

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


// return 0 OK, 1 Error

int faxtxconnect(void)
{
 string s;

 while(1)
 {
  if(sreadtext(s,3000,200))
  {
   if(s/"+FCON"==0) continue;
   else
   if(s/"+FCSI"==0) 
   {
    if(!faxtxFCSI(s)) return(1);
   }
   else
   if(s/"+FDCS"==0) faxtxFDCS(s);
   else
   if(s/"+FDIS"==0) faxtxFDIS(s);
   else
   if(s/"+FNSF"==0) continue;
   else
   if(s!="CED") break;
  }
 }

 return(s!="OK");
}


// 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 rxpagewidth;         // page width code
int rxpagelength;        // page height code
int rxdatacompression;   // data compression
int rxerrorcorrection;   // error correction
int rxbinaryftp;         // binary ftp
int rxscantime;          // min scan time


int rxppm;               // rx post page message code
int rxptr;               // rx page transfer status


void faxrxFPTS(string & s)
{
 rxptr=fxgetparam(s);
 rxpagelength=fxgetparam(s);
}


void faxrxFDCS(string & s)
{
 rxresln=fxgetparam(s);
 rxbitrate=fxgetparam(s);
 rxpagewidth=fxgetparam(s);
 rxpagelength=fxgetparam(s);
 rxdatacompression=fxgetparam(s);
 rxerrorcorrection=fxgetparam(s);
 rxbinaryftp=fxgetparam(s);
 rxscantime=fxgetparam(s);
 faxrxscan(rxbitrate,rxscantime);
 faxrxparams(rxpagelength,rxdatacompression,rxerrorcorrection,rxbinaryftp);
}


void faxrxFET(string & s)
{
 rxppm=fxgetparam(s);
}


void faxrxFHNG(string & s)
{
 int n;
 string temp;

 n=s/"+FHNG";
 if(n) s=s<<n;
 temp=s;

 rxgooddocument=fxgetparam(s);
 if(rxgooddocument) logreason(temp);
}


int faxrxFTSI(string & s)
{
 int code;
 s=s<<6;
 faxrxid(s);
 code=rxidok(s);

 if(!code) 
 {
  faxdisconnect();
  logreason("Bad ID");
 }

 return(code);
}


// called if get a +FCON after connect
// return 0 if good connect

int faxrxconnect(void)
{
 string s;

 while(1)
 {
  if(!sreadtext(s,3000,100)) break;

  if(s/"+FCON"==0) continue;
  else
  if(s/"+FDCS"==0) faxrxFDCS(s);
  else
  if(s/"+FTSI"==0) 
  {
   if(!faxrxFTSI(s)) return(1);   /* failed on id code */
  }
  else
  if(s/"+FHNG">=0)
  {
   faxrxFHNG(s);
   break;
  }
  else break;
 }
 return(s!="OK");
}


// return 0 OK, 1 Error

int faxmodem_rxdocument(void)
{
 string s;
 string result;
 int    docok;
 int    newdocument;
 int    badpage; 
 int    page;

 newdocument=1;
 rxgooddocument=1;

 while(1)
 {
  modemechoedcommand("AT+FDR",0);

  while(1)
  {
   if(!sreadtext(s,3000,100)) break;

   if(s/"+FHNG">=0) 
   {
    faxrxFHNG(s);
    break;
   }
   else
   if(s/"+FDCS"==0) faxrxFDCS(s);
   else
   if(s/"CONNECT"==0) break;
  }

  if(s/"CONNECT"!=0) break;

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

  sprints("|R");
  badpage=faxrxpage(page,rxpagewidth,rxresln);

  while(1)
  {
   if(!sreadtext(s,3000,100)) break;

   if(s/"+FPTS"==0) faxrxFPTS(s);
   else
   if(s/"+FET"==0)  faxrxFET(s);
   else
   if(s/"+FHNG">=0)
   {
    faxrxFHNG(s);
    break;
   }
   else
   if(s=="OK") break;
  }

  if(s!="OK") break;

  switch(rxppm)
  {
   case 0:
          page++;
          break;

   case 1:
          if(!newdocument) faxrxclosedocument(0);
          newdocument=1;
          break;

   case 2:
          if(!newdocument) faxrxclosedocument(0);
          newdocument=1;
          break;
  }
 }
 if(!newdocument) faxrxclosedocument(1);
 return(rxgooddocument);
}


/****************************************************************************/
// return 0 if OK, 1 on fail

int faxconnect(void)
{
 if(modemanswer) return(faxrxconnect());
 else            return(faxtxconnect());
}

// return 0 if OK, 1 on fail

int faxsetup(void)
{
 int code;

 code=1;

 settxbor(1);
 setrxbor(0);

 if(!modemechoedcommand("AT+FBOR=0",1))
 {
  if(!modemechoedcommand("AT+FCR=1",1))
  {
   if(!faxmodemsetparams())
   {
    code=faxmodemsetid();
   }
  }
 }

 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);
}


/***************************************************************************/
// High level general modem control functions
// return 0 on fail or connection type

int modemsetup(void)
{
 int code;

 code=0;

 if(modemmode==DATA) code=modemechoedcommand("AT+FCLASS=0",1);
 else
 if(modemmode==FAX || modemmode==(FAX|DATA))
                     code=modemechoedcommand("AT+FCLASS=2",1);

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

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

 return(code);
}


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


int modem_receive(int mode)
{
 if(mode & FAX)   return(faxmodem_rxdocument());
 else
 if(mode & DATA)  return(datamodem_getfiles());

 return(0);
}


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

 return(1);
}



int modemconnectstring(void)
{
 string s;

 while(sreadtext(s,6000,100))
 {
  if(s/"+FCON"==0)    return(faxconnect()?0:FAX);
  else
  if(s/"FAX"==0)      return(faxconnect()?0:FAX);
  else
  if(s/"CONNECT"==0)  return(DATA);
  else
  if(s=="CED")        continue;
  else
  if(s/"PROTOCOL"==0) continue;
  else
  if(s/"RINGING"==0)  continue;
  else
  if(s/"CARRIER"==0)  continue;
  else
  if(s/"DATA"==0)     continue; 
  else
  if(slen(s)>1) 
  {
   logreason(s);
   break;
  }
 }

 return(0);
}



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

 if(modemusedocumentresolution==0) faxcurrentresolution=faxmodemresolution;
 else                              faxcurrentresolution=txdocresolution;

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

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

  if(!modemechoedcommand(s+number,0))
  {
   code=modemconnectstring();

  }
 }
 return(code);
}


// return 0 on fail, or connection type

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

 if(modemanswer || modemusedocumentresolution==0) faxcurrentresolution=faxmodemresolution;
 else                                             faxcurrentresolution=txdocresolution;


 code=modemsetup();
 if(!code)
 {
  if(!modemechoedcommand(modemanswer?"ATA":"ATD",0))
  {
   code=modemconnectstring();
  }
 }
 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)
 {
  faxcurrentresolution=faxmodemresolution;
  code=modemsetup();
 }
 else
 {
  code=modemechoedcommand("AT+FCLASS=0",1);
 }

 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(sreadtext(s,50,100))
 {
  if(s=="RING")
  {
   rings=modemrings-1;

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

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

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

// cancel modem operation

int voicedialactive;

void modem_interrupt(void)
{
 string s;
 sprints("|M");
 while(sreadtext(s,50,100));
 if(voicedialactive) modemechoedcommand("ATS7=30",0);
}


int modem_voicedial(string & number)
{
 string s;

 modemechoedcommand((modemtonedial?"ATDT":"ATDP")+number+";",1);
 sreadtext(s,3000,100);
 modemechoedcommand("ATH0",1);

 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;

 modemport=port;

 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(50);

 code=modemechoedcommand("ATQ0V1&C1E1&D2S0=0S10=50",1);

 setpoll(1);

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

 return(code);
}


void main(void)
{


}

