#include <stdlib.h>

#include <devices/tpt.h>

#include <dos/dosextens.h>

#include <proto/exec.h>

extern long                tptflags;

extern SHORT               num_times;
extern SHORT               in_len;

extern struct timerequest *Timer;
extern struct MsgPort     *Timer_Port;

extern UBYTE               inline;

extern   void  CheckPending(struct TPTSess *);
extern   BOOL  Die(struct TPTSess *);
extern   void  NiceAbort(struct IORequest *);
extern   BOOL  OpenStuff(UBYTE *, UBYTE, ULONG, UBYTE *);
extern   void  returnpkt(struct DosPacket *, struct Process *, ULONG, ULONG);
extern   void  returnpktplain(struct DosPacket *, struct Process *);
extern   void  TDebug(char *);
extern   void  WriteCon(UBYTE *, long);
extern   void  WriteSer(UBYTE *, long);

UBYTE cursoron = 1;

struct ReaderInfo  *FindReader(struct List *, struct MsgPort *);
struct Node        *FirstNode(struct List *);


/// DoPacket
void                DoPacket(struct TPTPort *ps)
{
   struct FileHandle  *fh;
   struct ReaderInfo  *ri;
   long                len;

   ps->sess->mypkt = (struct DosPacket *) ps->mymess->mn_Node.ln_Name;

   switch (ps->sess->mypkt->dp_Type)   /* find what action to perform */
   {

///   ACTION_FINDINPUT ACTION_FINDOUTPUT
      case ACTION_FINDINPUT:
      case ACTION_FINDOUTPUT:
      {
         if (!(ps->sess->aux_open))    /* first time here we open the devices */
         {
            if (!OpenStuff(ps->tserdev, ps->unit, ps->serflags, ps->tctl))
            {
// debug
               TDebug("DoPacket/ACTION_FIND#?: Error: device in use.");
// end debug
               returnpkt(ps->sess->mypkt, ps->myproc, DOS_FALSE, ERROR_OBJECT_IN_USE);
               ps->sess->run = 0;
               break;
            }
         }
         (ps->sess->aux_open)++;

// debug
//         if(ps->sess->mypkt->dp_Type == ACTION_FINDINPUT)
//            TDebug("DoPacket: Input Device Opened");
//         else
//            TDebug("DoPacket: Output Device Opened");
// end debug

         fh = BADDR(ps->sess->mypkt->dp_Arg1);
         fh->fh_Arg1 = DOS_TRUE;
         fh->fh_Port = (struct MsgPort *) DOS_TRUE;

         if (ps->sess->mypkt->dp_Type == ACTION_FINDINPUT)
         {
            struct MsgPort     *port;

            port = ps->sess->mypkt->dp_Port;
            ri = malloc(sizeof(struct ReaderInfo));

            ri->node.ln_Name = NULL;
            ri->node.ln_Type = NT_UNKNOWN;
            ri->task = port->mp_SigTask;
            ri->port = port;
            fh->fh_Arg1 = (long) ri;
            AddHead(&(ps->sess->readerlist), (struct Node *) ri);
         }

         returnpkt(ps->sess->mypkt, ps->myproc, DOS_TRUE, ps->sess->mypkt->dp_Res2);
         break;
      }
//-
///   ACTION_WRITE
      case ACTION_WRITE:
      {
         if ((tptflags & T_PAUSED) || (!(tptflags & T_RAW) && ((tptflags & T_LINEFREEZE) && inline)) || (tptflags & T_FROZEN))
         {
            AddTail(&(ps->sess->othermsg), (struct Node *) ps->mymess);
            break;
         }

         len = ps->sess->mypkt->dp_Arg3;

         if (cursoron && len > 5)
         {
            WriteCon("\233\060\040\160", 4);
            cursoron = 0;
         }

         if (!(tptflags & T_KILLED))
         {
            WriteSer((UBYTE *) ps->sess->mypkt->dp_Arg2, len);
         }

         ps->sess->mypkt->dp_Res1 = len;

         /* tell em we wrote them all */
         returnpktplain(ps->sess->mypkt, ps->myproc);
         break;
      }
//-
///   ACTION_READ
      case ACTION_READ:
      {
         ri = FindReader(&(ps->sess->readerlist), ps->sess->mypkt->dp_Port);

         if ((tptflags & T_RPEND)      || 
             (tptflags & T_WAIT_FOR)   ||
             (tptflags & T_PAUSED)     ||
             (!(tptflags & T_RAW) && ((tptflags & T_LINEFREEZE) && inline)) ||
             (tptflags & T_FROZEN))
         {
            AddTail(&(ps->sess->readmsg), (struct Node *) ps->mymess);
            break;
         }

         if (ri)
         {
            ps->sess->creader = ri;
         }
         
         ps->sess->creader->rdpkt = ps->sess->mypkt;
         tptflags |= T_RPEND;
         num_times = 0;

         if (!((tptflags & T_KILLED) && Die(ps->sess)))
         {
            CheckPending(ps->sess);
         }

         if (!cursoron && (tptflags & T_RPEND))
         {
            WriteCon("\233\040\160", 3);
            cursoron = 1;
         }
         break;
      }
//-
///   ACTION_WAIT_CHAR
      case ACTION_WAIT_CHAR:
      {
         ri = FindReader(&(ps->sess->readerlist), ps->sess->mypkt->dp_Port);

         if ((tptflags & T_RPEND) ||
             (tptflags & T_WAIT_FOR) ||
             (tptflags & T_PAUSED) ||
             (!(tptflags & T_RAW) && ((tptflags & T_LINEFREEZE) && inline)) ||
             (tptflags & T_FROZEN))
         {
            AddTail(&(ps->sess->readmsg), (struct Node *) ps->mymess);
            break;
         }

         if (ri)
         {
            ps->sess->creader = ri;
         }
         
         ps->sess->creader->rdpkt = ps->sess->mypkt;
         tptflags |= T_WAIT_FOR;

         if (tptflags & T_KILLED)
         {
            if (Die(ps->sess))
            {
               break;
            }
         }
         else if (!ps->sess->creader->rdpkt->dp_Arg1 && !in_len)
         {
            tptflags &= ~T_WAIT_FOR;
            ps->sess->creader->rdpkt->dp_Res1 = DOS_FALSE;
            returnpktplain(ps->sess->creader->rdpkt, ps->myproc);
            break;
         }

         /* just queue up to wait for data */
         if (tptflags & T_SER_TIMEOUT)
         {
            NiceAbort((struct IORequest *) Timer);
            Wait(1L << Timer_Port->mp_SigBit);
         }

         tptflags |= T_WAIT_FOR | T_SER_TIMEOUT;

         if (ps->sess->creader->rdpkt->dp_Arg1 > 1000000)
         {
            Timer->tr_time.tv_secs = ps->sess->creader->rdpkt->dp_Arg1 / 1000000;
            Timer->tr_time.tv_micro = ps->sess->creader->rdpkt->dp_Arg1 % 1000000;
         }
         else
         {
            Timer->tr_time.tv_secs = 0;
            Timer->tr_time.tv_micro = ps->sess->creader->rdpkt->dp_Arg1;
         }

         SendIO((struct IORequest *) Timer);
         CheckPending(ps->sess);

         if (!cursoron && (ps->sess->creader->rdpkt->dp_Arg1 > 0) && (tptflags & T_WAIT_FOR))
         {
            WriteCon("\233\040\160", 3);
            cursoron = 1;
         }
         break;
      }
//-
///   ACTION_SCREEN_MODE
      case ACTION_SCREEN_MODE:
      {
         if (ps->sess->mypkt->dp_Arg1)
         {
            tptflags |= T_RAW;
            tptflags &= ~T_ECHO;
         }
         else
         {
            tptflags &= ~T_RAW;
            tptflags |= T_ECHO;
         }

         returnpkt(ps->sess->mypkt, ps->myproc, DOS_TRUE, ps->sess->mypkt->dp_Res2);
         break;
      }
//-
///   ACTION_END
      case ACTION_END:
      {
         ri = (struct ReaderInfo *) ps->sess->mypkt->dp_Arg1;

         if ((((int) ri) != DOS_TRUE)  && 
             ((tptflags & T_PAUSED) ||
             (!(tptflags & T_RAW) && ((tptflags & T_LINEFREEZE) && inline)) ||
             (tptflags & T_FROZEN)))
         {
            AddTail(&(ps->sess->othermsg), (struct Node *) ps->mymess);
            break;
         }

// debug
//         TDebug("DoPacket: Device Closed");
// end debug

         if (--(ps->sess->aux_open) == 0)
         {
            (ps->sess->run) = 0;
         }
         else if (!cursoron && ((tptflags & T_RPEND) || (tptflags & T_WAIT_FOR)))
         {
            WriteCon("\233\040\160", 3);
            cursoron = 1;
         }

         if (((int) ri) != DOS_TRUE)
         {
            if (ri == ps->sess->creader)
            {
               ps->sess->creader = (struct ReaderInfo *) FirstNode(&(ps->sess->readerlist));
            }

            Remove((struct Node *) ri);
            free(ri);
         }

         returnpkt(ps->sess->mypkt, ps->myproc, DOS_TRUE, ps->sess->mypkt->dp_Res2);
         break;
      }
//-
      
      default:
      {
         returnpkt(ps->sess->mypkt, ps->myproc, DOS_FALSE, ERROR_ACTION_NOT_KNOWN);
         break;
      }
   }  // end switch
}  // end DoPacket()
//-
/// FindReader
struct ReaderInfo  *FindReader(struct List *lst, struct MsgPort *port)
{
   struct Node        *nd;
   struct ReaderInfo  *ri;

   nd = lst->lh_Head;

   while (nd->ln_Succ)
   {
      ri = (struct ReaderInfo *) nd;
      
      if (ri->port == port)
      {
         return (ri);
      }

      nd = nd->ln_Succ;
   }

   return (NULL);
}
//-
///FirstNode
struct Node        *FirstNode(struct List *list)
{
   struct Node        *nd;

   nd = list->lh_Head;

   if (nd->ln_Succ)
   {
      return (nd);
   }
   else
   {
      return (NULL);
   }
}
//-
