//->$.Fax.!Arcfax.Driver.Class8
//
// Modified from ZyXel modem driver Copyright (c) David Pilling 1994
// Rockwell chipset voice modem driver version Copyright (c) Ian Grimstead 1995
//
// Version 0.17
//
//
// History:
//
// Main adjustments: voice commands now work with Rockwell chipset
//
// 0.01-0.04 Correct settings for voice operation with local record/play;
//           problems with replaying message via answered line.
//
// 0.05      Global variable "answeringcall" set up; this will enable the driver to decide
//           whether or not to switch the local phone line in or out during record or play.
//           Should then replay the message correctly down the telephone line.
//
// 0.06      Local record/play working properly with "answeringcall" variable
//
//
// 0.07      Will now look for fax DTMF tones during message playback, and
//           switch to fax receive if it detects such tones...
//
// 0.08      Looks for DTMF tones during modemechoedcommand, and sets a new
//           variable <currentcalltype> - which should hold the type of the
//           current call, unless DTMF tones indicate otherwise...
//
// 0.09      Should recover properly now, when a fax is detected during the
//           speech playback mechanism.
//
// 0.10      More fixes to the "fax detection"...
//
// 0.11      More fixes - slight bug in modemechoedcommand (wrt. clipping 1 char length
//           strings)
//
// 0.12      Debugging lines added everywhere to check operation of script...
//
// 0.13      Now changed auto fax detect s.t. script waits 2 seconds for a fax machine;
//           if no fax sounds are detected, carries on with normal answerphone.
//
// 0.14      Moved "roger bleep" to be immediately before a voice recording is started;
//           i.e. placed the bleep in voicemodem_record
//
// 0.15      Now set up "log on/off" global variable, to prevent logging when not required
//
// 0.16      Fixed part of the script that caused "OK" to be sent to the modem;
//           the script was sending "command" to the modem, and reading the
//           response back into "command". If the response was not "VCON", then
//           "command" was sent again, which had now been written with "OK" !
//           (problem was in xvoicemodem_device)
//
// 0.17      Altered response from "AT#VLS=x" case in xvoicemodem_device;
//           only accepted "OK" response with a LINE selection; should also
//           accept "OK" with a "0" selection, which is now the case
//           (VCON for all other cases).
//

int logtofile;


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

// Used to differentiate between record/play for a local source or an actual phone call
// - helps when setting up modem
int answeringcall;
int currentcalltype; // Holds the type of the current call.


// Debug version of "writetolog" - doesn't write to log unless "logtofile"==1

void debugwritetolog(string message)
{
  if (logtofile == 1) writetolog(message);
}


// count or purge buffer

int modem_countpurge(int rxtx,int code)
{
 int t;
 t=devcon(Device_CountPurge,modemport,rxtx,code);
 debugwritetolog("modem_countpurge(rxtx="+itos(rxtx)+",code="+itos(code)+") returns("+itos(t)+")\n");
}


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

void modem_setflow(int flow)
{
 debugwritetolog("modem_setflow(flow="+itos(flow)+")\n");
 devcon(Device_Flow,modemport,flow,1);
 modemflow=flow;
}


// set rx rate

void modem_setrxrate(int rate)
{
 debugwritetolog("modem_setrxrate(rate="+itos(rate)+")\n");
 devcon(Device_RxRate,modemport,rate,1);
 modemrxrate=devcon(Device_RxRate,modemport,0,0);
}


// set tx rate

void modem_settxrate(int rate)
{
 debugwritetolog("modem_settxrate(rate="+itos(rate)+")\n");
 devcon(Device_TxRate,modemport,rate,1);
 modemtxrate=devcon(Device_TxRate,modemport,0,0);
}


// set number of data bits

void modem_setbits(int bits)
{
 debugwritetolog("modem_setbits(bits="+itos(bits)+")\n");
 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)
{
 debugwritetolog("modem_setparity(parity="+itos(parity)+")\n");
 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)
{
 debugwritetolog("modem_setstop(stop="+itos(stop)+")\n");
 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)
{
 debugwritetolog("modem_tonedial(tonedial="+itos(tonedial)+") returns("+itos(modemtonedial)+")\n");
 modemtonedial=tonedial;
 return(modemtonedial);
}


void modem_setanswer(int answer)
{
 debugwritetolog("modem_setanswer(answer="+itos(answer)+")\n");
 modemanswer=answer;
}


void modem_setmode(int mode)
{
 debugwritetolog("modem_setmode(mode="+itos(mode)+")\n");
 modemmode=mode;
}

   
int modem_cap(string & s)
{
 // was s="compression,ADPCM,0,0,CELP,1,1;"
 // no CELP compression anymore, so just the ADPCM.
 debugwritetolog("modem_cap(s="+s+") returns("+itos(VOICE|FAX|DATA)+")\n");
 s="compression,ADPCM,0,0;";
 return(VOICE|FAX|DATA);
}



// is the modem on line ?

int modem_online(void)
{
 int t;
 t=devcon(Device_Status,modemport,0,-1) & SERIAL_DCD;
 debugwritetolog("modem_online() returns("+itos(t)+")\n");
}



// Checks for any incoming DTMF sounds remaining in the input buffer
void checkForDTMF(void)
{
 int char;

 char = sgetc(1);

 while (char != -1)
 {
  // Whilst there is data in the input buffer, read it.
  if (char == 0x10) // == 16 in decimal; ctrl-P
  {
   // We have a DTMF code.

   char = sgetc(1);  // Type of DTMF code
   while (char == -1) char = sgetc(10); // Ensure we read the type of DTMF

   switch (char)
   {
    case 'c':
     currentcalltype = FAX;
debugwritetolog("text read from modem == Fax calling tone\n");
     break;

    case 'a':
     currentcalltype = DATA;
debugwritetolog("text read from modem == Data calling tone\n");
     break;

    default:
debugwritetolog("text read from modem == Unknown calling tone\n");
     break;
   }
  }

  char = sgetc(1);                                       
 }

}


// Checks a given string for containing any DTMF codes.
void checkStringForDTMF(string t)
{
 int loop;
 string u;

 for (loop=0; loop<slen(t)-1; loop++)
  if (schar(t,loop) == 0x10)  // == 16 in decimal; ctrl-p
  {
   // We have a odd code returned ! (DTMF)

   // Check for end of string...
   if (loop == slen(t)-1)
   {
    sreads(u,20,2); // If so, then append the next character !
    if (schar(u,slen(u)-1) == 13) u = mids(u, 0, slen(u)-1);
    t += u;
   }

debugwritetolog("DTMF codes ("+itos(slen(t))+") detected: [10] ["+itos(schar(t,loop+1))+"]\n");

   switch (schar(t,loop+1))
   {
    case 'c':
     currentcalltype = FAX;
debugwritetolog("text read from modem == Fax calling tone\n");
     break;

    case 'a':
     currentcalltype = DATA;
debugwritetolog("text read from modem == Data calling tone\n");
     break;

    default:
debugwritetolog("text read from modem == Unknown calling tone\n");
     break;
   }
  }
 // End of for loop
}



int modemechoedcommand(string & s,int ok)
{
 int    n, loop;
 string t, u;

 // First thing to do; read input buffer for any stray characters - possible DTMF
 checkForDTMF();

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


   // Chop off ending character *if* its a return code (#13 = 0x0d)
   if (schar(t,slen(t)-1) == 0x0d)
   {
    if (slen(t) > 1) t = mids(t, 0, slen(t)-1);
    else t = "";
   }


debugwritetolog("Command sent:["+s+"]\n");
debugwritetolog("Command echoed:["+t+"]\n");

   checkStringForDTMF(t);
               

   if(s==t)
   {
    if(ok)
    {
     while(1)
     {
      if(!sreadtext(t,200,100)) break;

      checkStringForDTMF(t);

debugwritetolog("text read from modem:[" + t + "]\n");

      if(t=="OK") return(0);
     }
    }
    else return(0);
    break;
   }
  }
 }
 return(1);
}





// put modem off line

void modem_disconnect(void)
{
 debugwritetolog("modem_disconnect()\n");

 devicedtr(1,modemport);
 pause(200);
 devicedtr(0,modemport);

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

 answeringcall = 0; // We're not using BT anymore.
}



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




// 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;
 n=s/"+FHNG";
 if(n) s=s<<n;

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


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

 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);
     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/"+FHNG">=0)
   {
    hangup=1;
    faxtxFHNG(s);
    break;
   }
  }
 }
 return(txgooddocument);
}


int faxmodemsetparams(void)
{
 string s;
 s="AT+FDCC=";
 s=s+itos(faxmodemresolution);
 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,100))
  {
   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;
 n=s/"+FHNG";
 if(n) s=s<<n;

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



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

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

int voicecompression;
int voicebps;
int voicesps;
int voicesilence;
int voicemaxmessage;
int voicedevice;


void voicemodem_params(int compression,int bps,int sps)
{
 debugwritetolog("voicemodem_params(compression="+itos(compression)+",bps="+itos(bps)+",sps="+itos(sps)+")\n");
 voicecompression=compression;
 voicebps=bps;
 voicesps=sps;
}


void voicemodem_params2(int silence,int maxmessage)
{
 debugwritetolog("voicemodem_params2(silence="+itos(silence)+",maxmessage="+itos(maxmessage)+")\n");
 voicemaxmessage=maxmessage;
 voicesilence=silence;
}



// Different command numbers to use different sound i/o;
// also, Rockwell chipset uses AT#VLS, *not* AT+VLS

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

debugwritetolog("xvoicemodem_device: dev="+itos(dev)+"\n");

 if(dev==MODEMLS)            xdev=2;
 else
 if(dev==PHONE)              xdev=1; // was (dev==PHONE || dev==LINE)
 else
 if(dev==MIC)                xdev=3;
 else
 if((dev==LINE) || (dev==0)) xdev=0; // LINE - "default" action.
 // Other !!! :)  = "Comp" selection - we'll use it to route the sound only
 // through the phone (switching out the actual phone line)
 else                        xdev=1;
                
 command="AT#VLS="+itos(xdev);
  
 // If we're using the LINE, then we just get an "OK" response...
 if ((dev == LINE) || (dev == 0)) modemechoedcommand(command,1);  // Expect OK
 else
 {
  // Otherwise, we should get "VCON"

  for(i=0;i<5;i++)
  {
   modemechoedcommand(command,0);
   sreadtext(response,600,100);    // expect VCON
   if(response=="VCON")
   {
    debugwritetolog("xvoicemodem_device: received VCON\n");
    break;
   }
  }
 }

 return(0);
}



int voicemodem_device(int dev)
{
 debugwritetolog("voicemodem_device(dev="+itos(dev)+")\n");

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




void voicesetup(void)
{
 string command;
             
 debugwritetolog("voicesetup()\n");

 // Use #VSP, *not* +VSD
 command="AT#VSP="+itos(voicesilence*10);
 modemechoedcommand(command,1);

 // Select silence detection
 modemechoedcommand("AT#VSD=1",1);

 // Switch off silence detection
 modemechoedcommand("AT#VSS=0",1);

// xvoicemodem_device(voicedevice);

 // voicecompression is ignored - we must use ADPCM mode,
 // regardless.

 // use #VBS, *not* +VSM
 if (voicebps>1 && voicebps<4) modemechoedcommand("AT#VBS="+itos(voicebps),1);
 else writetolog("voicesetup: Must have 2,3,4 bits per sample. User has entered 1.\n");

 // voicesps is also ignored - we are fixed at 7200 samples/sec
}




int voicemodem_play(int start)
{
 string command;

 if(start)
 {
  if (!answeringcall)
  {
debugwritetolog("voicemodem_play: starting non-answercall\n");
   modemechoedcommand("AT#CLS=8",1);
   xvoicemodem_device(voicedevice);
  }
  else debugwritetolog("voicemodem_play: starting *answercall*\n");

  voicesetup();

  // #VTX *not* +VTX
  modemechoedcommand("AT#VTX",0);
  sreadtext(command,600,100);    // expect connect 

  modem_setflow(XONXOFF);

  // Stop polling - better reception ?
  debugwritetolog("Stop polling.\n");
  setpoll(1);
 }
 else
 {
  // Signal end of voice data stream.
  sputc(0x10);
  sputc(0x3);

  sreadtext(command,600,100);    // OK

  if (!answeringcall)
  {
debugwritetolog("voicemodem_play: finishing non-answercall\n");
   modemechoedcommand("AT#CLS=0",1);
   modemechoedcommand("ATH",1);
   modemechoedcommand("AT#CLS=8",1);

   // this happens automatically by a call to xvoicemodem_device with '0'
//  xvoicemodem_device(LINE);
  }
  else debugwritetolog("voicemodem_play: finishing *answercall*\n");


  modem_setflow(RTSCTS);

  // Resume polling.
  debugwritetolog("Resume polling.\n");
  setpoll(1);
 }

 return(0);
}


int voicemodem_record(int start)
{
 string command;

 if(start)
 {
  if (!answeringcall)
  {
debugwritetolog("voicemodem_record: starting non-answercall\n");
   modemechoedcommand("AT#CLS=8",1);
   xvoicemodem_device(voicedevice);
  }
  else debugwritetolog("voicemodem_record: starting *answercall*\n");

  voicesetup();

  // Make a beep, and expect "OK" as a response.
  modemechoedcommand("AT#VTS=[933,0,10]",1); // Beep ! (#VTS, not +VTS)


  // #VRX, *not* +VRX
  modemechoedcommand("AT#VRX",0);

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

  // stop polling - better reception ?
  debugwritetolog("Stop polling.\n");
  setpoll(0);

 }
 else
 {
  // Signal end of recording.
  sputc(0x10);
  sputc(0x3);

  if (!answeringcall)
  {
debugwritetolog("voicemodem_record: finishing non-answercall\n");
   modemechoedcommand("AT#CLS=0",1);
   modemechoedcommand("ATH",1);
   modemechoedcommand("AT#CLS=8",1);

   // this happens automatically by a call to xvoicemodem_device with '0'
//  xvoicemodem_device(LINE);
  }
  else debugwritetolog("voicemodem_record: finishing *answercall*\n");


  modem_countpurge(RX,0);

  // Resume polling.
  debugwritetolog("Resume polling.\n");
  setpoll(1);
 }

 return(0);
}


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

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;

debugwritetolog("modemsetup()\n");

 code=0;

 if(modemmode==DATA) code=modemechoedcommand("AT+FCLASS=0",1);
 else
 if(modemmode==FAX || modemmode==(FAX|DATA))
                     code=modemechoedcommand("AT+FCLASS=2",1);
 else
 {
   code=modemechoedcommand("AT#CLS=8",1);
   xvoicemodem_device(LINE);
 }

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

 if(!code)
 {
  if(modemmode!=DATA && modemmode!=FAX && modemmode!=VOICE)
                     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 modemconnectstring(void)
{
 string s;
 int code = -1;

 // We've either dialled out and got a connection, or we've answered the phone
 answeringcall = 1; // i.e. we're using an external connection via BT

 while(sreadtext(s,6000,100) && (code == -1))
 {
  if(s=="+FCON")
  {
   code = faxconnect()?0:FAX;
   break;
  }
  else
  if(s/"FAX"==0)
  {
   code = faxconnect()?0:FAX;
   break;
  }
  else
  if(s/"VCON">=0)
  {
   code = VOICE;
   break;
  }
  else
  if(s/"CONNECT"==0)
  {
   code = DATA;
   break;
  }
  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);
   code = 0;
   break;
  }
 }

debugwritetolog("Call type: ["+itos(code)+"]\n");

 currentcalltype = code;
 return(code);
}



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";

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

  }
 }
 return(code);
}


// return 0 on fail, or connection type

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

 code=modemsetup();
 if(!code)
 {
  if(!modemechoedcommand(modemanswer?"ATA":"ATD",0))
  {
   code=modemconnectstring();
  }
 }
 return(code);
}



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


/* Switches to fax mode from voice mode */
void switchToFaxMode(void)
{
  // We've got a fax !!!
debugwritetolog("Fax in voice !!!\n");

  // Set up fax mode, and call to fax receive.
  modemechoedcommand("AT#CLS=2",1);
  modemechoedcommand("ATA",0);
  currentcalltype = modemconnectstring(); // Pretend we've started over again...

  if(currentcalltype & FAX)   faxmodem_rxdocument();
  else
  if(currentcalltype & DATA)  datamodem_getfiles();

  // Quit voicemodem_answer
  answeringcall = 0;  // We've finished with BT...
  modemechoedcommand("ATH",1); // ...so hang up the phone.
}



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

int voicemodem_answer(void)
{
 int char;

debugwritetolog("Incoming voice message...waiting 5 seconds\n");

 // Wait for 2 seconds for any incoming sounds via fax machine et al.
 pause(250);

debugwritetolog("Done waiting.\n");

 // Once 2 seconds are up, check input buffer for information.
 checkForDTMF();

 // React if we picked up a FAX machine...
 if (currentcalltype == FAX)
 {
  switchToFaxMode();

  // all done, so exit.
  return(0);
 }

 // Otherwise, act as normal: play the message...
 replaymessage("!Message");

  checkForDTMF();

 // Check if any data or fax tones have been detected...
 // ...after the bleep, as this will delay slightly (until the modem has caught
 // with its backlog of commands) and will notify us if a DTMF tone is heard...

 if (currentcalltype == FAX)
 {
  switchToFaxMode();

  // all done, so exit.
  return(0);
 }
    

 // Record the message...
 recordmessage(voicemaxmessage);

 // We're all done, so hangup.
 modemechoedcommand("ATH",1);

 answeringcall = 0; // Finished with BT :)
 return(0);
}


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


int modem_receive(int mode)
{
 if(mode & FAX)   return(faxmodem_rxdocument());
 else
 if(mode & VOICE) return(voicemodem_answer());
 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);
}


// 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
 {
  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(bbc_inkey(-1)) return(VOICE);

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

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

    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)
{
 string s;

 if(!modemechoedcommand("AT#CLS=8",1))
 {
  if(!modemechoedcommand((modemtonedial?"ATDT":"ATDP")+number,0))
  {
   sreadtext(s,3000,100);
  }
 }
 modemechoedcommand("ATH0",0);
 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;

 answeringcall = 0; // We're not answering a call (yet!)

 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(38400);
 modem_settxrate(38400);

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

// xvoicemodem_device(0);  // select telephone as default device

 return(code);
}


void main(void)
{
 logtofile = 1;

}

