#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <dos.h>
#include <exec/types.h>
#include <exec/io.h>
#include <intuition/intuition.h>
#include <devices/serial.h>
#include <devices/timer.h>

#include <dlg/user.h>
#include <dlg/portconfig.h>
#include <dlg/dlg.h>
#include <dlg/log.h>
#include <dlg/misc.h>

#include <devices/tpt.h>

#include <link/io.h>
#include <link/lang.h>

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dlg.h>

#include <pragmas/dlg.h>

#include <private/Version.h>
#define  ObjRev "1"
const UBYTE version[]="\0$VER: Chat " BUILDVER "." ObjRev " ©" COPYRIGHT " by Digerati Dreams "__AMIGADATE__;

void  main(int, char **);

BOOL  OpenConsole(struct IOStdReq *,struct IOStdReq *,struct Window *);
void  SetRead(void);
void  SetConRead(void);
void  HandleChar(UBYTE);
void  WriteString(char *);
void  ConWriteString(char *);
void  WriteChar(char);
void  ConPutChar(char);
void  SerPutChar(char);
BOOL  OpenSer(char *);
BOOL  OpenTimer(void);
void  CloseSer(void);
void  CloseTimer(void);
void  CloseConStuff(void);
BOOL  DisplayNotes(void);
int   CarrierDetect(void);
BOOL  UseCharSet(UBYTE,char *);
void  Usage(void);

void _CXBRK(void);
void  CleanUp(char *);


#define READSER_SIG (1 << ss.read->IOSer.io_Message.mn_ReplyPort->mp_SigBit)
#define CONSOLE_SIG (1 << consoleReadPort->mp_SigBit)
#define TIMER_SIG   (1 << Timer_Port->mp_SigBit)
#define RWIN_SIG    (1 << rw->UserPort->mp_SigBit)

BOOL paged      = TRUE;
BOOL StopTimer  = FALSE;
BOOL fflag      = FALSE;
BOOL SingleFeed = FALSE;
BOOL RamFile    = FALSE;
BOOL CONOPEN    = FALSE;
BOOL SEROPEN    = FALSE;
BOOL runflag    = FALSE;

struct USER_DATA UserDat;
struct Ram_File  RStruct;

struct IntuiMessage *im = NULL;
struct Window       *w  = NULL;
struct Window       *rw = NULL;
struct NewWindow     nw;
SHORT                x;
SHORT                y;

struct IOStdReq    *consoleWriteMsg  = NULL;
struct MsgPort     *consoleWritePort = NULL;
struct IOStdReq    *consoleReadMsg   = NULL;
struct MsgPort     *consoleReadPort  = NULL;

struct timerequest *Timer            = NULL;
struct MsgPort     *Timer_Port       = NULL;
struct MsgPort     *ReadBak          = NULL;
struct MsgPort     *WriteBak         = NULL;
struct TPTSerStuff  ss;

char   titlebar[255];

UBYTE in_c, cin_c,linelen;
int signals;
int reqwidth;
char name[20],buf[80],title[80];
char inarray[256], outarray[256];
char activity[80],flippybindle=2, *logfile=NULL;
int  response=-1,numchars;
FILE *lfp=NULL;

struct EasyStruct es = {sizeof(struct EasyStruct),
                        0,
                       "Chat Request",
                       "%s is requesting chat",
                       "Answer|Ignore",
                       };

char                  ext[4]        = "";
BPTR                  sout          = NULL;
struct Library       *DLGBase       = NULL;
char                **SA;
struct IntuitionBase *IntuitionBase = NULL;

void main(int argc,char *argv[])

{long retval;
 struct Port     MyPort;
 struct Displays MyDisplay;
 char            filename[128];

 sout = Output();
 if (!(DLGBase = OpenLibrary(DLGNAME, DLGVERSION)))  exit(5);

 GetDevName(ext);
 if (!(SA = getlang(ext)))  CleanUp("Can't read language file");

 while(--argc > 0)
      {char  *s;

       s = *++argv;
       if (*s++=='-')
          {while(*s)
                {switch(*s)
                       {case 'p':
                        case 'P': if (!--argc)  break;
                                  strncpy(ext,*++argv,3);
                                  ext[3] = 0;
                                  paged       = FALSE;
                                  break;

                        case 'l':
                        case 'L': if (!--argc)  break;
                                  logfile = *++argv;
                                  break;

                        case 's':
                        case 'S': SingleFeed = 1;
                                  break;

                        case 't':
                        case 'T': StopTimer = 1;
                                  break;

                         default: Usage();
                       }
                 s++;
                }
          }
      }

 if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",NULL)))
       CleanUp(SA[2598]);

 if (!Strnicmp(ext, "TL", 2))            CleanUp(SA[2599]);
 if (!ReadUser(&RStruct, &UserDat, ext))  CleanUp(SA[2600]);
 RamFile = TRUE;

 UseCharSet(UserDat.charset, ext);

 strcpy(name, RStruct.Name);
 linelen =  UserDat.Screen_Width;
 linelen = (linelen>76) ? 76 : linelen;
 strcpy(activity, RStruct.Action);

 if (paged)
    {WriteLog(PAGED_SYSOP, RStruct.Name, ext, "");
     UserDat.Sysop_Pages++;
     WriteUser(RStruct.Name, &UserDat);

     DisplayBeep(NULL);
     if (!OpenTimer())  CleanUp(SA[2601]);

     Timer->tr_time.tv_secs  = 10;
     Timer->tr_time.tv_micro =  0;
     SendIO((struct IORequest *)Timer);

     rw = (struct Window *)BuildEasyRequest(NULL,&es,NULL,name);
     if (rw==(struct Window *)TRUE)
         response=1;
       else
         if (rw==(struct Window *)FALSE)
             response=0;
           else
            {signals = Wait(TIMER_SIG|RWIN_SIG);
             if (signals&RWIN_SIG)
                {response = SysReqHandler(rw,NULL,FALSE);
                 FreeSysRequest(rw);

                 if (!(signals&TIMER_SIG))
                    {if (!response)
                          Wait(TIMER_SIG);
                        else
                         {AbortIO((struct IORequest *)Timer);
                          WaitIO((struct IORequest *)Timer);
                         }
                    }
                }
              else
                {FreeSysRequest(rw);
                 response = -1;
                }
            }

     if (!response || response == -1)
        {if (response==-1)
            {TGetTitle(titlebar, ext);
             strcat(titlebar,   "  PAGED");
             TTitle(titlebar,    ext);
            }

         CleanUp(NULL);
        }
    }
  else
    strcpy(RStruct.Action, SA[2602]);
 WriteRam(&RStruct, ext);

 retval = TFreeze(ext);
 if (retval != NOERR)
    {if (retval == GENERALERR)  CleanUp(SA[2603]);
     if (retval == ACTIVERR)    CleanUp(SA[2604]);
     if (retval == FROZENERR)   CleanUp(SA[2605]);
     CleanUp(SA[2606]);
    }
 fflag = TRUE;

 if (!OpenSer(ext))  CleanUp(SA[2607]);
 SEROPEN = TRUE;

 /* if user drops carrier while requestor is up */
 if (!CarrierDetect())
    {Delay(50);
     CleanUp(SA[2608]);
    }

 SetRead();

 if (!(consoleWritePort = CreateMsgPort()))               CleanUp(SA[2609]);
 consoleWritePort->mp_Node.ln_Name = "chat.con.write";
 AddPort(consoleWritePort);
 if (!(consoleWriteMsg = CreateStdIO(consoleWritePort)))  CleanUp(SA[2610]);

 if (!(consoleReadPort = CreateMsgPort()))                CleanUp(SA[2611]);
 consoleReadPort->mp_Node.ln_Name = "chat.con.read";
 AddPort(consoleReadPort);
 if (!(consoleReadMsg = CreateStdIO(consoleReadPort)))    CleanUp(SA[2612]);

 ASPrintf(NULL, filename,"DlgConfig:Port/%s.port", ext);
 GetFirstStruct(filename,(char *)&MyPort,sizeof(MyPort));

 ASPrintf(NULL, filename,"DlgConfig:Port/%s",MyPort.DisplayFile);
 if (GetFirstStruct(filename,(char *)&MyDisplay,sizeof(MyDisplay))==-1)
    {nw.LeftEdge =   0;
     nw.TopEdge  =  11;
     nw.Width    = (linelen+4)*8;
     nw.Height   =  111;
    }
  else
    {nw.LeftEdge =  MyDisplay.Window.x;
     nw.TopEdge  =  MyDisplay.Window.y;
     nw.Width    = (linelen+4)*8;
     nw.Height   =  MyDisplay.Window.height;
    }

 ASPrintf(NULL, title,"%s / %s",RStruct.Name,UserDat.Alias);

 nw.DetailPen   = (UBYTE)-1;
 nw.BlockPen    = (UBYTE)-1;
 nw.IDCMPFlags  =  NULL;
 nw.Flags       =  WINDOWDEPTH|WINDOWSIZING|WINDOWDRAG|SMART_REFRESH|ACTIVATE;
 nw.FirstGadget =  NULL;
 nw.CheckMark   =  NULL;
 nw.Title       = (UBYTE *)title;
 nw.Screen      =  NULL;
 nw.BitMap      =  NULL;
 nw.MinWidth    =  85;
 nw.MinHeight   =  20;
 nw.MaxWidth    =  800;
 nw.MaxHeight   =  600;
 nw.Type        = PUBLICSCREEN;
// nw.Type        = (USHORT)WBENCHSCREEN;
 if (!(w = OpenWindow(&nw)))                          CleanUp(SA[2613]);

 if (!OpenConsole(consoleWriteMsg,consoleReadMsg,w))  CleanUp(SA[2614]);

 CONOPEN                     = TRUE;
 consoleWriteMsg->io_Command = CMD_WRITE;

 SetConRead();

 if (logfile && !(lfp=fopen(logfile,"a")))  CleanUp(SA[2615]);

 runflag  = TRUE;
 numchars = 0;

 WriteString(SA[2616]);
 WriteString(SA[2617]);
 WriteString(SA[2618]);

 if (StopTimer)  SuspendTime(&RStruct, ext);
 DisplayNotes();

 while(runflag)
      {signals = Wait(READSER_SIG|CONSOLE_SIG);
       if ((signals&READSER_SIG)&&CheckIO((struct IORequest *)ss.read))
          {if (flippybindle!=0)
              {flippybindle = 0;
               if (UserDat.Ansi_Flag&ANSI_COLOR)
                   WriteString(SA[2619]);
                 else
                   ConWriteString(SA[2619]);
              }

           HandleChar(in_c);
           SetRead();
          }

       if ((signals&CONSOLE_SIG)&&CheckIO((struct IORequest *)consoleReadMsg))
          {if (flippybindle!=1)
              {flippybindle = 1;
               if (UserDat.Ansi_Flag&ANSI_COLOR)
                   WriteString(SA[2620]);
                 else
                   ConWriteString(SA[2620]);
              }

           if (cin_c==14) DisplayNotes();
           HandleChar(cin_c);
           SetConRead();
          }
      }

 WriteString(SA[2621]);
 CleanUp(NULL);
}


BOOL OpenConsole(struct IOStdReq *writerequest,struct IOStdReq *readrequest,struct Window *window)

{writerequest->io_Data   = (APTR)window;
 writerequest->io_Length =  sizeof(struct Window);

 if (!OpenDevice("console.device",NULL,(struct IORequest *)writerequest,NULL))
    {readrequest->io_Device = writerequest->io_Device;
     readrequest->io_Unit   = writerequest->io_Unit;
     return(TRUE);
    }

 return(FALSE);
}


void SetRead(void)

{if (!CarrierDetect())
    {ConWriteString(SA[2622]);
     Delay(50);
     CleanUp(SA[2623]);
    }

 /* set up for a read */
 ss.read->IOSer.io_Length  =     1;
 ss.read->IOSer.io_Command =  CMD_READ;
 ss.read->IOSer.io_Data    = (APTR)&in_c;

 SendIO((struct IORequest *)ss.read);
}


void SetConRead(void)

{consoleReadMsg->io_Length  =     1;
 consoleReadMsg->io_Command =  CMD_READ;
 consoleReadMsg->io_Data    = (APTR)&cin_c;

 SendIO((struct IORequest *)consoleReadMsg);
}


void HandleChar(UBYTE c)

{UBYTE count;

 c = inarray[c];

 switch(c)
       {case 26:  if (logfile && numchars)
                     {if (numchars)  fwrite(buf,numchars,1,lfp);
                      fputc(10,lfp);
                     }

                  if (StopTimer)  ResumeTime(&RStruct, ext);
                  runflag = FALSE;
                  return;

        case 13:  c = 10;
                  if (logfile)
                     {if (numchars) fwrite(buf,numchars,1,lfp);
                      fputc(10,lfp);
                     }

                  if (!SingleFeed)
                     {if (logfile) fputc(10,lfp);
                      WriteChar(c);
                     }

                  numchars = -1;
                  break;

        case 11:
        case 12:
        case 10:  return;

        case 8:
        case 127: if (numchars)
                     {WriteString("\010 \010");
                      numchars--;
                     }
                  return;
       }

 if (c < 10  ||  (c>13 && c<32) || (c>126 && c<159))
     return;

 if (c)
    {if (numchars>=0)  buf[numchars]=c;
     numchars++;

     if (numchars>linelen)
        {numchars = linelen;
         while((--numchars > -1)  &&  (buf[numchars] != 32));

         if (logfile)
            {if (numchars>0) fwrite(buf,numchars,1,lfp);
             fputc(10,lfp);
            }

         if (numchars==-1)
            {numchars = 1;
             WriteChar(10);
             WriteChar(c);
            }
          else
            {numchars = linelen - numchars;
             for(count=0;count<numchars;count++)
                 WriteString("\010 \010");

             movmem(buf+linelen-numchars+1,buf,numchars);
             buf[numchars] = 0;
             WriteChar(10);
             WriteString(buf);
            }
        }
      else
        WriteChar(c);
    }
}


void WriteString(char *buf)

{short len = strlen(buf);
 short i;
 char  c;

 for(i=0; i<len; i++)
    {c = buf[i];
     WriteChar(c);
    }
}


void ConWriteString(char *buf)

{short len = strlen(buf);
 short i;
 char  c;

 for(i=0; i<len; i++)
    {c = buf[i];
     if (c==13)  c=10;
     ConPutChar(c);
    }
}


void WriteChar(char c)

{c = outarray[c];

 if (c)
    {if (c==10) SerPutChar(13);
     SerPutChar(c);
     ConPutChar(c);
    }
}


void ConPutChar(char c)

{consoleWriteMsg->io_Data=(APTR)&c;
 consoleWriteMsg->io_Length=1;

 DoIO((struct IORequest *)consoleWriteMsg);
}


void SerPutChar(char c)

{if (c)
    {ss.write->IOSer.io_Length =  1;
     ss.write->IOSer.io_Data   = (APTR)&c;
     DoIO((struct IORequest *)ss.write);
    }
}


BOOL OpenSer(char *port)

{int retval;

 retval = TGetSer(&ss,port);
 if (retval!=NOERR)  return(FALSE);

 ReadBak  = ss.read->IOSer.io_Message.mn_ReplyPort;
 WriteBak = ss.write->IOSer.io_Message.mn_ReplyPort;

 ss.read->IOSer.io_Message.mn_ReplyPort  = CreateMsgPort();
 ss.write->IOSer.io_Message.mn_ReplyPort = CreateMsgPort();

 return(TRUE);
}


BOOL OpenTimer(void)

{if (Timer_Port=CreateMsgPort())
    {if ((Timer=(struct timerequest *)CreateExtIO(Timer_Port,sizeof(*Timer))))
        {if (!(OpenDevice (TIMERNAME,UNIT_VBLANK,(struct IORequest *)Timer,0)))
            {Timer->tr_node.io_Command = TR_ADDREQUEST;
             Timer->tr_node.io_Flags   = NULL;
             Timer->tr_node.io_Error   = NULL;

             return(TRUE);
            }
        }
    }

 Timer->tr_node.io_Device = NULL;
 return(FALSE);
}


void CloseSer(void)

{if (!CheckIO((struct IORequest *)ss.read)) AbortIO((struct IORequest *)ss.read);
      WaitIO((struct IORequest *)ss.read);

 DeleteMsgPort(ss.read->IOSer.io_Message.mn_ReplyPort);
 DeleteMsgPort(ss.write->IOSer.io_Message.mn_ReplyPort);

 ss.read->IOSer.io_Message.mn_ReplyPort=ReadBak;
 ss.write->IOSer.io_Message.mn_ReplyPort=WriteBak;
}


void CloseTimer(void)

{if (Timer)
    {if (Timer->tr_node.io_Device)  CloseDevice((struct IORequest *)Timer);
     DeleteExtIO((struct IORequest *)Timer);
    }

 if (Timer_Port)  DeleteMsgPort(Timer_Port);
}


void CloseConStuff(void)

{if (CONOPEN)
    {AbortIO((struct IORequest *)consoleReadMsg);
     WaitIO((struct IORequest *)consoleReadMsg);
     CloseDevice((struct IORequest *)consoleWriteMsg);
    }

 if (w)                CloseWindow(w);
 if (consoleReadMsg)   DeleteStdIO(consoleReadMsg);
 if (consoleReadPort)
    {RemPort(consoleReadPort);
     DeleteMsgPort(consoleReadPort);
    }
 if (consoleWriteMsg)  DeleteStdIO(consoleWriteMsg);
 if (consoleWritePort)
    {RemPort(consoleWritePort);
     DeleteMsgPort(consoleWritePort);
    }
}


BOOL DisplayNotes(void)

{char filename[80],buffer[256];
 int  fp,charsread;

 ASPrintf(NULL, filename,"DLGCONFIG:Notes/%s.Notes",RStruct.Name);
 UnderScore(filename);

 if ((fp = open(filename,O_RDONLY))==EOF) return(FALSE);
 ConWriteString(SA[2624]);

 while((charsread = read(fp,buffer, 256)) == 256)
        ConWriteString(buffer);

 ConWriteString(buffer);
 ConWriteString("\n\n");
 close(fp);

 return(TRUE);
}


int CarrierDetect(void)

{UWORD cd;
 UBYTE i;

 ss.write->IOSer.io_Command=SDCMD_QUERY;

 for(i=0; i<5; i++)
    {DoIO((struct IORequest *)ss.write);
     cd =   ss.write->io_Status;
     cd = !(cd & (UWORD)1<<(UWORD)5);
     if (cd) break;
    }

 ss.write->IOSer.io_Command=CMD_WRITE;
 return((int)cd);
}


BOOL UseCharSet(UBYTE number,char *ext)

{struct CharSet Set;
 char   filename[56];
 int    counter,fp;

 if (number)
    {Set.number = number;
     if (GetStruct("DLGConfig:CharSets/CharSets.bbs",(char *)&Set,sizeof(Set),1)!=-1)
        {ASPrintf(NULL, filename,"DLGConfig:CharSets/%s.set",Set.name);

         if ((fp=open(filename,O_RDONLY))!=EOF)
            {read(fp,inarray,256);
             read(fp,outarray,256);
             close(fp);
             return(TRUE);
            }
        }
    }

 for(counter=0; counter<128; counter++)
     inarray[counter] = counter;
 for(counter=128; counter<256; counter++)
     inarray[counter] = counter;
 for(counter=0; counter<128; counter++)
     outarray[counter]=counter;
 for(counter=128; counter<256; counter++)
     outarray[counter]=counter;

 return(FALSE);
}


void Usage(void)

{AFPrintf(NULL, sout, SA[2625]);
 AFPrintf(NULL, sout, SA[2626]);
 AFPrintf(NULL, sout, SA[2627]);
 AFPrintf(NULL, sout, SA[2628]);
 AFPrintf(NULL, sout, SA[2629]);

 CleanUp(NULL);
}


void _CXBRK(void)

{CleanUp(SA[2630]);
}


void CleanUp(char *s)

{if (lfp)  fclose(lfp);
 CloseConStuff();

 if (SEROPEN)  CloseSer();
 CloseTimer();

 if (!paged)
    {RStruct.module = 0;
     RStruct.area   = 0;
    }

 if (Strnicmp(ext, "TL", 2))
    {if (RamFile)
        {strcpy(RStruct.Action, activity);
         WriteRam(&RStruct,     ext);
        }
    }

 if (fflag)          TCont(ext);
 if (IntuitionBase)  CloseLibrary((struct Library *)IntuitionBase);
 if (DLGBase)        CloseLibrary(DLGBase);

 if (s && *s)
    {Write(sout, "\n Error: ", 9);
     Write(sout,  s,    strlen(s));
     Write(sout, "\n\n",       2);
    }

 exit(s?5:0);
}
