#include <exec/types.h>
#include <exec/io.h>
#include <proto/exec.h>
#include <libraries/dosextens.h>
#include <proto/dos.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>

#include <devices/tpt.h>

#include <dlg/broadcast.h>
#include <dlg/dlg.h>

#include <link/io.h>

#include <proto/dlg.h>

#include <pragmas/dlg.h>

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

void               main(void);
void               HandleMsg(struct BCMessage *);
void               HandlePkt(struct MyPacket *);
BOOL               SendBCMsgToPorts(char *, char *, char );
BOOL               SendBCMsg(char *, char *, char );
BOOL               SendBCPacket(char *, struct BCMsgNode *, char );
struct BCPortNode *FindBCPort(char *);
void               ProcessPending(struct BCPortNode *);
void               InitList(struct List *);
void               CleanUp(char *);


struct Library *DLGBase  = NULL;

long  __stack             =  4000;
long  __priority          =   0;
long  __BackGroundIO      =   1;
char *__procname          = "TPTBC";

struct MsgPort *bcctl    = NULL;
long            bcsig;

struct MsgPort *readport = NULL;
long            readsig;

struct List     PortList;

/// main()
void main(void)
{
   struct Message *mymsg;
   long            signals;

   if (!(DLGBase=(struct Library *)OpenLibrary(DLGNAME, DLGVERSION)))  exit(5);

   InitList(&PortList);

   Forbid();
      bcctl = FindPort(BCCONTROL);
   Permit();

   if (bcctl)                       CleanUp("TPTBC is already active");

   if (!(bcctl = CreateMsgPort()))  CleanUp("Unable to open control port");
   bcsig                  = (1 << bcctl->mp_SigBit);
   bcctl->mp_Node.ln_Name =  BCCONTROL;
   AddPort(bcctl);

   readport = CreateMsgPort();
   if (!readport)  CleanUp("Unable to create message port");
   readsig = (1 << readport->mp_SigBit);

   AFPrintf(NULL, _Backstdout, "\n TPTBC installed successfully\n\n");

   for(;;)
   {
      signals = Wait(bcsig | readsig);

      if (signals & bcsig)
         while((mymsg = (struct Message *)GetMsg(bcctl)))
            HandleMsg((struct BCMessage *)mymsg);

      if (signals & readsig)
         while((mymsg = (struct Message *)GetMsg(readport)))
            HandlePkt((struct MyPacket *)mymsg);
   }
}
//-
/// HandleMsg()
void HandleMsg(struct BCMessage *message)
{
   struct BCPortNode *pn;
   struct BCMsgNode  *mn;

   switch(message->type)
   {

///   BCEXIT
      case BCEXIT:   AFPrintf(NULL, _Backstdout, "\n TPTBC exiting...\n\n");
                     message->type = BCNOERR;
                     ReplyMsg((struct Message *)message);
                     CleanUp(NULL);
//-

///   BCMSG
      case BCMSG:    Upper(message->ports);

                     if (!SendBCMsgToPorts(message->ports, message->buffer, message->flags))
                        message->type = NOPORT;
                     else
                        message->type = BCNOERR;

                     break;
//-

///   BCPEND
      case BCPEND:   Upper(message->ports);
                     pn = FindBCPort(message->ports);

                     if (!pn)
                     {
                        message->type = NOPORT;
                     }
                     else
                     {
                        pn->status   |= BCPENDING;
                        message->type = BCNOERR;
                     }

                     break;
//-

///   BCRESUME
      case BCRESUME: Upper(message->ports);
                     pn = FindBCPort(message->ports);

                     if (!pn)
                     {
                        message->type = NOPORT;
                        break;
                     }

                     pn->status    &= ~BCPENDING;
                     message->type  =  BCNOERR;
                     ProcessPending(pn);
                     break;
//-

///   BCGET
      case BCGET:    Upper(message->ports);
                     pn = FindBCPort(message->ports);

                     if (!pn)
                     {
                        message->type = NOPORT;
                        break;
                     }

                     mn = (struct BCMsgNode *)RemHead(&(pn->msglist));

                     if (mn)
                     {
                        strcpy(message->buffer, mn->buffer);
                        free(mn);
                        message->type = BCNOERR;
                     }
                     else
                     {
                        message->type = BCNOMSG;
                     }

                     break;
//-

      default:       message->type = BCBAD;
                     break;
   }

   ReplyMsg((struct Message *)message);
}
//-
/// HandlePkt()
void HandlePkt(struct MyPacket *packet)
{
   struct BCMsgNode  *mn;
   struct BCPortNode *pn;

   Close(packet->fhand);
   if (packet->paused)  TSetFlags(T_PAUSE, packet->port);

   mn = packet->msgnode;
   free(mn->packet);
   free(mn);

   pn = (struct BCPortNode *)FindName(&PortList, packet->port);
   if (pn)  pn->status &= ~BCMSGOUT;
   ProcessPending(pn);
}
//-
/// SendBCMsgToPorts()
BOOL SendBCMsgToPorts(char *ports, char *buffer, char flags)
{
   char  portname[4];
   BOOL  result = TRUE;

   for(; *ports; ports += 3)
   {
      strncpy(portname, ports, 3);
      portname[3] = 0;

      result &= SendBCMsg(portname, buffer, flags);
   }

   return(result);
}
//-
/// SendBCMsg()
BOOL SendBCMsg(char *port, char *buffer, char flags)
{
   struct BCMsgNode  *mn;
   struct BCPortNode *pn;
   int                len;

   pn = FindBCPort(port);
   if (!pn)  return(FALSE);

   len = strlen(buffer);
   if (!(flags & BCIMPORTANTMSG)) len = (len>70)?70:len;

   mn  =  malloc(sizeof(struct BCMsgNode) + len + 1);
   if (!mn)  return(FALSE);

   mn->buffer = (char *)mn + sizeof(struct BCMsgNode);
   strncpy(mn->buffer, buffer, len);
   mn->buffer[len] = 0;

   if ((pn->status & BCMSGOUT) || ((pn->status & BCPENDING) && !(flags & BCIMPORTANTMSG)))
   {
      AddTail(&(pn->msglist),(struct Node *)mn);
   }
   else
   {
      if (SendBCPacket(port,mn,flags))
      {
         pn->status |= BCMSGOUT;
      }
      else
      {
         free(mn);
         return(FALSE);
      }
   }

   return(TRUE);
}
//-
/// SendBCPacket()
BOOL SendBCPacket(char *port,struct BCMsgNode *mn,char flags)
{
   long               retval;
   char              *buf;
   struct FileHandle *fh;
   char               portfile[5];

   retval = TSetFlags(0, port);
   if (retval < 0)  return(FALSE);

   mn->packet = malloc(sizeof(struct MyPacket) + 53 + strlen(mn->buffer));
   buf        = (char *)mn->packet + sizeof(struct MyPacket);
   if (!mn->packet)  return(FALSE);

   mn->packet->paused = FALSE;

   if (retval & T_PAUSE)
      if (flags & BCIMPORTANTMSG)
      {
         TUnSetFlags(T_PAUSE, port);
         mn->packet->paused = TRUE;
      }

   strcpy(mn->packet->port, port);
   mn->packet->msgnode = mn;

   strcpy(portfile, port);
   portfile[3] = ':';
   portfile[4] =  0;

   mn->packet->fhand = (long)Open(portfile, MODE_NEWFILE);
   fh                = (struct FileHandle *)(mn->packet->fhand<<2);

   if (!mn->packet->fhand)
   {
      free(mn->packet);
      return(FALSE);
   }

   ASPrintf(NULL, buf, "\n\n\007** System Message **\n\n%s\n\n\007** End Of Message **\n\n", mn->buffer);
   mn->buffer = buf;

   mn->packet->sp_Msg.mn_Node.ln_Name = (char *)&(mn->packet->sp_Pkt);
   mn->packet->sp_Pkt.dp_Link         = (struct Message *)&(mn->packet->sp_Msg);
   mn->packet->sp_Pkt.dp_Type         =  ACTION_WRITE;
   mn->packet->sp_Pkt.dp_Arg1         =  fh->fh_Arg1;
   mn->packet->sp_Pkt.dp_Arg2         = (long)mn->buffer;
   mn->packet->sp_Pkt.dp_Arg3         = (long)strlen(mn->buffer);
   mn->packet->sp_Pkt.dp_Port         = (struct MsgPort *)readport;

   PutMsg(fh->fh_Type, (struct Message *)mn->packet);
   return(TRUE);
}
//-
/// FindBCPort()
struct BCPortNode *FindBCPort(char *port)
{
   struct BCPortNode *pn;
   char               ports[256];
   char              *p;

   pn = (struct BCPortNode *)FindName(&PortList, port);
   if (pn)  return(pn);

   ListPorts(ports, "BBS");

   for(p = ports; *p; p+=3)
      if (!Strnicmp(p, port, 3))
         break;

   if (!*p)  return(NULL);

   pn = malloc(sizeof(struct BCPortNode) + 4);
   if (!pn)  return(NULL);

   pn->node.ln_Name = (char *)pn + sizeof(struct BCPortNode);
   strcpy(pn->node.ln_Name, port);
   pn->status       =  0;

   InitList((struct List *)&(pn->msglist));
   AddTail(&PortList, (struct Node *)pn);

   return(pn);
}
//-
/// ProcessPending()
void ProcessPending(struct BCPortNode *pn)
{
   struct BCMsgNode *mn;

   if (!pn)                     return;
   if (pn->status & BCMSGOUT)   return;
   if (pn->status & BCPENDING)  return;

   mn = (struct BCMsgNode *)RemHead(&(pn->msglist));

   if (mn)
   {
      if (SendBCPacket(pn->node.ln_Name, mn, 0))
         pn->status |= BCMSGOUT;
      else
         free(mn);
   }

   return;
}
//-
/// InitList()
void InitList(struct List *list)
{
   list->lh_Head     = (struct Node *)&(list->lh_Tail);
   list->lh_TailPred = (struct Node *)&(list->lh_Head);
   list->lh_Tail     =  NULL;
   list->lh_Type     =  NT_UNKNOWN;
}
//-
/// CleanUp()
void CleanUp(char *s)
{
   struct BCPortNode *pn;
   struct BCMsgNode  *mn;

   if (bcctl)
   {
      RemPort(bcctl);
      DeleteMsgPort(bcctl);
   }

   while(pn = (struct BCPortNode *)RemHead(&PortList))
   {
      pn->status |= BCPENDING;

      while(pn->status & BCMSGOUT) Delay(10);

      while(mn = (struct BCMsgNode *)RemHead(&(pn->msglist))) free(mn);

      free(pn);
   }

   if (readport)  DeleteMsgPort(readport);

   CloseLibrary(DLGBase);

   if (s)
   {
      Write(_Backstdout, "\n TPTBC Error: ", 15);
      Write(_Backstdout, s, strlen(s));
      Write(_Backstdout, "\n\n", 2);
   }

   Close(_Backstdout);
   exit(s?5:0);
}
//-
