#define VERSION   "3.21"
#define DATENUM   "(09.04.00)"
#define DATETEXT  "9 April, 2000"
#define COPYRIGHT "Copyright © 1996 John Hughes, © 1999-2000 Simon Tyrrell"

/* THIS VERSION REQUIRES EXEC.LIBRARY V44  ie, WB3.5 */

//#define DEBUG 1

/*********************************************************************************/

#include <exec/types.h>
#include <clib/dos_protos.h>
#include <stdio.h>
#include <stdlib.h>
#include <utility/tagitem.h>    /* TAG_DONE */
#include <dos/exall.h>
#include <clib/alib_protos.h>   /* List */
#include <clib/exec_protos.h>   /* AllocVec() */
#include <string.h>
#include <exec/memory.h>        /* MEMF_PUBLIC */
#include <workbench/startup.h>
#include <dos/dostags.h>
#include <prefs/wbpattern.h>

// new headers
#include <workbench/icon.h>

#include "WBStartup+.h"

#include <exec/execbase.h>

#include <proto/dos.h>
#include <proto/icon.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/commodities.h>
#include <proto/utility.h>
#include <proto/input.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/iffparse.h>
#include <proto/layers.h>

#include "ReadKeyboard.h"
#include "ProgressWindow.h"


#include <proto/wbstart.h>
struct Library *WBStartBase;


#include <exec/libraries.h>

#include <clib/wb_protos.h>
#include <pragmas/wb_pragmas.h>

char WBSTARTUPPLUS[] = "WBStartup+";

char const version[]="\0$VER: WBStartup+ "VERSION" "DATENUM"\r\n"COPYRIGHT"\r\njohughes@heartland.bradley.edu\r\nbillyfish@ukonline.co.uk\r\nThis is the OS3.x version.\0";

extern struct ExecBase *SysBase;
extern struct Library * WorkbenchBase;

struct IntuitionBase *IntuitionBase;
struct Library *CxBase, *IconBase, *UtilityBase, *GadToolsBase, *IFFParseBase, *DataTypesBase, *LayersBase;
struct Device *InputBase;
struct GfxBase *GfxBase;

main (int argc, char **argv)
{
  struct List filenamequeue;
  struct WBStartupPrefs prefs = {NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  APTR   filenamePool;

  if (OpenLibraries ())
    {                      
      if (filenamePool = CreatePool (MEMF_PUBLIC, 31, 31))  /* Node is 14 bytes, largest filename is 30 */
        {
          struct Screen *scr_p = NULL;
          GetArguments ((long) argc, (UBYTE **) argv, &prefs);

          // check to see which launching method to use
          if (prefs . UseWBStart)
            { // use WBStart library
              if (! (WBStartBase = OpenLibrary ("wbstart.library", 0L)))
                {
                  ShowRequester ("Couldn't open WBStart library. Using Workbench instead");
                }
            }

          if (prefs . ShowWindow)
            {
              if (! (scr_p = LockPubScreen (prefs . PubScreenName)))
                prefs . ShowWindow = FALSE;           // couldn't lock screen so can't show window
            }

          if (!IsQualifierDepressed (&prefs, scr_p))
            {
              //DisplayVars();

              if (CheckSemaphore())
                {

                  NewList (&filenamequeue);

                  GetFilenames (&filenamequeue, filenamePool, prefs . ExecutePath, prefs . ShowWindow, prefs . OnlyIcons);
                 
                  if (!IsListEmpty (&filenamequeue))
                    {
                      if (scr_p)
                        RunPrograms (&filenamequeue, &prefs, scr_p);
                      else
                        RunPrograms (&filenamequeue, &prefs, NULL);
                    }

                  if (prefs . ShowWindow)
                    {
                      FreeIcons (&filenamequeue);
                      UnlockPubScreen (NULL, scr_p);
                    }
                }
            }
          DeletePool (filenamePool);
        }
      if (WBStartBase)
        CloseLibrary (WBStartBase);

      CloseLibraries ();
    }
  else
    ShowRequester ("This is the Amiga OS 3.5 version of WBStartup+\n please install the 2.8 version of WBStartup+.");

  exit (0);
}

void GetFilenames(struct List *filenamequeue, APTR memPool, char *directory, BOOL ShowWindow, BOOL OnlyIcons)
{
  struct ExAllControl *myexallctrl;  /* The ExAllControl structure */
  struct ExAllData    *eadptr;       /* temp pointer to data structure */
  BPTR mylock;                       /* Lock on the directory */
  int more;                          /* Are there more entries ? */
  struct ExAllData ExAllBuf[20];     /* hold the filenames and types */
  struct Node *node;                 /* hold the filenames and proiority for the List */  /* Is actually WBStartupNode */
  struct DiskObject *diskobj;        /* Pointer to the icon info */
  STRPTR toolstring;                  /* Pointer to a tooltype string */
  char pathname[500];                /* path and filename of the icon */
  char filename[32];                 /* parsed match string 2*length+2=22 */

  if (mylock=Lock(directory,SHARED_LOCK))
    {
      if (myexallctrl=(struct ExAllControl *)AllocDosObjectTags(DOS_EXALLCONTROL,TAG_END))
        {
          myexallctrl->eac_LastKey = 0;
          myexallctrl->eac_Entries = 0;
          myexallctrl->eac_MatchString = NULL;
          myexallctrl->eac_MatchFunc = NULL;

          do   /* go until more==0 */
            {
              more = ExAll(mylock, ExAllBuf, sizeof(ExAllBuf), ED_TYPE, myexallctrl);
              if ((!more) && (IoErr() != ERROR_NO_MORE_ENTRIES))
                {
                  ShowRequester("ExAll failed abnormally");//: %ld.", IoErr());
                  break;
                }

              if (myexallctrl->eac_Entries)
                {
                  eadptr=ExAllBuf;

                  while (eadptr)
                    {
                      if (eadptr->ed_Type < 0)  /* make sure it is a file, not a directory, see end of dos/dosextens.h */
                        {
                          strcpy(filename, eadptr->ed_Name);

                          /* truncate the filename if it is an icon, ie. remove the ".info" */
                          if (!Stricmp(filename+strlen(filename)-5,".info"))
                            filename[strlen(filename) - 5] = NULL;  /* take the .info off of the string */
                          else
                            {
                              // new
                              // if ONLYICONS is set don't run iconless files

                              if (OnlyIcons)
                                filename [0] = '\0';
                              else
                                filename[strlen(filename)] = NULL;  /* take the .info off of the string */
                             }

                          /* See if the file is already in the list */

                          if (filename [0] != '\0')
                            {
                              if (!FindNameNoCase(filenamequeue,filename))
                                {
                                  if (Stricmp(filename,WBSTARTUPPLUS))  /* Make sure we don't recursively run ourself */
                                    {
                                      strcpy(pathname,directory);
                                      AddPart(pathname, filename, 500);
                                      if (diskobj=GetDiskObjectNew(pathname))
                                        {
                                          if ((diskobj->do_Type==WBPROJECT) || (diskobj->do_Type==WBTOOL))  /* Make sure we got an icon that isn't a drawer icon */
                                            {
                                              if (node=(struct Node *)AllocPooled(memPool,sizeof(struct WBStartupNode)))
                                                {
                                                  if (node->ln_Name=(char *)AllocPooled(memPool, strlen(pathname)+1))
                                                    {
                                                      STRPTR *tooltypes_p = (STRPTR *) diskobj->do_ToolTypes;

                                                      strcpy(node->ln_Name, filename);

                                                      if (toolstring = FindToolType (tooltypes_p, (STRPTR) "STARTPRI"))
                                                        node->ln_Pri=atoi((char *)toolstring);
                                                      else
                                                        node->ln_Pri=0;

                                                      ((struct WBStartupNode *)node)->StackSize = diskobj->do_StackSize;

                                                      if (toolstring=FindToolType(diskobj->do_ToolTypes,"TOOLPRI"))
                                                        ((struct WBStartupNode *)node)->toolpri = strtol(toolstring,NULL,10);
                                                      else
                                                        ((struct WBStartupNode *)node)->toolpri = 0;

                                                      if (toolstring=FindToolType(diskobj->do_ToolTypes,"WAIT"))
                                                        ((struct WBStartupNode *)node)->wait = strtoul(toolstring,NULL,10);
                                                      else
                                                        ((struct WBStartupNode *)node)->wait = 0;

                                                      if (ShowWindow)
                                                        ((struct WBStartupNode *)node)->diskobj = diskobj;
                                                      else
                                                        FreeDiskObject(diskobj);

                                                      Enqueue(filenamequeue,node);
                                                    }
                                                }
                                            }
                                          else
                                            FreeDiskObject(diskobj);
                                        }
                                    }
                                }
                            }
                        }
                      eadptr=eadptr->ed_Next;
                    }
                }
            } while (more);

          FreeDosObject(DOS_EXALLCONTROL,myexallctrl);
        }
      else
        ShowRequester("Not enough memory for ExAllControl.");

      UnLock(mylock);
    }
  else
    ShowRequester("Couldn't lock directory.");
}


void RunPrograms(struct List *filenamequeue, struct WBStartupPrefs *prefs, struct Screen *scr_p)
{
  struct Node *node;           /* used to go through the filenames queue */
  BPTR fl;
  BPTR olddirlock;
  LONG numprograms=0,currentprogram=0;      // used for the progress bar
  struct ProgressWindowData *windata=NULL;  // this must be initialized to NULL!

  if (prefs->ShowWindow)
    {
      /* Count the # of programs to run */
      for (node=filenamequeue->lh_Head; node->ln_Succ; node = node->ln_Succ)
        numprograms++;
      /* Open and set up the Progress Window */
      windata=CreateProgressWindow(prefs, scr_p);
    }

  fl=Lock(prefs->ExecutePath,SHARED_LOCK);
  olddirlock=CurrentDir(fl);

  for (node=filenamequeue->lh_Head; node->ln_Succ; node=node->ln_Succ)
    {
      struct DiskObject *disk_obj_p = ((struct WBStartupNode *) node) -> diskobj;
      BOOL success;
      BOOL run_program = TRUE;    // for interactive usage

      currentprogram++;

      /* Update Progress Bar in Window */
      if (windata)
        {
          LayoutIconA (disk_obj_p, scr_p, NULL);

          ShowIconImage(windata,((struct WBStartupNode *)node)->diskobj);
          DisplayProgramName(windata,node->ln_Name, (prefs->Interactive) ? TRUE : FALSE);

          /* Interactive Mode */
          if (prefs->Interactive)
            run_program = InteractiveRunProgram (windata -> win);

          UpdateProgressBar (windata -> win, currentprogram, numprograms, prefs -> FillGaugeType);
        }


      if (run_program)
        {
          if (prefs -> UseWBStart)
            {
              if (WBStartTags (WBStart_Name, node->ln_Name, WBStart_DirectoryLock, fl, TAG_DONE) == RETURN_OK)
                success = TRUE;
              else
                success = FALSE;
            }
          else
            {
              success = OpenWorkbenchObjectA (node -> ln_Name, NULL);
            }

          if (!success)
            {
              const char *message = "Couldn't run ";
              const long length = strlen (node -> ln_Name) + 1 + strlen (message);

              char *string_p = (char *) malloc (length);

              if (string_p)
                {
                  strcpy (string_p, message);
                  strcat (string_p, node -> ln_Name);
                  string_p [length - 1] = '\0';

                  ShowRequester (string_p);

                  free (string_p);
                }
            }

          /* Wait the amount of time specified in the WAIT= tooltype */
          if (((struct WBStartupNode *)node)->wait)
            Delay(((struct WBStartupNode *)node)->wait*50);
        }
    }

  /* Free resources */
  CurrentDir(olddirlock);
  UnLock(fl);

  if (windata)
    CloseProgressWindow(windata);

  /* All OK! */
  return;
}

void GetArguments (long argc, STRPTR *argv, struct WBStartupPrefs *prefs)
{
  UBYTE **ttypes;
  UBYTE *toolstring;

  ttypes = ArgArrayInit (argc, (UBYTE **) argv);

  strcpy ((STRPTR) (prefs -> ExecutePath), (const char *) ArgString ((STRPTR *) ttypes, (STRPTR) "ENABLEDPATH", (STRPTR) "SYS:WBStartup/Enabled"));
  strcpy (prefs -> StoragePath, ArgString (ttypes, "DISABLEDPATH", "SYS:WBStartup/Disabled"));
  prefs->ShowWindow = ((FindToolType(ttypes,"PROGRESSWINDOW")) ? TRUE : FALSE);
  prefs->OnlyIcons = ((FindToolType(ttypes,"ONLYICONS")) ? TRUE : FALSE);
  prefs -> UseWBStart = ((FindToolType(ttypes,"USEWBSTART")) ? TRUE : FALSE);
  prefs->BackgroundFilename[0]=NULL;
  if (toolstring=FindToolType(ttypes,"BACKGROUNDPIC"))
  {
    if (MatchToolValue(toolstring,"WORKBENCH"))
      prefs->BackgroundType=WORKBENCH;
    else if (MatchToolValue(toolstring,"SCREEN"))
      prefs->BackgroundType=SCREEN;
    else if (MatchToolValue(toolstring,"WINDOWS"))
      prefs->BackgroundType=WINDOWS;
    else if (MatchToolValue(toolstring,"NONE"))
      prefs->BackgroundType=NONE;
    else
    {
      strncpy(prefs->BackgroundFilename,toolstring,200);
      prefs->BackgroundFilename[strlen(toolstring)]=NULL;
      prefs->BackgroundType=USERDEFINED;
    }
  }
  else
  {
    prefs->BackgroundType=WINDOWS;
  }

  // NEW

  if (toolstring=FindToolType(ttypes,"FILLBAR"))
    {
      if (MatchToolValue(toolstring,"BACKGROUND"))
        prefs->FillGaugeType = BACKGROUNDPEN;
      else if (MatchToolValue(toolstring,"FILL"))
        prefs->FillGaugeType = FILLPEN;
      else if (MatchToolValue(toolstring,"HIGHLIGHTTEXT"))
        prefs->FillGaugeType = HIGHLIGHTTEXTPEN;
      else if (MatchToolValue(toolstring,"SHINE"))
        prefs->FillGaugeType = SHINEPEN;
      else if (MatchToolValue(toolstring,"SHADOW"))
        prefs->FillGaugeType = SHADOWPEN;
    }

  strcpy(prefs->PrefsPath , ArgString(ttypes,"PREFSPATH","SYS:Prefs/WBStartup+Prefs"));
  strcpy(prefs->PubScreenName , ArgString(ttypes,"PUBSCREEN","Workbench"));

  strcpy (prefs -> RunPrefsQual, ArgString(ttypes,"RUNPREFS","LEFTBUTTON"));
  strcpy (prefs -> InteractiveQual, ArgString(ttypes,"INTERACTIVE","MIDBUTTON"));
  strcpy (prefs -> DisableQual, ArgString(ttypes,"DISABLE","RBUTTON"));

  ArgArrayDone();
}

void ShowRequester(STRPTR RequesterText)
{
  struct EasyStruct myRequestStruct={
    sizeof (struct EasyStruct ),
    0,
    WBSTARTUPPLUS,
    NULL,
    "Ok"};

    myRequestStruct.es_TextFormat=RequesterText;
    EasyRequestArgs(NULL,&myRequestStruct,NULL,NULL);
}

/*
#include <dos/dosextens.h>
#include <dos/var.h>
void DisplayVars(void)
{
  struct Process *pr;
  struct LocalVar *node;
  char   buffer[1000];
  BPTR file;

  if (file=Open("CON:////",MODE_NEWFILE))
  {
    Write(file,"These are vars in WBStartup+:\n",30);
    if (pr = (struct Process *)FindTask(NULL))
    {
      for (node = (struct LocalVar *)pr->pr_LocalVars.mlh_Head; node->lv_Node.ln_Succ; node = (struct LocalVar *)node->lv_Node.ln_Succ)
      {
        sprintf(buffer,"%s\n",node->lv_Node.ln_Name);
        Write(file,buffer,strlen(buffer));
      }
      Delay(50*5);
    }
    Close(file);
  }
}

*/


void FreeIcons(struct List *list)
{
  struct Node *node;

  for (node=list->lh_Head; node->ln_Succ; node=node->ln_Succ)
    FreeDiskObject(((struct WBStartupNode *)node)->diskobj);
}

void RunPrefs(struct WBStartupPrefs *prefs, struct Screen *scr_p)
{
  struct WBStartupNode node;
  struct List list;
  struct MsgPort *mp;
  struct WBStartupPrefs newprefs;
  char filename[35];
  long length;

  /* Set up filename list, which only consists of the WBStartup+Prefs program */
  NewList(&list);
  node.diskobj=NULL;
  node.StackSize=8192;
  node.wait=0;
  node.toolpri=0;
  node.node.ln_Name=filename;
  strcpy(filename,FilePart(prefs->PrefsPath));
  {
    struct Node *my_node_p = (struct Node *) (&node);
    AddHead(&list, my_node_p);
  }

  /* Set up New Prefs structure */
  //strncpy (newprefs . ExecutePath, prefs -> PrefsPath, PathPart (prefs -> PrefsPath) - prefs -> PrefsPath);

  length = strlen (prefs -> PrefsPath) - strlen (PathPart (prefs -> PrefsPath));

  strncpy (newprefs . ExecutePath, prefs -> PrefsPath, length);

  newprefs . ExecutePath [length] = '\0';

  /*
  ShowRequester (prefs -> PrefsPath);
  ShowRequester (filename);
  ShowRequester (newprefs . ExecutePath);
  */

  newprefs.StoragePath[0]=NULL;
  newprefs.ShowWindow=FALSE;
  newprefs.Interactive=FALSE;
  newprefs.PrefsPath[0]=NULL;

  RunPrograms(&list,&newprefs, scr_p);

  if (mp=CreatePort(WBSTARTUPPLUS,0))
  {
    Wait( (1 << mp->mp_SigBit) | SIGBREAKF_CTRL_C);   /* Wait for WBStartup+Prefs to complete OR a CTRL-C */
    DeletePort(mp);
  }
}

struct Node *FindNameNoCase(struct List *list, char *name)
{
  struct Node *node;
  struct Node *result=NULL;

  for (node=list->lh_Head; node->ln_Succ; node = node->ln_Succ)
    if (!Stricmp(name,node->ln_Name))
    {
      result = node;
      break;
    }
  return(result);
}

BOOL CheckSemaphore(void)
{
  /* RETURN TRUE if this is the 1st time WBStartup+ has been run, OR if the user says it is ok to run it again */
  struct SignalSemaphore *sema;
  char   *name;
  BOOL ContinueRunning = TRUE;

  Forbid();
  if (!FindSemaphore(WBSTARTUPPLUS))
  {
    if (name=(char *)AllocMem(11,MEMF_PUBLIC))
    {
      strcpy(name,WBSTARTUPPLUS);
      if (sema=(struct SignalSemaphore *)AllocMem(sizeof(struct SignalSemaphore),MEMF_PUBLIC|MEMF_CLEAR))
      {
        sema->ss_Link.ln_Name=name;
        AddSemaphore(sema);
      }
    }
    Permit();
  }
  else  /* Semaphore was found, meaning WBStartup+ has already been run */
  {
    struct EasyStruct myRequestStruct={
      sizeof (struct EasyStruct ),
      0,
      WBSTARTUPPLUS,
      "Run WBStartup+ again?",
      "Ok|Cancel"};

    ContinueRunning = (BOOL)EasyRequestArgs(NULL,&myRequestStruct,NULL,NULL);

    Permit();
  }

  return(ContinueRunning);
}

UBYTE OpenLibraries (void)
{
  if (SysBase -> LibNode.lib_Version>=39)
    {
      if (WorkbenchBase -> lib_Version >= 44)
        {
          if (IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 37))
            {
              if (CxBase = OpenLibrary("commodities.library", 37L))
                {
                  if (IconBase = OpenLibrary("icon.library", 44L))
                    {
                      if (UtilityBase = OpenLibrary("utility.library", 37L))
                        {
                          if (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37L))
                            {
                              if (GadToolsBase = OpenLibrary("gadtools.library", 37L))
                                {
                                  if (IFFParseBase = OpenLibrary("iffparse.library", 37L))
                                    {
                                      if (DataTypesBase=OpenLibrary("datatypes.library",39L))
                                        {
                                          if (LayersBase=OpenLibrary("layers.library",37L))
                                            {
                                              return TRUE;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
  CloseLibraries ();

  return FALSE;
}

void CloseLibraries (void)
{
  CloseLibrary (DataTypesBase);
  CloseLibrary (IFFParseBase);
  CloseLibrary ((struct Library *) GfxBase);
  CloseLibrary (GadToolsBase);
  CloseLibrary (UtilityBase);
  CloseLibrary (IconBase);
  CloseLibrary (CxBase);
  CloseLibrary ((struct Library *) IntuitionBase);
}

