#include "ToolManager.h"

/* Version string */
MyIdent="$VER: ToolManager 1.0 (4.11.1990)";

/* Library pointers */
extern struct Library *SysBase,*IntuitionBase; /* Autoinit Libraries */
struct Library *WorkbenchBase,*IconBase;

/* Structures for my icon */
__chip UWORD ImageData[]={ /* Graphic data (48x48 Pixels) */
 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
 0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,
 0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,
 0x3fff,0xffff,0xfffC,0x3fff,0xffff,0xfffC,0x3fff,0xffff,0xfffC,
 0x3fff,0xffff,0xfffC,0x3fff,0xffff,0xfffC,0x3fff,0xffff,0xfffC,
 0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,
 0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,0x0000,0x7ffE,0x0000,
 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000};
struct Image MyIconImage={NULL,NULL,48,24,1,ImageData,1,0,NULL};
struct DiskObject MyIcon={NULL,NULL,
  /* Gadget definition */ {NULL,NULL,NULL,48,25,NULL,NULL,NULL,&MyIconImage,
                           NULL,NULL,NULL,NULL,NULL,NULL},
                          NULL,NULL,NULL,NO_ICON_POSITION,
                          NO_ICON_POSITION,NULL,NULL,NULL};
struct AppIcon *MyAppIcon;

/* Structures for my window */
struct IntuiText MyText={1,0,JAM2,0,0,NULL,(UBYTE *) "Active Tools: 00",NULL};
struct NewWindow MyNewWindow={20,20,250,50,0,1,CLOSEWINDOW,WINDOWDRAG|
                              WINDOWDEPTH|WINDOWCLOSE|SMART_REFRESH|ACTIVATE,
                              NULL,NULL,
                              (UBYTE *) "ToolManager ©1990 by S. Becker",NULL,
                              NULL,0,0,0,0,WBENCHSCREEN};
struct Window *MyWindow;

/* Structures for my message port */
struct MsgPort *MyMP;
char *MyMPName="ToolManager";

/* Tags for System() */
struct TagItem MyTags[]={SYS_Input,NULL,  /* Input file handle */
                         SYS_Output,NULL, /* Output file handle */
                         SYS_Asynch,TRUE, /* Run tools asynchronously */
                         TAG_DONE,NULL};

/* miscellaneous */
char *MyName;
struct AppMenuItem *MyAppMenuItem;
struct List ToolList; /* This list contains ToolNodes */
UWORD ActTools=0;     /* number of tools in ToolList */
#define BUFLEN 100
char ConfigLine[BUFLEN];

/* Workbench main entry point */
void wbmain(struct WBStartup *wbarg)
{
 BPTR fl;

 MyName=wbarg->sm_ArgList->wa_Name;         /* What is my name? */
 fl=CurrentDir(wbarg->sm_ArgList->wa_Lock); /* Goto a valid directory */
 startup();                                 /* common startup code */
 CurrentDir(fl);                            /* Go back to old directory */
                                            /* Process WB startup parameters */
 WBAddToolNode(&wbarg->sm_ArgList[1],wbarg->sm_NumArgs-1);
 mainloop();                                /* Go into main loop */
}

/* CLI main entry point */
void main(int argc, char **argv)
{
 int i;
 BPTR fl;

 MyName=argv[0];      /* What is my name? */
 startup();           /* common startup code */
 fl=CurrentDir(NULL); /* Get current directory lock */
                      /* Process CLI startup parameters */
 for (i=1; i<argc; i++) AddToolNode(fl,argv[i],NULL);
 CurrentDir(fl);      /* Go back to old directory */
 mainloop();          /* Go into main loop */
}

/* Handle common startup operations */
void startup(void)
{
 BPTR fl;
 FILE *fh; /* Filehandle for config file */

 if (SysBase->lib_Version<36)  /* Sanity check */
  {
   puts("You need a Kickstart 2.0 or better to run ToolManager!");
   exit(20);
  }

 Forbid();                     /* Is "ToolManager" already running? */
 MyMP=FindPort(MyMPName);
 Permit();
 if (MyMP)
  {                            /* Yes, exit gracefully */
   puts("ToolManager already running!");
   exit(5);
  }

 if (!(WorkbenchBase=OpenLibrary(WORKBENCH_NAME,36))) /* Open libraries */
  cleanup(1);

 if (!(IconBase=OpenLibrary(ICONNAME,0)))
  cleanup(2);

 if (!(MyMP=CreateMsgPort()))  /* Create a message port */
  cleanup(3);
 MyMP->mp_Node.ln_Pri=0;
 MyMP->mp_Node.ln_Name=MyMPName; /* Our name */
 AddPort(MyMP);                  /* Announce our presence */

 /* Notify Workbench about icon */
 if (!(MyAppIcon=AddAppIcon(NULL,NULL,MyName,MyMP,NULL,&MyIcon,NULL)))
  cleanup(4);

 /* Notify Workbench about special menu item. If this item is selected,
    the program will quit */
 if (!(MyAppMenuItem=AddAppMenuItem(NULL,NULL,"Quit ToolManager",MyMP,NULL)))
  cleanup(5);

 NewList(&ToolList);  /* Initialize tool list */
 fl=CurrentDir(NULL); /* Get the current directory */

 if (fh=fopen("S:ToolManager.Config","r")) /* Scan config file */
  {
   while (!feof(fh)) /* if not end of file, read one line into buffer */
    if (fgets(ConfigLine,BUFLEN,fh) && (strlen(ConfigLine)>1))
     {
      char *tp;

      ConfigLine[strlen(ConfigLine)-1]='\0'; /* Strip newline */
      if (tp=strchr(ConfigLine,';'))         /* scan config line */
       {
        *tp='\0';                        /* Menu entry ; real program name */
        AddToolNode(fl,ConfigLine,++tp);
       }
      else
       AddToolNode(fl,ConfigLine,NULL);  /* Menu entry == real program name */
     }
   fclose(fh);  /* close config file */
  }
 CurrentDir(fl); /* go back to old directory */
}

/* The main processing loop */
void mainloop(void)
{
 BOOL end=FALSE;               /* Flag for main loop */
 BOOL windowopen=FALSE;
 ULONG sigs,bsigs,psigs,wsigs; /* Buffers for signals */
 struct ToolNode *tn;
 BPTR fl,dfl;                  /* AmigaDOS Filehandles */

                            /* break signals */
 bsigs=SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F;
 psigs=1L<<MyMP->mp_SigBit; /* port signal */
 sigs=bsigs|psigs;          /* merge signals */

 while (!end) /* main loop */
  {
   register struct AppMessage *msg; /* pointer to received message */
   register ULONG gotsigs;          /* received signals */

   gotsigs=Wait(sigs); /* Wait for specified signals */

   if (gotsigs&bsigs)  /* Got break signals? */
    end=TRUE;          /* Yes, quit program */

   if (gotsigs&psigs)  /* Message arrived at message port? */
    while (msg=GetMsg(MyMP)) /* Empty message queue */
     {
      switch(msg->am_Type)   /* Switch between message types */
       {
        case MTYPE_APPICON:      /* Icon action */
         if (msg->am_NumArgs==0) /* Did the user double click my icon? */
          {                      /* Yes! If window not open, open it */
           if ((!windowopen) && (MyWindow=OpenWindow(&MyNewWindow)))
            {
             PrintActTools();   /* Print status line */
             wsigs=1L<<MyWindow->UserPort->mp_SigBit; /* Window signals */
             sigs|=wsigs;       /* Merge with other signals */
             windowopen=TRUE;   /* Window is open */
            }
          }
         else                    /* User dropped an icon on my icon */
          {                      /* Add Workbench parameters to tool list */
           if (!WBAddToolNode(msg->am_ArgList,msg->am_NumArgs))
            DisplayBeep(NULL);
           if (windowopen)       /* If window is open, actualize status line */
            PrintActTools();
          }
         break;

        case MTYPE_APPMENUITEM: /* Menu action */
         if (msg->am_ID==NULL)  /* Special menu item selected? */
          end=TRUE;             /* Yes, quit program */
         else                   /* No, scan list for selected tool */
          for (tn=(struct ToolNode *) ToolList.lh_Head; tn->tn_Node.ln_Succ;
               tn=(struct ToolNode *) tn->tn_Node.ln_Succ)
           if (tn->tn_ID==msg->am_ID)
            {                               /* Corresponding tool found */
             register char *cp;

             fl=CurrentDir(tn->tn_DirLock); /* Change to tool's directory */
             dfl=Open("NIL:",MODE_NEWFILE); /* Open dummy files for */
             MyTags[0].ti_Data=dfl;         /* program I/O */
             dfl=Open("NIL:",MODE_NEWFILE);
             MyTags[1].ti_Data=dfl;
             if (!(cp=tn->tn_RealName))     /* Get tool name */
              cp=tn->tn_Name;
             if (System(cp,MyTags)==-1)     /* Start tool */
               DisplayBeep(NULL);
             CurrentDir(fl);                /* Change to old directory */
             break;                         /* quit loop */
            }
         break;
       } /* end of switch(msg->am_Type) */

      ReplyMsg(msg); /* Reply message to sender */
     } /* end of while(msg=GetMsg(MyMP)) */

   if (windowopen && (gotsigs&wsigs)) /* If window open, got window signal? */
    {
     CloseWindow(MyWindow); /* Yes, close window */
     sigs&=~wsigs;          /* Remove window signals */
     windowopen=FALSE;      /* Window is closed */
    }
  } /* end of main loop */

 /* If window is open, close it */
 if (windowopen) CloseWindow(MyWindow);

 while (ActTools--) /* delete all entries on tool list */
  {
   tn=RemHead(&ToolList);              /* Remove node from list */
   UnLock(tn->tn_DirLock);             /* Free directory */
   RemoveAppMenuItem(tn->tn_MenuItem); /* Remove menu entry */
   if (tn->tn_RealName) free(tn->tn_RealName); /* free memory */
   free(tn->tn_Name);
   free(tn);
  }

 cleanup(99); /* exit program */
}

/* Scan Workbench parameters and add them to tool list */
BOOL WBAddToolNode(struct WBArg *wbarg, int numargs)
{
 int i;
 BOOL rc=TRUE;
 BPTR fl;
 struct DiskObject *dobj;

 for (i=0; i<numargs; i++, wbarg++) /* Scan all parameters */
  if ((wbarg->wa_Lock) && (strlen(wbarg->wa_Name)!=0)) /* Sanity check */
   {
    fl=CurrentDir(wbarg->wa_Lock);          /* Change to icon's directory */
    if (dobj=GetDiskObject(wbarg->wa_Name)) /* Get icon data */
     {
      switch(dobj->do_Type) /* Perform action depending on icon type */
       {
        case WBTOOL: /* Icon is a Tool. Menu entry == real program name */
         rc&=AddToolNode((struct FileLock *) wbarg->wa_Lock,wbarg->wa_Name,
                         NULL);
         break;
        case WBPROJECT: /* Icon is a Project. Menu entry = icon name,
                           real program name = default tool name */
         rc&=AddToolNode((struct FileLock *) wbarg->wa_Lock,wbarg->wa_Name,
                         dobj->do_DefaultTool);
         break;
        default:rc&=FALSE; /* Every other icon type is erroneous */
                break;
       }
      FreeDiskObject(dobj); /* Release icon data */
     }
    else
     rc&=FALSE; /* Error: Can't get icon data */

    CurrentDir(fl); /* Change to old directory */
   }
  else
   rc&=FALSE; /* Error: Bad Workbench parameter */

 return(rc); /* Return TRUE if no error */
}

/* Add one program to tool list */
BOOL AddToolNode(BPTR dir, char *name, char *realname)
{
 struct ToolNode *tn;

 if (ActTools>99) goto e1; /* Not more than 99 Tools :-) possible */

 if (!(tn=malloc(sizeof(struct ToolNode)))) /* Get memory for ToolNode */
  goto e1;

 if (!(tn->tn_Name=malloc(strlen(name)+1))) /* Get memory for program name */
  goto e2;
 strcpy(tn->tn_Name,name); /* Copy name to allocated memory */

 if (realname) /* Does the program have a real name? */
  {
   if (!(tn->tn_RealName=malloc(strlen(realname)+1))) /* Allocate memory */
    goto e3;
   strcpy(tn->tn_RealName,realname); /* Copy real program name */
  }
 else
  tn->tn_RealName=NULL; /* otherwise: Menu item = real program name */

 /* Add a new menu entry */
 if (!(tn->tn_MenuItem=AddAppMenuItem(ActTools+1,NULL,tn->tn_Name,MyMP,NULL)))
  goto e4;

 if (!(tn->tn_DirLock=DupLock(dir))) /* Duplicate directory lock */
  goto e5;

 AddTail(&ToolList,tn); /* Add node to list */
 tn->tn_ID=++ActTools;  /* Increment tool count */
 return(TRUE);

e5: RemoveAppMenuItem(tn->tn_MenuItem);
e4: if (tn->tn_RealName) free(tn->tn_RealName);
e3: free(tn->tn_Name);
e2: free(tn);
e1: return(FALSE);
}

/* Print status line into window */
void PrintActTools(void)
{
 MyText.IText[14]=ActTools/10+'0'; /* Hack */
 MyText.IText[15]=ActTools%10+'0';
 PrintIText(MyWindow->RPort,&MyText,15,25);
}

/* Final cleanup routine */
void cleanup(int i)
{
 register struct Message *msg;

 switch(i)
  {
   case 99:
           RemoveAppMenuItem(MyAppMenuItem);
   case  5:RemoveAppIcon(MyAppIcon);
   case  4:RemPort(MyMP);                           /* Remove message port */
           while (msg=GetMsg(MyMP)) ReplyMsg(MyMP); /* Reply all messages */
           DeleteMsgPort(MyMP);
   case  3:CloseLibrary(IconBase);
   case  2:CloseLibrary(WorkbenchBase);
   case  1:break;
  }

 if (i!=99) exit(i); /* error if i!=99 */
 exit(RETURN_OK);    /* all o.k. */
}
