#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <libraries/reqtools.h>
#include <libraries/gadtools.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <intuition/imageclass.h>
#include <rexx/rxslib.h>
#include <rexx/errors.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <pd/nl.h>

#include <stdarg.h>
#include <varargs.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/keymap_protos.h>
#include <clib/reqtools_protos.h>
#include <clib/icon_protos.h>
#include <clib/rexxsyslib_protos.h>

#define VERSION "1.2"
UBYTE *ver="\0$VER: TrapDispatcher "VERSION" ("__COMMODORE_DATE__")";

#define AverageWidth(x) (TextLength(x,"ABCDEFGHIJKLMNOPQRSTUVWXYZ",26)/26)
#define StringHeight(x) (3+((struct RastPort *)x)->Font->tf_YSize+3)
#define ButtonHeight(x) (3+((struct RastPort *)x)->Font->tf_YSize+3)

#define getgadgetstring(g) ((struct StringInfo *)(g)->SpecialInfo)->Buffer
#define getgadgetvalue(g)  ((struct StringInfo *)(g)->SpecialInfo)->LongInt

struct CallNode
{
   struct Node Node;
   UBYTE buf[30];

   UBYTE node[20];
   UBYTE phone[40];

   UBYTE last[40];
   ULONG retries;
   ULONG failed;

   ULONG maxretries;
   ULONG endtime;
   ULONG endday;

   ULONG txxendtime;
   ULONG txxendday;

   BOOL zmhonly;
   BOOL deleted;
};

struct MenuNode
{
   struct Node Node;
   UBYTE Title[50];
   UBYTE Key[2];
   UBYTE Command[256];
};

struct List CallList;
struct List MenuList;

ULONG calls;
ULONG menunum;
ULONG lvlines;

BOOL realstart;

ULONG cfg_Width=0;
ULONG cfg_Height=0;
ULONG cfg_Top=-1;
ULONG cfg_Left=-1;
ULONG cfg_DelayCall=5;
ULONG cfg_DelayAll=10;
UBYTE cfg_DispatchPort[40]={"TRAPDISPATCHER"};
UBYTE cfg_TrapPort[40]={"TrapDoor"};
UBYTE cfg_PubScreen[40]={"TRAPDOOR"};
UBYTE cfg_StartTrapDoor[80]={"MAIL:TrapDoor Answer"};
UBYTE cfg_Nodelist[80]={"NODELIST:"};
ULONG cfg_DefaultRetries=10;
ULONG cfg_MaxFailed=1;
BOOL cfg_ActiveWindow=TRUE;
BOOL cfg_QuitTrapDoor=FALSE;
UBYTE cfg_CommandAfterLast[80];
WORD cfg_TimeZone;

UBYTE cfg_Settings[100]={"MAIL:TrapDispatcher.prefs"};

BOOL zmh;
Addr DefAddr;

extern struct ExecBase *SysBase;
extern struct Library *GadToolsBase;

struct Library *KeymapBase;
struct Library *ReqToolsBase;
struct Library *NodelistBase;
struct RDArgs *rdargs;
struct Screen *scr;
struct Window *win;
struct Gadget *glist;
APTR vi;
struct MsgPort *rexxport;
struct MsgPort *replyport;
NodeList nodelist;

ULONG sentmsg;

struct Menu *menus;
struct NewMenu *menubuf;

struct NewMenu stdmenus[] = { {NM_TITLE,"Project","",0,0,NULL},
                              {NM_ITEM, "About...","?",0,0,NULL},
                              {NM_ITEM, (STRPTR)NM_BARLABEL,NULL,0,0,NULL},
                              {NM_ITEM, "Quit","Q",0,0,NULL},
                              {NM_TITLE,"Settings","",0,0,NULL},
                              {NM_ITEM, "Quit TrapDoor",NULL,CHECKIT|MENUTOGGLE,0,NULL},
                              {NM_ITEM, "Quit after last",NULL,CHECKIT|MENUTOGGLE,0,NULL},
                              {NM_ITEM, "Zone Mail Hour",NULL,CHECKIT|MENUTOGGLE,0,NULL},
                              {NM_ITEM, (STRPTR)NM_BARLABEL,NULL,0,0,NULL},
                              {NM_ITEM, "Save window position",NULL,0,0,NULL},
                              {NM_TITLE,"External","",0,0,NULL}};
/*                            {NM_END,  "","",0,0,NULL}}; */



ShowError(UBYTE *fmt,...);
SendRexxCommand(UBYTE *portname,struct MsgPort *replyport,UBYTE *command,UBYTE *str,ULONG len,BOOL wait);
ReplyRexxCmd(struct RexxMsg *Msg,LONG rc,UBYTE *s);
ShowCall(struct CallNode *call);
ShowEmptyCall(void);
NextCall(void);
RemoveAll(void);
RemoveZMHOnly(void);
BOOL CompleteNode(UBYTE *str,UBYTE *buf);

void CleanUp(int err)
{
   struct CallNode *tmp;
   struct MenuNode *menu;
   struct RexxMsg *RexxMsg;

   while(menu=(struct MenuNode *)RemTail(&MenuList))
      FreeMem(menu,sizeof(struct MenuNode));

   while(tmp=(struct CallNode *)RemTail(&CallList))
      FreeMem(tmp,sizeof(struct CallNode));

   if(menus)
   {
    	ClearMenuStrip(win);
	   FreeMenus(menus);
   }

   if(menubuf)       FreeVec(menubuf);
   if(win)           CloseWindow(win);
   if(glist)         FreeGadgets(glist);
   if(vi)            FreeVisualInfo(vi);
   if(scr)           UnlockPubScreen(NULL,scr);
   if(rdargs)        FreeArgs(rdargs);
   if(nodelist)      NLClose(nodelist);
   if(KeymapBase)    CloseLibrary(KeymapBase);
   if(ReqToolsBase)  CloseLibrary(ReqToolsBase);
   if(NodelistBase)  CloseLibrary(NodelistBase);

   if(cfg_QuitTrapDoor && realstart)
      SendRexxCommand(cfg_TrapPort,replyport,"QUIT",NULL,0,TRUE);

   while(sentmsg && rexxport)
   {
      while(RexxMsg=(struct RexxMsg *)GetMsg(rexxport))
      {
         if(RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
         {
            if(RexxMsg->rm_Args[15])
               ReplyMsg((struct Message *)RexxMsg->rm_Args[15]);

            DeleteArgstring(RexxMsg->rm_Args[0]);
            DeleteRexxMsg(RexxMsg);
            sentmsg--;
         }
         else
         {
            RexxMsg->rm_Result1=0;
            RexxMsg->rm_Result2=0;
            ReplyRexxCmd(RexxMsg,0,NULL);
            ReplyMsg((struct Message *)RexxMsg);
         }
      }

      while(RexxMsg=(struct RexxMsg *)GetMsg(replyport))
      {
         if(RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
         {
            DeleteArgstring(RexxMsg->rm_Args[0]);
            DeleteArgstring((UBYTE *)RexxMsg->rm_Result2);
            DeleteRexxMsg(RexxMsg);
            sentmsg--;
         }
      }

      Wait((1<<rexxport->mp_SigBit) | (1<<replyport->mp_SigBit));
   }

   if(rexxport)
   {
      while(RexxMsg=(struct RexxMsg *)GetMsg(rexxport))
      {
         if(RexxMsg->rm_Node.mn_Node.ln_Type != NT_REPLYMSG)
         {
            RexxMsg->rm_Result1=0;
            RexxMsg->rm_Result2=0;
            ReplyRexxCmd(RexxMsg,0,NULL);
            ReplyMsg((struct Message *)RexxMsg);
         }
      }

      Forbid();
      DeletePort(rexxport);
      Permit();
   }

   if(replyport)
   {
      Forbid();
      DeletePort(replyport);
      Permit();
   }

   _exit(err);
}

ULONG GetTxxTime(UBYTE letter)
{
   LONG txxtime;

   if(letter >= 'A' && letter<='Z')
   {
      txxtime=(letter-'A')*60;
   }
   else if(letter>='a' && letter<='z')
   {
      txxtime=(letter-'a')*60+30;
   }
   else
   {
      return(0);
   }

   txxtime+=cfg_TimeZone*60;

   if(txxtime < 0) txxtime+=1440;
   if(txxtime >= 1440) txxtime-=1440;
   return(txxtime);
}

/* Använder jbcpos */

ULONG jbcpos;

jbstrcpy(UBYTE *dest,UBYTE *src,ULONG maxlen)
{
   ULONG d=0;
   UBYTE stopchar1,stopchar2;

   while(src[jbcpos]==32 || src[jbcpos]==9) jbcpos++;

   if(src[jbcpos]=='"')
   {
      jbcpos++;
      stopchar1='"';
      stopchar2=0;
   }
   else
   {
   	stopchar1=' ';
   	stopchar2=9;
   }

   while(src[jbcpos]!=stopchar1 && src[jbcpos]!=stopchar2 && src[jbcpos]!=10 && src[jbcpos]!=0 && d<maxlen-1)
   {
      if(src[jbcpos]=='\\' && src[jbcpos+1]!=0 && src[jbcpos+1]!=10)
      {
         jbcpos++;
         dest[d++]=src[jbcpos++];
      }

      else
         dest[d++]=src[jbcpos++];
   }
   dest[d]=0;
   if(src[jbcpos]==9 || src[jbcpos]==' ' || src[jbcpos]=='"') jbcpos++;

   if(d!=0 || stopchar1=='"') return(TRUE);

   return(FALSE);
}

UBYTE *cfgcomment="; Windowsize saved from within TrapDispatcher\n";

void UpdateConfig(UBYTE *cfg)
{
   BPTR fh1,fh2;
   UBYTE buf[200],cfgbuf[200],cfgword[100];

   strcpy(buf,cfg);
   strcat(buf,".bak");

   if(!(fh1=Open(cfg,MODE_OLDFILE)))
   {
      ShowError("Unable to open configuration file \"%s\"",cfg);
      CleanUp(10);
   }

   if(!(fh2=Open(buf,MODE_NEWFILE)))
   {
      ShowError("Unable to write to \"%s\"",buf);
      CleanUp(10);
   }

   while(FGets(fh1,cfgbuf,200))
   {
      jbcpos=0;
      jbstrcpy(cfgword,cfgbuf,100);

      if(strcmp(cfgbuf,cfgcomment) != 0 && stricmp(cfgword,"WINDOW") != 0)
         FPuts(fh2,cfgbuf);
   }

   FPuts(fh2,cfgcomment);
   FPrintf(fh2,"WINDOW %lu %lu %lu %lu\n",win->LeftEdge,win->TopEdge,win->Width,win->Height);

   Close(fh1);
   Close(fh2);

   DeleteFile(cfg);
   Rename(buf,cfg);
}


void ReadConfig(UBYTE *cfg)
{
   BPTR fh;
   ULONG cfgline=0;
   UBYTE cfgbuf[200];
   UBYTE cfgword[100];
   UBYTE cfgtemp[40];
   struct MenuNode *menu;

   if(!(fh=Open(cfg,MODE_OLDFILE)))
   {
      ShowError("Unable to open configuration file \"%s\"",cfg);
      return;
   }

   while(FGets(fh,cfgbuf,200))
   {
      cfgline++;
      jbcpos=0;
      jbstrcpy(cfgword,cfgbuf,100);

      if(stricmp(cfgword,"Window")==0)
      {
         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("Wrong number of arguments after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_Left=atoi(cfgtemp);

         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("Wrong number of arguments after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_Top=atoi(cfgtemp);

         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("Wrong number of arguments after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_Width=atoi(cfgtemp);

         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("Wrong number of arguments after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh,);
            CleanUp(10);
         }
         cfg_Height=atoi(cfgtemp);
      }
      else if(stricmp(cfgword,"DefaultNode")==0)
      {
         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }

         if(NLParseAddr(&DefAddr,cfgtemp,NULL))
         {
            ShowError("Invalid node \"%s\" in line %lu",cfgtemp,cfgline);
            Close(fh);
            CleanUp(10);
         }
      }
      else if(stricmp(cfgword,"MaxFailed")==0)
      {
         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_MaxFailed=atoi(cfgtemp);
      }
      else if(stricmp(cfgword,"TimeZone")==0)
      {
         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_TimeZone=atoi(cfgtemp);
      }
      else if(stricmp(cfgword,"DelayCall")==0)
      {
         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_DelayCall=atoi(cfgtemp);
      }
      else if(stricmp(cfgword,"DelayAll")==0)
      {
         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_DelayAll=atoi(cfgtemp);
      }
      else if(stricmp(cfgword,"DefaultRetries")==0)
      {
         if(!(jbstrcpy(cfgtemp,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
         cfg_DefaultRetries=atoi(cfgtemp);
      }
      else if(stricmp(cfgword,"DispatchPort")==0)
      {
         if(!(jbstrcpy(cfg_DispatchPort,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
      }
      else if(stricmp(cfgword,"TrapPort")==0)
      {
         if(!(jbstrcpy(cfg_TrapPort,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
      }
      else if(stricmp(cfgword,"PubScreen")==0)
      {
         if(!(jbstrcpy(cfg_PubScreen,cfgbuf,40)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
      }
      else if(stricmp(cfgword,"StartTrapDoor")==0)
      {
         if(!(jbstrcpy(cfg_StartTrapDoor,cfgbuf,80)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
      }
      else if(stricmp(cfgword,"Nodelist")==0)
      {
         if(!(jbstrcpy(cfg_Nodelist,cfgbuf,80)))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }
      }
      else if(stricmp(cfgword,"InactiveWindow")==0)
      {
         cfg_ActiveWindow=FALSE;
      }
      else if(stricmp(cfgword,"QuitTrapDoor")==0)
      {
         cfg_QuitTrapDoor=TRUE;
      }
      else if(stricmp(cfgword,"External")==0)
      {
         if(!(menu=(struct MenuNode *)AllocMem(sizeof(struct MenuNode),MEMF_CLEAR)))
         {
            DisplayBeep(NULL);
            Close(fh);
            CleanUp(10);
         }

         AddTail(&MenuList,(struct Node *)menu);

         if(!jbstrcpy(menu->Title,cfgbuf,40))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }

         if(!jbstrcpy(menu->Command,cfgbuf,256))
         {
            ShowError("No argument after \"%s\" in line %lu",cfgword,cfgline);
            Close(fh);
            CleanUp(10);
         }

         if(menu->Title[0]=='@' && strlen(menu->Title) > 2)
         {
            menu->Key[0]=menu->Title[1];
            strcpy(menu->Title,&menu->Title[2]);
         }

         menunum++;
      }
      else if(cfgword[0]!=10 && cfgword[0]!=0 && cfgword[0]!=';')
      {
         ShowError("Unknown keyword \"%s\" in line %lu",cfgword,cfgline);
         Close(fh);
         CleanUp(10);
      }
   }

   Close(fh);
}

ReplyRexxCmd(struct RexxMsg *Msg,LONG rc,UBYTE *s)
{
   Msg->rm_Result1=rc;

   if((Msg->rm_Action & (1<< RXFB_RESULT)) && s)
      Msg->rm_Result2=(LONG)CreateArgstring(s,strlen(s));

   else
      Msg->rm_Result2=0;
}

SendRexxMsg(UBYTE *s,struct RexxMsg *m,LONG flags)
{
   struct MsgPort *ARexxport;
   struct RexxMsg *RexxMsg;

   if(!(RexxMsg=(struct RexxMsg *)CreateRexxMsg(rexxport,"trapdispatcher",rexxport->mp_Node.ln_Name)))
      return(NULL);

   if(RexxMsg->rm_Args[0]=(UBYTE *)CreateArgstring(s,strlen(s)))
   {
      RexxMsg->rm_Action = RXCOMM | flags;
      RexxMsg->rm_Args[15]=m;

      Forbid();

      if(ARexxport=(struct MsgPort *)FindPort("REXX"))
         PutMsg(ARexxport,(struct Message *)RexxMsg);

      Permit();

      if(ARexxport)
         return(RexxMsg);
   }

   if(RexxMsg->rm_Args[0])
      DeleteArgstring(RexxMsg->rm_Args[0]);

   DeleteRexxMsg(RexxMsg);
   return(NULL);
}

SendRexxCommand(UBYTE *portname,struct MsgPort *replyport,UBYTE *command,UBYTE *str,ULONG len,BOOL wait)
{
   struct MsgPort *msgport;
   struct RexxMsg *RexxMsg;
   ULONG res;

   Forbid();
   if(!(msgport=FindPort(portname)))
   {
      Permit();
      return(10);
   }

   if(!(RexxMsg=(struct RexxMsg *)CreateRexxMsg(replyport,NULL,msgport->mp_Node.ln_Name)))
   {
      Permit();
      return(10);
   }

   if(!(RexxMsg->rm_Args[0]=(UBYTE *)CreateArgstring(command,strlen(command))))
   {
      Permit();
      DeleteRexxMsg(RexxMsg);
      return(10);
   }

   RexxMsg->rm_Action = RXCOMM | 1L<<RXFB_RESULT;
   PutMsg(msgport,(struct Message *)RexxMsg);
   Permit();

   if(wait)
   {
      WaitPort(replyport);

      while(RexxMsg=(struct RexxMsg *)GetMsg(replyport))
      {
         if(RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
         {
            if(str && RexxMsg->rm_Action & (1L<<RXFB_RESULT))
            {
               strncpy(str,(UBYTE *)RexxMsg->rm_Result2,len-1);
               str[len-1]=0;
            }
            res=RexxMsg->rm_Result1;
            DeleteArgstring(RexxMsg->rm_Args[0]);
            DeleteArgstring((UBYTE *)RexxMsg->rm_Result2);
            DeleteRexxMsg(RexxMsg);
         }
      }
      return(res);
   }
   else
   {
      sentmsg++;
      return(0);
   }
}


#define GAD_ACTION   1
#define GAD_CYCLE    2
#define GAD_LIST     3
#define GAD_NODE     4
#define GAD_RETRIES  5
#define GAD_LAST     6
#define GAD_EXIT     7
#define GAD_ADD      8
#define GAD_REMOVE   9
#define GAD_NOW     10

struct NewGadget newgadgets[10];
struct Gadget *gadgets[10];

struct CallNode *currentcall;
ULONG currentcallnum;

struct CallNode *activecall;
ULONG activecallnum;

ULONG delayleft;

BOOL iscalling,quitprg,quitafterlast,active=TRUE;
UBYTE action[100];

int ListViewTop(struct Gadget *gadget, struct Window *window)
{
   ULONG top;

   if (GadToolsBase->lib_Version < 39)
      top = *(UWORD *)(((char *)gadget) + sizeof(struct Gadget) + 4);
   else
      GT_GetGadgetAttrs(gadget, window, NULL, GTLV_Top, &top, TAG_DONE);

   return (top);
}

void MakeVisible(ULONG vis,ULONG lines,struct Gadget *gadget, struct Window *window)
{
   ULONG top;

   if (GadToolsBase->lib_Version < 39)
   {
      top=ListViewTop(gadget,window);

      if(vis < top)
      {
         while(vis < top)
            top--;
      }
      else
      {
         while(vis > top+lines-1)
            top++;
      }

      GT_SetGadgetAttrs(gadget,window,NULL,GTLV_Top,top,TAG_END);
   }
   else
   {
      GT_SetGadgetAttrs(gadget,window,NULL,GTLV_MakeVisible,vis,TAG_END);
   }
}

#define QUAL_SHIFT (IEQUALIFIER_RSHIFT|IEQUALIFIER_LSHIFT)

ULONG CursorUp(ULONG current,ULONG total,ULONG visible,ULONG top,ULONG qual)
{
   LONG res;

   res=current;

   if(qual & QUAL_SHIFT)
   {
      if(current==top)
         res=current-visible;

      else
         res=top;

      if(res<0)
         res=0;
   }
   else if(qual & IEQUALIFIER_CONTROL)
   {
      res=0;
   }
   else
   {
      if(current>0)
         res=current-1;
   }
   return(res);
}

ULONG CursorDown(ULONG current,ULONG total,ULONG visible,ULONG top,ULONG qual)
{
   LONG res;

   res=current;

   if(qual & QUAL_SHIFT)
   {
      if(top+visible-1 == current)
         res=current+visible;

      else
         res=top+visible-1;

      if(res>=total)
         res=total-1;
   }
   else if(qual & IEQUALIFIER_CONTROL)
   {
      res=total-1;
   }
   else
   {
      if(current<total-1)
         res=current+1;
   }
   return(res);
}

MakeNewGadget(struct NewGadget *ng,struct TextAttr *font,APTR vi,ULONG id,UBYTE *text,ULONG place)
{
   ng->ng_TextAttr=font;
   ng->ng_VisualInfo=vi;
   ng->ng_GadgetID=id;
   ng->ng_GadgetText=text;
   ng->ng_Flags=place;
}

int Widest(int old,UBYTE *text,struct RastPort *rport)
{
   struct TextExtent te;

   TextExtent(rport,text,strlen(text),&te);

   if(te.te_Width > old)
      return(te.te_Width);

   return(old);
}

STRPTR cyclestrings[]={"Active","Inactive",NULL};
UBYTE *texts[]={"Node:","Retries:","Last:","Remove:"};
UBYTE infotexts[4][100];

void DrawGUI(void)
{
   ULONG lvheight,bwidth,bevelheight,textoffset,c;
   struct Gadget *lastgad=NULL;

   textoffset=0;

   for(c=0;c<4;c++)
      textoffset=Widest(textoffset,texts[c],win->RPort);

   textoffset+=14;

   bevelheight=scr->RastPort.Font->tf_YSize*4+2+2;

   lvheight=win->Height-win->BorderBottom-win->BorderTop
            -(StringHeight(&scr->RastPort)+1)*2
            -ButtonHeight(&scr->RastPort)
            -1-bevelheight-1
            -1-1;

   if(GadToolsBase->lib_Version < 39)
      lvlines=(lvheight-8)/scr->RastPort.Font->tf_YSize-1;

   else
      lvlines=(lvheight-4)/scr->RastPort.Font->tf_YSize;

   bwidth=(win->Width-win->BorderLeft-win->BorderRight-2-2-2-2)/3;

   lastgad=(struct Gadget *)CreateContext(&glist);

   MakeNewGadget(&newgadgets[0],scr->Font,vi,GAD_ACTION,"Action",PLACETEXT_LEFT);
   newgadgets[0].ng_LeftEdge=win->BorderLeft+2;
   newgadgets[0].ng_TopEdge=1+win->BorderTop;
   newgadgets[0].ng_Height=StringHeight(&scr->RastPort);
   newgadgets[0].ng_Width=win->Width-win->BorderLeft-win->BorderRight-2-2;

   lastgad = CreateGadget(TEXT_KIND,lastgad,&newgadgets[0],GTTX_Border,TRUE,
                                                           GTTX_Text,action,
                                                           TAG_END);
   gadgets[0]=lastgad;

   MakeNewGadget(&newgadgets[1],scr->Font,vi,GAD_CYCLE,"",0);
   newgadgets[1].ng_LeftEdge=win->BorderLeft+2;
   newgadgets[1].ng_TopEdge=1+win->BorderTop+StringHeight(&scr->RastPort)+1;
   newgadgets[1].ng_Height=StringHeight(&scr->RastPort);
   newgadgets[1].ng_Width=win->Width-win->BorderLeft-win->BorderRight-2-2;

   if(active) c=0;
   else       c=1;

   lastgad = CreateGadget(CYCLE_KIND,lastgad,&newgadgets[1],GTCY_Labels,cyclestrings,
                                                            GTCY_Active,c,
                                                            TAG_END);
   gadgets[1]=lastgad;

   MakeNewGadget(&newgadgets[2],scr->Font,vi,GAD_LIST,"",0);
   newgadgets[2].ng_LeftEdge=win->BorderLeft+2;
   newgadgets[2].ng_TopEdge=1+win->BorderTop+(StringHeight(&scr->RastPort)+1)*2;
   newgadgets[2].ng_Height=lvheight;
   newgadgets[2].ng_Width=win->Width-win->BorderLeft-win->BorderRight-2-2;

   lastgad = CreateGadget(LISTVIEW_KIND,lastgad,&newgadgets[2],GTLV_Labels,       &CallList,
                                                               GTLV_ShowSelected, NULL,
                                                               TAG_END);
   gadgets[2]=lastgad;

   for(c=0;c<4;c++)
   {
      MakeNewGadget(&newgadgets[c+3],scr->Font,vi,GAD_NODE+c,texts[c],PLACETEXT_LEFT);
      newgadgets[c+3].ng_LeftEdge=win->BorderLeft+2+2+2+textoffset;
      newgadgets[c+3].ng_TopEdge=win->Height-win->BorderBottom-1-ButtonHeight(&scr->RastPort)-1-bevelheight+2+scr->RastPort.Font->tf_YSize*c;
      newgadgets[c+3].ng_Height=scr->RastPort.Font->tf_YSize;
      newgadgets[c+3].ng_Width=win->Width-win->BorderLeft-win->BorderRight-2-2-2-2-2-textoffset;

      lastgad = CreateGadget(TEXT_KIND,lastgad,&newgadgets[c+3],GTTX_Clipped,TRUE,
                                                                GTTX_Text,infotexts[c],
                                                                TAG_END);
      gadgets[c+3]=lastgad;
   }

   MakeNewGadget(&newgadgets[7],scr->Font,vi,GAD_ADD,"_Add...",PLACETEXT_IN);
   newgadgets[7].ng_LeftEdge=win->BorderLeft+2;
   newgadgets[7].ng_TopEdge=win->Height-win->BorderBottom-1-ButtonHeight(&scr->RastPort);
   newgadgets[7].ng_Height=ButtonHeight(&scr->RastPort);
   newgadgets[7].ng_Width=bwidth;

   lastgad = CreateGadget(BUTTON_KIND,lastgad,&newgadgets[7],GT_Underscore,'_',TAG_END);
   gadgets[7]=lastgad;

   MakeNewGadget(&newgadgets[8],scr->Font,vi,GAD_REMOVE,"_Remove...",PLACETEXT_IN);
   newgadgets[8].ng_LeftEdge=win->BorderLeft+2+bwidth+2;
   newgadgets[8].ng_TopEdge=win->Height-win->BorderBottom-1-ButtonHeight(&scr->RastPort);
   newgadgets[8].ng_Height=ButtonHeight(&scr->RastPort);
   newgadgets[8].ng_Width=bwidth;

   lastgad = CreateGadget(BUTTON_KIND,lastgad,&newgadgets[8],GT_Underscore,'_',TAG_END);
   gadgets[8]=lastgad;

   MakeNewGadget(&newgadgets[9],scr->Font,vi,GAD_NOW,"Call _now",PLACETEXT_IN);
   newgadgets[9].ng_LeftEdge=win->Width-win->BorderLeft-2-bwidth;
   newgadgets[9].ng_TopEdge=win->Height-win->BorderBottom-1-ButtonHeight(&scr->RastPort);
   newgadgets[9].ng_Height=ButtonHeight(&scr->RastPort);
   newgadgets[9].ng_Width=bwidth;

   lastgad = CreateGadget(BUTTON_KIND,lastgad,&newgadgets[9],GT_Underscore,'_',GA_Disabled,TRUE,TAG_END);
   gadgets[9]=lastgad;

	DrawBevelBox(win->RPort,
	             win->BorderLeft+2,
	             win->Height-win->BorderBottom-1-ButtonHeight(&scr->RastPort)-1-bevelheight,
	             win->Width-win->BorderLeft-win->BorderRight-2-2,
	             bevelheight,
	             GTBB_Recessed,TRUE,
	             GT_VisualInfo,vi,
	             TAG_END);

   AddGList(win,glist,-1,-1,NULL);
   RefreshGList(glist,win,NULL,-1);
   GT_RefreshWindow(win,NULL);

   if(activecall)
   {
      ShowCall(activecall);
      GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Selected,activecallnum,TAG_END);
   }

   if(calls == 0) GT_SetGadgetAttrs(gadgets[GAD_REMOVE-1],win,NULL,GA_Disabled,TRUE,TAG_END);
   else           GT_SetGadgetAttrs(gadgets[GAD_REMOVE-1],win,NULL,GA_Disabled,FALSE,TAG_END);

   if(delayleft == 0) GT_SetGadgetAttrs(gadgets[GAD_NOW-1],win,NULL,GA_Disabled,TRUE,TAG_END);
   else               GT_SetGadgetAttrs(gadgets[GAD_NOW-1],win,NULL,GA_Disabled,FALSE,TAG_END);
}

int copypos;

getcomma(unsigned char *dest, unsigned char *src, int max)
{
   int c=0;
   dest[c]=0;

   if(src[copypos]==0) return(0);

   while(c<max-1 && src[copypos]!=',' && src[copypos]!=0)
      dest[c++]=src[copypos++];

   dest[c]=0;

   if(src[copypos]==',') copypos++;
   return(1);
}

BOOL CompleteNode(UBYTE *str,UBYTE *buf)
{
   Addr node;

   if(NLParseAddr(&node,str,&DefAddr))
      return(FALSE);

   sprintf(buf,"%lu:%lu/%lu.%lu",node.Zone,node.Net,node.Node,node.Point);
   return(TRUE);
}

struct CallNode *FindCall(UBYTE *call)
{
   struct CallNode *tmp;
   ULONG c;

   tmp=CallList.lh_Head;

   for(c=0;c<calls;c++)
   {
      if(strcmp(tmp->node,call)==0)
         return(tmp);

      tmp=tmp->Node.ln_Succ;
   }
   return(NULL);
}

BOOL FindPhone(UBYTE *phone)
{
   struct CallNode *tmp;
   ULONG c;

   tmp=CallList.lh_Head;

   for(c=0;c<calls;c++)
   {
      if(strcmp(tmp->phone,phone)==0)
         return(tmp);

      tmp=tmp->Node.ln_Succ;
   }
   return(NULL);
}

DoAddCall(UBYTE *buf,UBYTE *phone,ULONG retries,ULONG endtime,ULONG txxendtime,BOOL zmhonly)
{
   struct CallNode *tmp;
   struct DateStamp ds;

   if(!(tmp=AllocMem(sizeof(struct CallNode),MEMF_CLEAR)))
   {
      DisplayBeep(NULL);
      return(20);
   }

   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,~0,TAG_END);

   strncpy(tmp->node,buf,19);
   tmp->node[19]=0;

   tmp->maxretries=retries;
   tmp->endtime=endtime % 1440;
   tmp->txxendtime=txxendtime % 1440;
   tmp->zmhonly=zmhonly;
   strcpy(tmp->phone,phone);

   DateStamp(&ds);

   tmp->endday=ds.ds_Days;
   tmp->txxendday=ds.ds_Days;

   if(ds.ds_Minute >= tmp->endtime)
      tmp->endday++;

   if(ds.ds_Minute >= tmp->txxendtime)
      tmp->txxendday++;

   tmp->Node.ln_Name=tmp->node;

   AddTail(&CallList,(struct Node *)tmp);
   calls++;
   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,&CallList,TAG_END);

   if(calls == 1)
   {
      NextCall();
      GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Selected,1,TAG_END);
      activecallnum=0;
      activecall=tmp;
      ShowCall(activecall);
   }

   /* delaying och ska ta första som nästa... */
   if(delayleft > 0 && currentcallnum == 0 && calls > 1)
   {
      currentcallnum=calls-1;
      currentcall=tmp;
   }

   GT_SetGadgetAttrs(gadgets[GAD_REMOVE-1],win,NULL,GA_Disabled,FALSE,TAG_END);
   return(0);
}

ULONG AddCall(UBYTE *str,ULONG retries,ULONG endtime,BOOL quiet,BOOL force,UBYTE *err)
{
   Addr node;
   UBYTE buf[50],flag[20],*tmp;
   NodeDesc *nodedesc;
   BOOL cm;
   BOOL res,correcttime;
   ULONG txxendtime,txxstarttime;
   struct DateStamp ds;

   DateStamp(&ds);

   if(!CompleteNode(str,buf))
   {
      if(err)
         strcpy(err,"Invalid node number");

      if(!quiet)
         rtEZRequestTags("%s is an invalid node number","Ok",NULL,&str,
                  RT_Underscore,'_',
                  RT_Window,win,
                  RT_WaitPointer,TRUE,
                  RT_LockWindow, TRUE,
                  RTEZ_Flags, EZREQF_CENTERTEXT,
                  TAG_END);

      return(10);
   }

   tmp=buf;

   if(FindCall(buf))
   {
      if(err)
         strcpy(err,"Already in the list");

      if(!quiet)
         rtEZRequestTags("%s is already in the list","Ok",NULL,&tmp,
                  RT_Underscore,'_',
                  RT_Window,win,
                  RT_WaitPointer,TRUE,
                  RT_LockWindow, TRUE,
                  RTEZ_Flags, EZREQF_CENTERTEXT,
                  TAG_END);

      return(5);
   }

	NLParseAddr(&node,buf,NULL);

   if(!(nodedesc = NLFind(nodelist,&node,0)))
   {
      if(err)
         strcpy(err,"Node not found in nodelist");

      if(!quiet)
         rtEZRequestTags("Node %s not found in nodelist","Ok",NULL,&tmp,
                  RT_Underscore,'_',
                  RT_Window,win,
                  RT_WaitPointer,TRUE,
                  RT_LockWindow, TRUE,
                  RTEZ_Flags, EZREQF_CENTERTEXT,
                  TAG_END);

	   return(10);
   }

   if(nodedesc->Type == NL_ENTRY_HOLD && !force)
   {
      if(err)
         strcpy(err,"Node is listed as Hold");

      if(quiet)
      {
         NLFreeNode(nodedesc);
         return(10);
      }

      res=rtEZRequestTags("Node %s is listed as Hold. Call anyway?","Call|Cancel",NULL,&tmp,
                  RT_Underscore,'_',
                  RT_Window,win,
                  RT_WaitPointer,TRUE,
                  RT_LockWindow, TRUE,
                  RTEZ_Flags, EZREQF_CENTERTEXT,
                  TAG_END);

      if(!res)
      {
         NLFreeNode(nodedesc);
         return(10);
      }
   }

   if(FindPhone(nodedesc->Phone))
   {
      if(err)
         strcpy(err,"A node with the same phonenumber is in the list");

      if(!quiet)
         rtEZRequestTags("A node with the same phonenumber as %s\nis already in the list","Ok",NULL,&tmp,
                  RT_Underscore,'_',
                  RT_Window,win,
                  RT_WaitPointer,TRUE,
                  RT_LockWindow, TRUE,
                  RTEZ_Flags, EZREQF_CENTERTEXT,
                  TAG_END);

      NLFreeNode(nodedesc);
      return(5);
   }

   cm=FALSE;
   copypos=0;

   txxendtime=0;
   txxstarttime=0;

   while(getcomma(flag,nodedesc->Flags,19))
   {
      if(stricmp(flag,"CM")==0) cm=TRUE;

      if(flag[0]=='T' && strlen(flag)==3)
         if(toupper(flag[1]) >= 'A' && toupper(flag[1]) <= 'Z')
            if(toupper(flag[2]) >= 'A' && toupper(flag[2]) <= 'Z')
            {
               txxstarttime=GetTxxTime(flag[1]);
               txxendtime=GetTxxTime(flag[2]);
            }
   }

   if(txxstarttime!=0 || txxendtime!=0)
   {
      correcttime=FALSE;

      if(ds.ds_Minute >= txxstarttime && ds.ds_Minute <= txxendtime) correcttime=TRUE;
      if(ds.ds_Minute <= txxendtime && ds.ds_Minute <=txxstarttime && txxstarttime>txxendtime) correcttime=TRUE;
      if(ds.ds_Minute >= txxstarttime && ds.ds_Minute >=txxstarttime && txxstarttime>txxendtime) correcttime=TRUE;

      if(!correcttime && !force)
      {
         UBYTE reqstr[200];

         if(err)
            sprintf(err,"Only online between %02lu:%02lu and %02lu:%02lu (local time)",
               txxstarttime/60,
               txxstarttime%60,
               txxendtime/60,
               txxendtime%60);

         res=FALSE;

         sprintf(reqstr,"%s is only online between\n%02lu:%02lu and %02lu:%02lu (local time)",
               tmp,
               txxstarttime/60,
               txxstarttime%60,
               txxendtime/60,
               txxendtime%60);

         if(!quiet)
            res=rtEZRequestTags(reqstr,"Call|Cancel",NULL,NULL,
                           RT_Underscore,'_',
                           RT_Window,win,
                           RT_WaitPointer,TRUE,
                           RT_LockWindow, TRUE,
                           RTEZ_Flags, EZREQF_CENTERTEXT,
                           TAG_END);

         if(!res)
         {
            NLFreeNode(nodedesc);
            return(10);
         }
      }
   }
   
   if(!cm && !zmh && !force && txxstarttime==0 && txxendtime==0)
   {
      if(err)
         strcpy(err,"Not marked as CM and not ZMH");

      res=FALSE;

      if(!quiet)
         res=rtEZRequestTags("%s is not marked as CM and should\nonly be called during Zone Mail Hour","Call|Cancel",NULL,&tmp,
                        RT_Underscore,'_',
                        RT_Window,win,
                        RT_WaitPointer,TRUE,
                        RT_LockWindow, TRUE,
                        RTEZ_Flags, EZREQF_CENTERTEXT,
                        TAG_END);

      if(!res)
      {
         NLFreeNode(nodedesc);
         return(10);
      }
   }

   if(!cm && txxstarttime==0 && txxendtime==0)
      DoAddCall(buf,nodedesc->Phone,retries,endtime,txxendtime,TRUE);

   else
      DoAddCall(buf,nodedesc->Phone,retries,endtime,txxendtime,FALSE);

   if(err)
      strcpy(err,"Everything ok");

   NLFreeNode(nodedesc);
   return(0);
}

ULONG RemoveCall(UBYTE *node)
{
   ULONG c;
   struct CallNode *tmp;
   UBYTE buf[50];

   if(!CompleteNode(node,buf))
      return(10);

   if(!(tmp=FindCall(buf)))
      return(5);

   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,~0,TAG_END);

   if(currentcall == tmp)
   {
      if(iscalling)
      {
         tmp->deleted=TRUE;
      }
      else
      {
         NextCall();

         calls--;

         if(calls == 0)
         {
            currentcall=NULL;
            delayleft=0;
            NextCall();
         }

         Remove((struct Node *)tmp);
         FreeMem(tmp,sizeof(struct CallNode));
      }
   }
   else
   {
      calls--;
      Remove((struct Node *)tmp);
      FreeMem(tmp,sizeof(struct CallNode));
   }

   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,&CallList,TAG_END);

   if(calls != 0)
   {
      if(activecallnum == calls) activecallnum--;
      activecall=CallList.lh_Head;
      for(c=0;c<activecallnum;c++) activecall=activecall->Node.ln_Succ;
      ShowCall(activecall);
      GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Selected,activecallnum,TAG_END);
   }
   else
   {
      activecall=NULL;
      activecallnum=0;
      ShowEmptyCall();
      GT_SetGadgetAttrs(gadgets[GAD_REMOVE-1],win,NULL,GA_Disabled,TRUE,TAG_END);
      delayleft=0;
   }

   return(0);
}

RemoveAll()
{
   ULONG c,newcalls;
   struct CallNode *tmp,*tmp2;

   tmp=CallList.lh_Head;
   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,~0,TAG_END);

   newcalls=calls;

   for(c=0;c<calls;c++)
   {
      if(currentcall == tmp)
      {
         if(iscalling)
         {
            tmp->deleted=TRUE;
            tmp=tmp->Node.ln_Succ;
         }
         else
         {
            currentcall=NULL;
            tmp2=tmp->Node.ln_Succ;
            Remove((struct Node *)tmp);
            FreeMem(tmp,sizeof(struct CallNode));
            newcalls--;
            tmp=tmp2;
         }
      }
      else
      {
         tmp2=tmp->Node.ln_Succ;
         Remove((struct Node *)tmp);
         FreeMem(tmp,sizeof(struct CallNode));
         newcalls--;
         tmp=tmp2;
      }
   }
   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,&CallList,TAG_END);
   calls=newcalls;

   if(calls != 0)
   {
      activecallnum=0;
      activecall=CallList.lh_Head;
      GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Selected,activecallnum,TAG_END);
   }

   return;
}

RemoveZMHOnly()
{
   ULONG c,newcalls;
   struct CallNode *tmp,*tmp2;

   tmp=CallList.lh_Head;
   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,~0,TAG_END);

   newcalls=calls;

   for(c=0;c<calls;c++)
   {
      if(tmp->zmhonly)
      {
         if(currentcall == tmp)
         {
            if(iscalling)
            {
               tmp->deleted=TRUE;
               tmp=tmp->Node.ln_Succ;
            }
            else
            {
               currentcall=NULL;
               tmp2=tmp->Node.ln_Succ;
               Remove((struct Node *)tmp);
               FreeMem(tmp,sizeof(struct CallNode));
               newcalls--;
               tmp=tmp2;
            }
         }
         else
         {
            tmp2=tmp->Node.ln_Succ;
            Remove((struct Node *)tmp);
            FreeMem(tmp,sizeof(struct CallNode));
            newcalls--;
            tmp=tmp2;
         }
      }
      else
      {
         tmp=tmp->Node.ln_Succ;
      }
   }
   GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Labels,&CallList,TAG_END);
   calls=newcalls;

   if(calls != 0)
   {
      activecallnum=0;
      activecall=CallList.lh_Head;
      GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Selected,activecallnum,TAG_END);
   }

   return;
}

ShowEmptyCall()
{
   ULONG c;

   for(c=0;c<4;c++)
      infotexts[c][0]=0;

   GT_SetGadgetAttrs(gadgets[GAD_NODE-1],win,NULL,GTTX_Text,infotexts[0],TAG_END);
   GT_SetGadgetAttrs(gadgets[GAD_RETRIES-1],win,NULL,GTTX_Text,infotexts[1],TAG_END);
   GT_SetGadgetAttrs(gadgets[GAD_LAST-1],win,NULL,GTTX_Text,infotexts[2],TAG_END);
   GT_SetGadgetAttrs(gadgets[GAD_EXIT-1],win,NULL,GTTX_Text,infotexts[3],TAG_END);
}

ShowCall(struct CallNode *call)
{
   strcpy(infotexts[0],call->node);
   GT_SetGadgetAttrs(gadgets[GAD_NODE-1],win,NULL,GTTX_Text,infotexts[0],TAG_END);

   if(call->failed == 0)
      sprintf(infotexts[1],"%lu",call->retries);

   else
      sprintf(infotexts[1],"%lu (%lu failed)",call->retries,call->failed);

   GT_SetGadgetAttrs(gadgets[GAD_RETRIES-1],win,NULL,GTTX_Text,infotexts[1],TAG_END);

   strcpy(infotexts[2],call->last);
   if(infotexts[2][0]==0) strcpy(infotexts[2],"<None>");

   GT_SetGadgetAttrs(gadgets[GAD_LAST-1],win,NULL,GTTX_Text,infotexts[2],TAG_END);

   if(call->deleted)
      strcpy(infotexts[3],"After this call");

   else if(call->maxretries!=0)
      sprintf(infotexts[3],"After %lu retries",call->maxretries);

   else
      sprintf(infotexts[3],"At %02lu:%02lu",call->endtime/60,call->endtime%60);

   if(call->zmhonly)
   {
      strcat(infotexts[3],"/End of ZMH");
   }
   else if(call->txxendtime)
   {
      UBYTE tmpbuf[100];
      sprintf(tmpbuf,"/At %02lu:%02lu",call->txxendtime/60,call->txxendtime%60);
      strcat(infotexts[3],tmpbuf);
   }

   GT_SetGadgetAttrs(gadgets[GAD_EXIT-1],win,NULL,GTTX_Text,infotexts[3],TAG_END);
}

NextCall(void)
{
   if(calls == 0)
   {
      strcpy(action,"No calls in list");
      GT_SetGadgetAttrs(gadgets[GAD_ACTION-1],win,NULL,GTTX_Text,action,TAG_END);
      currentcall=NULL;
   }
   else
   {
      if(currentcall == NULL)
      {
         currentcall = (struct CallNode *)CallList.lh_Head;
         currentcallnum=0;
      }
      else
      {
         if(currentcallnum < calls-1)
         {
            currentcall = (struct CallNode *)currentcall->Node.ln_Succ;
            currentcallnum++;
         }
         else
         {
            currentcall = (struct CallNode *)CallList.lh_Head;
            currentcallnum=0;
         }
      }
   }
}

/*
ULONG LookupNode(UBYTE *str)
{
   Addr node;
   NodeDesc *nodedesc;
   ULONG args[10];

   if(!NLParseAddr(&node,buf,NULL))
      return;

   if(!(nodedesc = NLFind(nodelist,&node,0)))
      return;

         rtEZRequestTags("%s is an invalid node number","Ok",NULL,&str,
                  RT_Underscore,'_',
                  RT_Window,win,
                  RT_WaitPointer,TRUE,
                  RT_LockWindow, TRUE,
                  RTEZ_Flags, EZREQF_CENTERTEXT,
                  TAG_END);

      return(10);



   NLFreeNode(nodedesc);
   return(0);
}
*/

void _waitwbmsg(void);
routine_that_is_never_called() { _waitwbmsg(); }
extern struct WBStartup *_WBMsg;

#define CALL            0
#define RETRIES         1
#define UNTIL           2
#define TIME            3
#define FORCE           4
#define REQ             5
#define ZMH             6
#define NOZMH           7
#define QUITAFTERLAST   8
#define QUITTRAPDOOR    9
#define KEEPTRAPDOOR    10
#define INACTIVEWINDOW  11
#define SETTINGS        12

UBYTE *argstr="CALL/M,RETRIES/K/N,UNTIL/K,TIME/K/N,FORCE/S,REQ/S,ZMH/S,NOZMH/S,QUITAFTERLAST/S,QUITTRAPDOOR/S,KEEPTRAPDOOR/S,INACTIVEWINDOW/S,SETTINGS/K";
ULONG argarray[13]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};

myputs(UBYTE *str)
{
   Write(Output(),str,strlen(str));
}

LONG GetSizeHeight(struct Screen *scr)
{
   struct DrawInfo *dri;
   APTR obj;
   LONG h = 10;

   if(dri = GetScreenDrawInfo(scr))
   {
      if(obj = NewObject( NULL, "sysiclass", SYSIA_DrawInfo, dri,
                                             SYSIA_Size, SYSISIZE_MEDRES,
                                             SYSIA_Which, SIZEIMAGE,
                                             TAG_DONE))
      {
         if(!GetAttr(IA_Height,obj,&h))
            h = 10;  // This check maybe isn't necessary... :)

         DisposeObject(obj);
      }

      FreeScreenDrawInfo(scr,dri);
   }

   return(h);
}

ShowError(UBYTE *fmt,...)
{
   va_list args;

   va_start(args, fmt);

   if(_WBMsg)
   {
      rtEZRequestTags(fmt,"Quit",NULL,(APTR)args,TAG_END);
   }
   else
   {
      VFPrintf(Output(),fmt,args);
      Printf("\n");
   }

   va_end(args);
}

/* Rawcodes for the shiftkeys */

#define LEFTSHIFT   0x60
#define RIGHTSHIFT  0x61

BOOL FakePress(struct Gadget *gad,struct Window *win,UBYTE rawcode)
{
   BOOL kg,res,shift;
   struct IntuiMessage *msg;

   /* Draw the selected image */

   gad->Flags|=GFLG_SELECTED;
   RefreshGList(gad,win,NULL,1);

   kg=TRUE;
   shift=FALSE;

   /* Main loop */

   while(kg)
   {
      WaitPort(win->UserPort);

      while(msg=(struct IntuiMessage *)GetMsg(win->UserPort))
      {
         /* Key released */

         if(msg->Class == IDCMP_RAWKEY && msg->Code == rawcode+128)
         {
            res=TRUE;
            kg=FALSE;
         }

         /* Shift key pressed */

         if(msg->Class == IDCMP_RAWKEY && (msg->Code==LEFTSHIFT || msg->Code==RIGHTSHIFT))
         {
            shift=TRUE;
            res=FALSE;
            kg=FALSE;
         }

         /* Window deactivated */

         if(msg->Class == IDCMP_INACTIVEWINDOW)
         {
            res=FALSE;
            kg=FALSE;
         }

         ReplyMsg((struct Message *)msg);
      }
   }

   /* Draw normal image */

   gad->Flags&=~GFLG_SELECTED;
   RefreshGList(gad,win,NULL,1);

   /* Wait for the key to be released if it was aborted with shift */

   if(shift)
   {
      kg=TRUE;

      while(kg)
      {
         WaitPort(win->UserPort);

         while(msg=(struct IntuiMessage *)GetMsg(win->UserPort))
         {
            if(msg->Class == IDCMP_RAWKEY && msg->Code == rawcode+128)
               kg=FALSE;

            ReplyMsg((struct Message *)msg);
         }
      }
   }

   return(res);
}

GadgetRemove()
{
   ULONG res;

   res=rtEZRequestTags("Do you really want to remove this call?","_Remove|_Cancel",NULL,NULL,
            RT_Underscore,'_',
            RT_Window,win,
            RT_WaitPointer,TRUE,
            RT_LockWindow, TRUE,
            RTEZ_Flags, EZREQF_CENTERTEXT | EZREQF_NORETURNKEY,
            TAG_END);

   if(res) RemoveCall(activecall->node);
}

GadgetAdd()
{
   ULONG res;
   UBYTE buf[100];

   buf[0]=0;

   res=rtGetString(buf,99,"Call node...",NULL,
         RT_Window,win,
         RT_WaitPointer, TRUE,
         RT_LockWindow, TRUE,
         TAG_END);

   if(res)
      res=AddCall(buf,cfg_DefaultRetries,0,FALSE,FALSE,NULL);
}

ExecuteCommand(UBYTE *com)
{
   BPTR nilfh;

   if(com[0]=='@')
   {
      SendRexxCommand(cfg_TrapPort,replyport,&com[1],NULL,0,TRUE);
   }
   else
   {
      if(nilfh=Open("NIL:",MODE_READWRITE))
      {
         SystemTags(com,SYS_Input,nilfh,
                        SYS_Output,NULL,
                        SYS_Asynch,TRUE,
                        SYS_UserShell, TRUE,
                        TAG_END);
      }
   }
}

UBYTE *abouttxt="TrapDispatcher "VERSION"\nCopyright 1995 Johan Billing\n"
                "All rights reserved.\n"
                "\n"
                "This program has been released as FreeWare.";

void PassOnError(UBYTE *com,ULONG res)
{
   if(res == 0)
      Printf("-> %s, everything ok\n",com);

   else
      Printf("-> %s, error %lu\n",com,res);
}

_main()
{
   struct InputEvent ie;
   struct IntuiMessage *imsg;
   struct RexxMsg *RexxMsg;
   ULONG class,code,qualifier;
   APTR iaddress;
   UBYTE buf[100],rexxbuf[100],callbuf[100],*errbuf,**callarray;
   UBYTE retbuf[100],*retstr;
   ULONG c,err,retries,endtime,res,minwidth,minheight,actual;
   BOOL ok;
   struct DateStamp ds;
   struct CallNode *tmp;
   BPTR nilfh;
   struct MenuItem *menuitem;
   struct Menu *setmenu;
   ULONG num;
   struct MenuNode *menunode;
   struct NewMenu *menutmp;

   if(SysBase->LibNode.lib_Version<37)
   {
      if(!_WBMsg) myputs("Sorry! This program requires Kickstart 2.04 or higher\n");
      _exit(0);
   }

   if(!(replyport=(struct MsgPort *)CreatePort(NULL,0)))
   {
      DisplayBeep(NULL);
      CleanUp(10);
   }

   if(!(KeymapBase=OpenLibrary("keymap.library",37)))
      CleanUp(10);

   if(!(ReqToolsBase=OpenLibrary(REQTOOLSNAME,REQTOOLSVERSION)))
   {
      if(!_WBMsg) Printf("TrapDispatcher needs "REQTOOLSNAME" %lu or higher\n",REQTOOLSVERSION);
      CleanUp(10);
   }

   if(!(NodelistBase=OpenLibrary(TRAPLIST_NAME, TRAPLIST_VER)))
   {
      ShowError("Can't open "TRAPLIST_NAME" %lu or higher",TRAPLIST_VER);
      CleanUp(10);
   }

   if(_WBMsg)
   {
      struct DiskObject *dobj;
      UBYTE *what;
      BPTR olddir;

      olddir=CurrentDir(_WBMsg->sm_ArgList->wa_Lock);

      if(!(dobj=GetDiskObject(_WBMsg->sm_ArgList->wa_Name)))
      {
         CurrentDir(olddir);
         CleanUp(10);
      }

      if(what=FindToolType(dobj->do_ToolTypes,"SETTINGS"))
      {
         strncpy(cfg_Settings,what,99);
         cfg_Settings[99]=0;
      }

      FreeDiskObject(dobj);
      CurrentDir(olddir);
   }
   else
   {
      if(!(rdargs=ReadArgs(argstr,argarray,NULL)))
      {
         PrintFault(IoErr(),"");
         CleanUp(10);
      }
   }

   if(argarray[SETTINGS])
   {
      strncpy(cfg_Settings,(UBYTE *)argarray[SETTINGS],99);
      cfg_Settings[99]=0;
   }

   NewList(&CallList);
   NewList(&MenuList);

   ReadConfig(cfg_Settings);

   Forbid();
   if(FindPort(cfg_DispatchPort))
   {
      Permit();

      if(_WBMsg)
      {
         ShowError("TrapDispatcher is already running");
      }
      else
      {
         Printf("TrapDispatcher is already running, passing on commands...\n");

         if(argarray[CALL])
         {
            callarray=(UBYTE **)argarray[CALL];

            for(c=0;callarray[c];c++)
            {
               if(argarray[TIME])
                  sprintf(buf,"CALL %s TIME %lu",callarray[c],*(ULONG *)argarray[TIME]);

               else if(argarray[UNTIL])
                  sprintf(buf,"CALL %s UNTIL %s",callarray[c],(UBYTE *)argarray[UNTIL]);

               else if(argarray[RETRIES])
                  sprintf(buf,"CALL %s RETRIES %lu",callarray[c],*(ULONG *)argarray[RETRIES]);

               else
                  sprintf(buf,"CALL %s",callarray[c]);

               if(argarray[FORCE])
                  strcat(buf," FORCE");

               if(argarray[REQ])
                  strcat(buf," REQ");

               res=SendRexxCommand(cfg_DispatchPort,replyport,buf,NULL,0,TRUE);
               PassOnError(buf,res);
            }
         }
         if(argarray[QUITTRAPDOOR])
         {
            res=SendRexxCommand(cfg_DispatchPort,replyport,"QUITTRAPDOOR",NULL,0,TRUE);
            PassOnError("QUITTRAPDOOR",res);
         }
         if(argarray[ZMH])
         {
            res=SendRexxCommand(cfg_DispatchPort,replyport,"ZMH",NULL,0,TRUE);
            PassOnError("ZMH",res);
         }
         if(argarray[NOZMH])
         {
            res=SendRexxCommand(cfg_DispatchPort,replyport,"NOZMH",NULL,0,TRUE);
            PassOnError("NOZMH",res);
         }
         if(argarray[KEEPTRAPDOOR])
         {
            res=SendRexxCommand(cfg_DispatchPort,replyport,"KEEPTRAPDOOR",NULL,0,TRUE);
            PassOnError("KEEPTRAPDOOR",res);
         }
         if(argarray[QUITAFTERLAST])
         {
            res=SendRexxCommand(cfg_DispatchPort,replyport,"QUITAFTERLAST",NULL,0,TRUE);
            PassOnError("QUITAFTERLAST",res);
         }
      }
      CleanUp(0);
   }
   Permit();

   realstart=TRUE;

   if(argarray[QUITAFTERLAST])
      quitafterlast=TRUE;

   retries=0;

   if(argarray[RETRIES])
      retries=*(ULONG *)argarray[RETRIES];

   if(retries == 0)
      retries=cfg_DefaultRetries;

   endtime=0;

   if(argarray[UNTIL])
   {
      endtime=atoi((UBYTE *)argarray[UNTIL])*60;
      for(c=0;((UBYTE *)argarray[UNTIL])[c]!=0 && ((UBYTE *)argarray[UNTIL])[c]!=':';c++);
      if(((UBYTE *)argarray[UNTIL])[c]==':') endtime+=atoi(&((UBYTE *)argarray[UNTIL])[c+1]);
   }

   if(argarray[TIME])
   {
      DateStamp(&ds);
      endtime=ds.ds_Minute+(*(ULONG *)argarray[TIME]);
   }

   if(endtime != 0)
      retries=0;

	if(!(nodelist=NLOpen(cfg_Nodelist,0)))
   {
      ShowError("Can't open nodelist in %s\n",cfg_Nodelist);
      CleanUp(10);
   }

   if(argarray[ZMH])
      zmh=TRUE;

   if(argarray[QUITTRAPDOOR])
      cfg_QuitTrapDoor=TRUE;

   if(argarray[KEEPTRAPDOOR])
      cfg_QuitTrapDoor=FALSE;

   if(argarray[INACTIVEWINDOW])
      cfg_ActiveWindow=FALSE;

   Forbid();
   if(!FindPort(cfg_TrapPort))
   {
      Permit();

      if(cfg_StartTrapDoor[0])
      {
         if(nilfh=Open("NIL:",MODE_READWRITE))
         {
            res=SystemTags(cfg_StartTrapDoor,SYS_Input,nilfh,
                                             SYS_Output,NULL,
                                             SYS_Asynch,TRUE,
                                             SYS_UserShell, TRUE,
                                             TAG_END);
         }

         ok=FALSE;

         if(res == 0)
         {
            for(c=0;c<50 && ok==FALSE;c++)
            {
               Forbid();
               if(FindPort(cfg_TrapPort)) ok=TRUE;
               Permit();

               if(!ok) Delay(10);
            }
         }

         if(!ok)
         {
            ShowError("Couldn't start TrapDoor");
            CleanUp(10);
         }
      }
      else
      {
         Permit();
         ShowError("TrapDoor is not running");
         CleanUp(10);
      }
   }

   Forbid();
   if(!(rexxport=(struct MsgPort *)CreatePort(cfg_DispatchPort,0)))
   {
      Permit();
      ShowError("Couldn't create ARexx message port");
      CleanUp(10);
   }
   Permit();

   if(!(scr=LockPubScreen(cfg_PubScreen)))
   {
      if(!(scr=LockPubScreen(NULL)))
      {
         ShowError("Couldn't find screen");
         CleanUp(10);
      }
   }

   minwidth=0;

   minwidth=Widest(minwidth,"Add...",&scr->RastPort);
   minwidth=Widest(minwidth,"Remove...",&scr->RastPort);
   minwidth=Widest(minwidth,"Call now",&scr->RastPort);

   minwidth=(minwidth+6)*3+4*2;

   minheight=1+StringHeight(&scr->RastPort)+1+StringHeight(&scr->RastPort)+
              6+scr->RastPort.Font->tf_YSize*5+
              1+scr->RastPort.Font->tf_YSize*4+2+2+
              ButtonHeight(&scr->RastPort)+1+1;

   minheight+=scr->WBorTop+GetSizeHeight(scr);
   minwidth+=scr->WBorLeft+scr->WBorRight;

   if(cfg_Width<minwidth)
      cfg_Width=minwidth;

   if(cfg_Height<minheight)
      cfg_Height=minheight;

   win=OpenWindowTags(NULL,WA_Top,                cfg_Top,
                           WA_Left,               cfg_Left,
                           WA_PubScreen,          scr,
                           WA_Width,              cfg_Width,
                           WA_Height,             cfg_Height,
                           WA_MinWidth,           minwidth,
                           WA_MinHeight,          minheight,
                           WA_MaxWidth,           -1,
                           WA_MaxHeight,          -1,
                           WA_AutoAdjust,         TRUE,
                           WA_Flags,              WFLG_SIZEGADGET|WFLG_SIZEBBOTTOM|WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_DRAGBAR|WFLG_NEWLOOKMENUS,
                           WA_Activate,           cfg_ActiveWindow,
                           WA_IDCMP,              IDCMP_NEWSIZE|IDCMP_CLOSEWINDOW|CYCLEIDCMP|IDCMP_GADGETUP|IDCMP_RAWKEY|IDCMP_MENUPICK|IDCMP_INACTIVEWINDOW|LISTVIEWIDCMP,
                           WA_Title,              "TrapDispatcher "VERSION" © 1995 Johan Billing",
                           TAG_END);

   if(!win)
   {
      DisplayBeep(NULL);
      CleanUp(10);
   }

   vi=GetVisualInfo(scr,TAG_END);

   if(!(menubuf=(struct MewMenu *)AllocVec(sizeof(stdmenus)+sizeof(struct NewMenu)*(1+menunum),MEMF_CLEAR)))
   {
      DisplayBeep(NULL);
      CleanUp(0);
   }

   CopyMem(stdmenus,menubuf,sizeof(stdmenus));

   menutmp=(struct NewMenu *)(((ULONG)menubuf)+sizeof(stdmenus));
   menunode=MenuList.lh_Head;

   for(c=0;c<menunum;c++)
   {
      menutmp->nm_Type=NM_ITEM;
      menutmp->nm_Label=menunode->Title;

      if(menunode->Key[0])
         menutmp->nm_CommKey=menunode->Key;

      menutmp=(struct NewMenu *)(((ULONG)menutmp)+sizeof(struct NewMenu));
      menunode=menunode->Node.ln_Succ;
   }

   menutmp->nm_Type=NM_END;

	if(!(menus=CreateMenus(menubuf,GTMN_FrontPen,0L,TAG_DONE)))
   {
      DisplayBeep(NULL);
      CleanUp(0);
   }

	LayoutMenus(menus,vi,GTMN_TextAttr,scr->Font,GTMN_NewLookMenus,TRUE,TAG_DONE);

   if(cfg_QuitTrapDoor)
   {
      setmenu=menus->NextMenu;
      menuitem=setmenu->FirstItem;
      menuitem->Flags |= CHECKED;
   }

   if(quitafterlast)
   {
      setmenu=menus->NextMenu;
      menuitem=setmenu->FirstItem->NextItem;
      menuitem->Flags |= CHECKED;
   }

   if(zmh)
   {
      setmenu=menus->NextMenu;
      menuitem=setmenu->FirstItem->NextItem->NextItem;
      menuitem->Flags |= CHECKED;
   }

	SetMenuStrip(win,menus);
   DrawGUI();

   if(argarray[CALL])
   {
      callarray=(UBYTE **)argarray[CALL];

      for(c=0;callarray[c];c++)
      {
         AddCall(callarray[c],retries,endtime,!argarray[REQ],argarray[FORCE],NULL);
      }
   }

   for(;;)
   {
      while(imsg=GT_GetIMsg(win->UserPort))
      {
         class=imsg->Class;
         code=imsg->Code;
         qualifier=imsg->Qualifier;
         iaddress=imsg->IAddress;
         GT_ReplyIMsg(imsg);

         switch(class)
         {
            case IDCMP_CLOSEWINDOW:   quitprg=TRUE;
                                      break;

            case IDCMP_MENUPICK:      num=code;
                                      while(num!=MENUNULL)
                                      {
                                         if(MENUNUM(num)==0)
                                         {
                                            switch(ITEMNUM(num))
                                            {
                                               case 0: rtEZRequestTags(abouttxt,"_Ok",NULL,NULL,RT_Underscore,'_',
                                                                                                RT_Window,win,
                                                                                                RT_WaitPointer,TRUE,
                                                                                                RT_LockWindow, TRUE,
                                                                                                RTEZ_Flags, EZREQF_CENTERTEXT,
                                                                                                TAG_END);
                                                       break;
                                               case 2: quitprg=TRUE;
                                                       break;
                                            }
                                         }
                                         if(MENUNUM(num)==1)
                                         {
                                            switch(ITEMNUM(num))
                                            {
                                               case 0: setmenu=menus->NextMenu;
                                                       menuitem=setmenu->FirstItem;

                                                       if(menuitem->Flags & CHECKED) cfg_QuitTrapDoor=TRUE;
                                                       else                          cfg_QuitTrapDoor=FALSE;
                                                       break;
                                               case 1: setmenu=menus->NextMenu;
                                                       menuitem=setmenu->FirstItem->NextItem;

                                                       if(menuitem->Flags & CHECKED) quitafterlast=TRUE;
                                                       else                          quitafterlast=FALSE;
                                                       break;
                                               case 2: setmenu=menus->NextMenu;
                                                       menuitem=setmenu->FirstItem->NextItem->NextItem;

                                                       if(menuitem->Flags & CHECKED) zmh=TRUE;
                                                       else                          zmh=FALSE;
                                                       if(!zmh) RemoveZMHOnly();
                                                       break;
                                               case 4: UpdateConfig(cfg_Settings);
                                                       break;
                                            }
                                         }
                                         if(MENUNUM(num)==2)
                                         {
                                            menunode=MenuList.lh_Head;
                                            for(c=0;c<ITEMNUM(num);c++)
                                               menunode=menunode->Node.ln_Succ;
                                            ExecuteCommand(menunode->Command);
                                         }
                                         menuitem=ItemAddress(menus,num);
                                         num=menuitem->NextSelect;
                                      }

                                      break;
            case IDCMP_GADGETUP:      switch(((struct Gadget *)iaddress)->GadgetID)
                                      {
                                         case GAD_NOW:      delayleft=0;
                                                            break;
                                         case GAD_LIST:     activecallnum=code;
                                                            activecall=CallList.lh_Head;
                                                            for(c=0;c<code;c++) activecall=activecall->Node.ln_Succ;
                                                            ShowCall(activecall);
                                                            break;
                                         case GAD_CYCLE:    if(code == 0) active=TRUE;
                                                            if(code == 1) active=FALSE;
                                                            break;
                                         case GAD_REMOVE:   GadgetRemove();
                                                            break;
                                         case GAD_ADD:      GadgetAdd();
                                                            break;                                                        break;
                                      }
                                      break;

            case IDCMP_NEWSIZE:       RemoveGList(win,glist,-1);
                                      FreeGadgets(glist);
                                      Move(win->RPort,0,0);
                                      ClearScreen(win->RPort);
                                      RefreshWindowFrame(win);
                                      DrawGUI();
                                      break;

            case IDCMP_RAWKEY:        if(code == CURSORUP)
                                      {
                                         if(calls != 0)
                                         {
                                            activecallnum=CursorUp(activecallnum,calls,lvlines,ListViewTop(gadgets[GAD_LIST-1],win),qualifier);
                                            activecall=CallList.lh_Head;
                                            for(c=0;c<activecallnum;c++) activecall=activecall->Node.ln_Succ;
                                            GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Selected,activecallnum,TAG_END);
                                            MakeVisible(activecallnum,lvlines,gadgets[GAD_LIST-1],win);
                                            ShowCall(activecall);
                                         }
                                      }
                                      else if(code == CURSORDOWN)
                                      {
                                         if(calls != 0)
                                         {
                                            activecallnum=CursorDown(activecallnum,calls,lvlines,ListViewTop(gadgets[GAD_LIST-1],win),qualifier);
                                            activecall=CallList.lh_Head;
                                            for(c=0;c<activecallnum;c++) activecall=activecall->Node.ln_Succ;
                                            GT_SetGadgetAttrs(gadgets[GAD_LIST-1],win,NULL,GTLV_Selected,activecallnum,TAG_END);
                                            MakeVisible(activecallnum,lvlines,gadgets[GAD_LIST-1],win);
                                            ShowCall(activecall);
                                         }
                                      }
                                      else
                                      {
                                         ie.ie_Class = IECLASS_RAWKEY;
                                         ie.ie_SubClass = 0;
                                         ie.ie_Code = code;
                                         ie.ie_Qualifier = qualifier;
                                         /* recover dead key codes & qualifiers */
                                         ie.ie_EventAddress = (APTR *) *((ULONG *)iaddress);
                                         actual = MapRawKey(&ie,buf,99,0);

                                         if(actual)
                                         {
                                            if(ToUpper(buf[0])=='N')
                                            {
                                               if(delayleft > 9)
                                               {
                                                  if(FakePress(gadgets[GAD_NOW-1],win,code))
                                                     delayleft=0;
                                               }
                                            }
                                            if(ToUpper(buf[0])=='A')
                                            {
                                               if(FakePress(gadgets[GAD_ADD-1],win,code))
                                                  GadgetAdd();
                                            }
                                            if(ToUpper(buf[0])=='R')
                                            {
                                               if(calls != 0)
                                               {
                                                  if(FakePress(gadgets[GAD_REMOVE-1],win,code))
                                                     GadgetRemove();
                                               }
                                            }

                                            if(code == 0x40)
                                            {
                                               if(active==FALSE)
                                               {
                                                  active=TRUE;
                                                  GT_SetGadgetAttrs(gadgets[GAD_CYCLE-1],win,NULL,GTCY_Active,0,TAG_END);
                                               }
                                               else
                                               {
                                                  active=FALSE;
                                                  GT_SetGadgetAttrs(gadgets[GAD_CYCLE-1],win,NULL,GTCY_Active,1,TAG_END);
                                               }
                                               break;
                                            }
                                         }
                                      }
                                      break;

            case IDCMP_REFRESHWINDOW: GT_BeginRefresh(win);
                                      GT_EndRefresh(win,TRUE);
         }
      }

      /* ta emot ARexx-kommandon */

      while(RexxMsg=(struct RexxMsg *)GetMsg(rexxport))
      {
         if(RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
         {
            if(RexxMsg->rm_Args[15])
               ReplyMsg((struct Message *)RexxMsg->rm_Args[15]);

            DeleteArgstring(RexxMsg->rm_Args[0]);
            DeleteRexxMsg(RexxMsg);
            sentmsg--;
         }
         else
         {
            RexxMsg->rm_Result1=0;
            RexxMsg->rm_Result2=0;

            strncpy(rexxbuf,RexxMsg->rm_Args[0],99);
            rexxbuf[99]=0;

            if(rexxbuf[0]=='@')
               strcpy(rexxbuf,&rexxbuf[1]);

            jbcpos=0;
            err=0;
            retstr=NULL;
            ok=TRUE;

            if(jbstrcpy(buf,rexxbuf,100))
            {
               if(stricmp(buf,"QUIT")==0)
               {
                  quitprg=TRUE;
               }
               else if(stricmp(buf,"REMOVEALL")==0)
               {
                  RemoveAll();
               }
               else if(stricmp(buf,"QUITAFTERLAST")==0)
               {
                  quitafterlast=TRUE;
                  ClearMenuStrip(win);
                  setmenu=menus->NextMenu;
                  menuitem=setmenu->FirstItem->NextItem;
                  menuitem->Flags |= CHECKED;
                  ResetMenuStrip(win,menus);
               }
               else if(stricmp(buf,"QUITTRAPDOOR")==0)
               {
                  cfg_QuitTrapDoor=TRUE;
                  ClearMenuStrip(win);
                  setmenu=menus->NextMenu;
                  menuitem=setmenu->FirstItem;
                  menuitem->Flags |= CHECKED;
                  ResetMenuStrip(win,menus);
               }
               else if(stricmp(buf,"KEEPTRAPDOOR")==0)
               {
                  cfg_QuitTrapDoor=FALSE;
                  ClearMenuStrip(win);
                  setmenu=menus->NextMenu;
                  menuitem=setmenu->FirstItem;
                  menuitem->Flags &= ~(CHECKED);
                  ResetMenuStrip(win,menus);
               }
               else if(stricmp(buf,"ZMH")==0)
               {
                  zmh=TRUE;
                  ClearMenuStrip(win);
                  setmenu=menus->NextMenu;
                  menuitem=setmenu->FirstItem->NextItem->NextItem;
                  menuitem->Flags |= CHECKED;
                  ResetMenuStrip(win,menus);
               }
               else if(stricmp(buf,"NOZMH")==0)
               {
                  zmh=FALSE;
                  ClearMenuStrip(win);
                  setmenu=menus->NextMenu;
                  menuitem=setmenu->FirstItem->NextItem->NextItem;
                  menuitem->Flags &= ~(CHECKED);
                  ResetMenuStrip(win,menus);
                  RemoveZMHOnly();
               }
               else if(stricmp(buf,"ACTIVE")==0)
               {
                  active=TRUE;
                  GT_SetGadgetAttrs(gadgets[GAD_CYCLE-1],win,NULL,GTCY_Active,0,TAG_END);
               }
               else if(stricmp(buf,"INACTIVE")==0)
               {
                  active=FALSE;
                  GT_SetGadgetAttrs(gadgets[GAD_CYCLE-1],win,NULL,GTCY_Active,1,TAG_END);
               }
               else if(stricmp(buf,"COMMANDAFTERLAST")==0)
               {
                  if(!jbstrcpy(cfg_CommandAfterLast,rexxbuf,100))
                     err=10;
               }
               else if(stricmp(buf,"REMOVE")==0)
               {
                  if(jbstrcpy(buf,rexxbuf,100))
                     res=RemoveCall(buf);

                  else
                     err=10;
               }
               else if(stricmp(buf,"GETERROR")==0)
               {
                  retstr=retbuf;
               }
               else if(stricmp(buf,"CALL")==0)
               {
                  BOOL req,force;

                  req=FALSE;
                  force=FALSE;

                  if(jbstrcpy(callbuf,rexxbuf,100))
                  {
                     retries=0;
                     endtime=0;

                     while(jbstrcpy(buf,rexxbuf,100))
                     {
                        if(stricmp(buf,"RETRIES")==0)
                        {
                           if(jbstrcpy(buf,rexxbuf,100))
                              retries=atoi(buf);

                           else
                              err=10;
                        }
                        else if(stricmp(buf,"UNTIL")==0)
                        {
                           if(jbstrcpy(buf,rexxbuf,100))
                           {
                              endtime=atoi(buf)*60;
                              for(c=0;buf[c]!=0 && buf[c]!=':';c++);
                              if(buf[c]==':') endtime+=atoi(&buf[c+1]);
                           }
                           else
                              err=10;
                        }
                        else if(stricmp(buf,"TIME")==0)
                        {
                           if(jbstrcpy(buf,rexxbuf,100))
                           {
                              DateStamp(&ds);
                              endtime=ds.ds_Minute+atoi(buf);
                           }
                           else
                              err=10;
                        }
                        else if(stricmp(buf,"REQ")==0)
                        {
                           req=TRUE;
                        }
                        else if(stricmp(buf,"FORCE")==0)
                        {
                           force=TRUE;
                        }
                     }

                     if(err==0)
                     {
                        if(retries == 0 && endtime == 0)
                           retries=cfg_DefaultRetries;

                        err=AddCall(callbuf,retries,endtime,!req,force,retbuf);
                     }
                  }
                  else
                  {
                     err=10;
                  }
               }
               else
               {
                  ok=FALSE;
               }
            }
            else
            {
               ok=FALSE;
            }

            if(ok)
            {
               ReplyRexxCmd(RexxMsg,err,retstr);
               ReplyMsg((struct Message *)RexxMsg);
            }
            else
            {
               if(SendRexxMsg(RexxMsg->rm_Args[0],RexxMsg,0))
               {
                  sentmsg++;
               }
               else
               {
                  ReplyRexxCmd(RexxMsg,RC_FATAL,NULL);
                  ReplyMsg((struct Message *)RexxMsg);
               }
            }
         }
      }

      if(quitprg)
      {
         if(iscalling)
         {
            strcpy(action,"Waiting...");
            GT_SetGadgetAttrs(gadgets[GAD_ACTION-1],win,NULL,GTTX_Text,action,TAG_END);
         }
         else
         {
            CleanUp(0);
         }
      }

      while(RexxMsg=(struct RexxMsg *)GetMsg(replyport))
      {
         if(RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
         {
            err=666;

            if(RexxMsg->rm_Result2)
               err=atoi((UBYTE *)RexxMsg->rm_Result2);

            DeleteArgstring(RexxMsg->rm_Args[0]);
            DeleteArgstring((UBYTE *)RexxMsg->rm_Result2);
            DeleteRexxMsg(RexxMsg);
            sentmsg--;

            switch(err)
            {
               case 0:  errbuf=NULL;
                        tmp=CallList.lh_Head;
                        for(c=0;c<calls;c++)
                        {
                           if(strcmp(tmp->node,currentcall->node)==0)
                              tmp->deleted=TRUE;

                           tmp=tmp->Node.ln_Succ;
                        }
                        break;
               case 2:  errbuf="Protocol error (too many retries)";
                        currentcall->retries++;
                        currentcall->failed++;
                        break;
               case 5:  errbuf="User break";
                        currentcall->retries++;
                        break;
               case 6:  errbuf="Carrier lost";
                        currentcall->retries++;
                        currentcall->failed++;
                        break;
               case 7:  errbuf="Disk I/O error";
                        currentcall->retries++;
                        currentcall->failed++;
                        break;
               case 8:  errbuf="Remote cancelled transmission";
                        currentcall->retries++;
                        currentcall->failed++;
                        break;
               case 9:  errbuf="Internal TrapDoor error";
                        currentcall->retries++;
                        currentcall->failed++;
                        break;
               case 11: errbuf="Remote didn't answer";
                        currentcall->retries++;
                        break;
               case 13: errbuf="Node not found in nodelist";
                        currentcall->deleted=TRUE;
                        break;
               case 14: errbuf="Accounting limit reached";
                        currentcall->deleted=TRUE;
                        break;
               case 15: errbuf="Node is undialable";
                        currentcall->deleted=TRUE;
                        break;
               case 16: errbuf="No response from modem";
                        currentcall->retries++;
                        break;
               case 17: errbuf="Baudrate too low";
                        currentcall->retries++;
                        currentcall->failed++;
                        break;
               case 18: errbuf="BUSY";
                        currentcall->retries++;
                        break;
               case 19: errbuf="VOICE";
                        currentcall->retries++;
                        break;
               case 20: errbuf="NO DIALTONE";
                        currentcall->retries++;
                        break;
               case 21: errbuf="NO CARRIER";
                        currentcall->retries++;
                        break;
               case 22: errbuf="RINGING limit (NO ANSWER)";
                        currentcall->retries++;
                        break;
               case 23: errbuf="Incoming call detected";
                        currentcall->retries++;
                        break;
               case 24: errbuf="ERROR";
                        currentcall->retries++;
                        break;
               default: errbuf="Unknown response from TrapDoor";
                        currentcall->retries++;
                        break;
            }

            if(errbuf)
            {
               strcpy(action,errbuf);
               GT_SetGadgetAttrs(gadgets[GAD_ACTION-1],win,NULL,GTTX_Text,action,TAG_END);
               strcpy(currentcall->last,errbuf);
               if(activecall == currentcall) ShowCall(currentcall);
            }

            NextCall();
            iscalling=FALSE;

            c=0;
            DateStamp(&ds);

            while(c!=calls)
            {
               tmp=CallList.lh_Head;

               for(c=0;c<calls;c++)
               {
                  if(tmp->deleted)
                     break;

                  if(cfg_MaxFailed != 0 && tmp->failed == cfg_MaxFailed)
                     break;

                  if(tmp->maxretries != 0 && tmp->retries == tmp->maxretries)
                     break;

                  if(tmp->maxretries == 0 && ds.ds_Minute >= tmp->endtime && tmp->endday == ds.ds_Days)
                     break;

                  if(tmp->txxendtime != 0 && ds.ds_Minute >= tmp->txxendtime && tmp->txxendday == ds.ds_Days)
                     break;

                  tmp=tmp->Node.ln_Succ;
               }
               if(c!=calls) RemoveCall(tmp->node);
            }

            if(calls == 0 && cfg_CommandAfterLast[0])
               ExecuteCommand(cfg_CommandAfterLast);

            if(quitprg || (calls == 0 && quitafterlast))
               CleanUp(0);

            if(calls != 0)
            {
               if(currentcallnum == 0) delayleft=10*cfg_DelayAll+9;
               else                    delayleft=10*cfg_DelayCall+9;
               GT_SetGadgetAttrs(gadgets[GAD_NOW-1],win,NULL,GA_Disabled,FALSE,TAG_END);
            }
         }
      }

      if(!active && delayleft)
      {
         delayleft%=10; /* behålla NO CARRIER, BUSY etc ett tag... */
         strcpy(action,"");
         GT_SetGadgetAttrs(gadgets[GAD_ACTION-1],win,NULL,GTTX_Text,action,TAG_END);
      }

      if(delayleft == 0)
      {
         GT_SetGadgetAttrs(gadgets[GAD_NOW-1],win,NULL,GA_Disabled,TRUE,TAG_END);

         if(iscalling == FALSE && currentcall && active)
         {
            /* dags för ett nytt call... */

            errbuf=NULL;

            sprintf(buf,"@CALL %s",currentcall->node);
            if(SendRexxCommand(cfg_TrapPort,replyport,buf,buf,100,TRUE)==0)
            {
               if(stricmp(buf,"OUTGOING")==0)
                  errbuf="TrapDoor is already calling";

               else if(stricmp(buf,"INCOMING")==0)
                  errbuf="TrapDoor is answering a call";

               else if(stricmp(buf,"CLOSED")==0)
                  errbuf="TrapDoor is sleeping";

               else if(stricmp(buf,"SPAWNING")==0)
                  errbuf="TrapDoor is running a command";

               else
               {
                  SendRexxCommand(cfg_TrapPort,replyport,"STATUS X",NULL,0,FALSE);
                  /* Ingen delay att avbryta... */

                  sprintf(action,"Calling %s",currentcall->node);
                  GT_SetGadgetAttrs(gadgets[GAD_ACTION-1],win,NULL,GTTX_Text,action,TAG_END);
                  iscalling=TRUE;
               }
            }
            else
            {
               errbuf="TrapDoor is not running";
            }

            if(errbuf)
            {
               strcpy(action,errbuf);
               GT_SetGadgetAttrs(gadgets[GAD_ACTION-1],win,NULL,GTTX_Text,action,TAG_END);
               GT_SetGadgetAttrs(gadgets[GAD_NOW-1],win,NULL,GA_Disabled,FALSE,TAG_END);
               strcpy(currentcall->last,errbuf);

               if(currentcallnum == 0) delayleft=10*cfg_DelayAll+9;
               else                    delayleft=10*cfg_DelayCall+9;

               if(activecall == currentcall) ShowCall(currentcall);
            }
         }

         if(delayleft == 0)
            Wait((1<<rexxport->mp_SigBit) | (1<<win->UserPort->mp_SigBit) | (1<<replyport->mp_SigBit));
      }
      else
      {
         /* delay countdown */

         if(delayleft % 10 == 0)
         {
            sprintf(action,"Delaying, %lu seconds left",delayleft/10);
            GT_SetGadgetAttrs(gadgets[GAD_ACTION-1],win,NULL,GTTX_Text,action,TAG_END);
         }
         Delay(5);
         delayleft--;
      }
   }
}

