/*********************************************************************\
**                               ________________________________    **
**    A n t h o n y             |________    __    __    ________|   **
**                                       |  |o_|  |o_|  |            **
**            T h y s s e n            __|   __    __   |__          **
**                                  __|   __|  |  |  |__   |__       **
**   `` Dragon Computing ! ''    __|   __|     |  |     |__   |__    **
**                              |_____|        |__|        |_____|   **
**                                                                   **
\*********************************************************************/

#include "AMenu.h"
#include "AllocStr.h"
#include <libraries/DosExtens.h>  /* for Process definition */
#include <Workbench/Startup.h>    /* for WBStartup definition */
#include <Workbench/WorkBench.h>  /* for DiskObject definition */
#include <Proto/Icon.h>
#include <string.h>

extern struct Process *MyProc;         /* This Process */
extern BPTR            WorkDirLock;    /* The current directory */

extern void Warn(char *);         /* routine to notify user of error */

/*-----------------------------------------------------*/

static void
RunCLI( EItem )
  struct ExtMenuItem *EItem;
/* execute a CLI command using the appropiate console
** for command (either a default or commands own).
*/
{
  struct ProcessControlBlock *PCB=NULL; /* structure for Arp's ASynchRun */
  BPTR    NewDirLock=NULL;              /* Directory Lock for CD */
  long    CLI=0;                        /* result code - CLI number used */
  char    buf[256], *ConWin, *Error;

  DB( FPrintf(DBWindow, "CLIRun -> `%s'\n", EItem->Cmd); )

    /* is new Directory Update the directory */
  if( EItem->Directory ) {
    if( !( NewDirLock = Lock(EItem->Directory, ACCESS_READ) ) ) {
      SPrintf(buf, "Unable to Find Directory `%s' ", EItem->Directory);
      Warn(buf);  return;
    }
    CurrentDir(NewDirLock);
  }


  PCB = (struct ProcessControlBlock *)
     AllocMem( sizeof(struct ProcessControlBlock), MEMF_CLEAR|MEMF_PUBLIC);

  if( !PCB )
    CLI = PR_NOMEM;                /* failed to allocate PCB struct */
  else {
     /* allocate appropiate stack and prioity */
    PCB->pcb_StackSize = EItem->Stack;       /* Stack */
    PCB->pcb_Pri       = EItem->Pri;         /*  Pri  */

    ConWin = ( EItem->Console ? EItem->Console : M->Console );
    switch( EItem->Mode ) {
    case 'C':                              /* CLI mode - Open a Console */
      PCB->pcb_Control  =  PRF_STDIO;
      PCB->pcb_Console.pcb_ConName = (BYTE *) ConWin;
      break;
    case 'R':                              /* RunBack - No File Handles */
      PCB->pcb_Control = PRF_SAVEIO;
      break;
    case 'S':                              /* Script - New Shell */
      PCB->pcb_Control = INTERACTIVE_SHELL;
    }

    DB( FPrintf(DBWindow, "-- ASynchRun()\n"); )
    CLI = ( EItem->Mode == 'S' )
          ? ASyncRun( ConWin, EItem->Cmd, PCB)       /* interactive shell */
          : ASyncRun( EItem->Cmd, EItem->Args, PCB); /* run command */
  }

    /* Handle Errors */
  if( CLI<0 ) {       /* negitive result -- Report Appropiate Error */
    switch( CLI ) {
    case PR_NOSTDIO:  Error = "%s: Unable to open Console"; break;
    case PR_NOFILE:   Error = "%s: Unknown Command `%s'"; break;
    case PR_NOMEM:    Error = "%s: Not Enough Memory"; break;
    case PR_NOSLOT:   Error = "%s: Task Table Full"; break;
    case PR_NOINPUT:
    case PR_NOOUTPUT: Error = "%s: IO Error on Console"; break;
    case PR_SCRIPT:   Error = "%s: Script run using wrong mode"; break;
    default:          Error = "%s: ASynchRun Failed on `%s' (%ld)";
    }
    SPrintf(buf, Error, VERSION, EItem->Cmd, CLI);
    Warn(buf);
  }

  if( PCB )
    FreeMem( PCB, sizeof(struct ProcessControlBlock) );
  if( EItem->Directory )            /* Return to the Working Directory */
    UnLock( CurrentDir( WorkDirLock ) );
  DB( FPrintf(DBWindow, "---- Done ----\n"); )
}


/*--------------------------------------------------*/
/* procedures to support running WorkBench programs */


extern                   WBProcesses;  /* WB processes currently running */
extern struct MsgPort   *WBReplyPort;  /* Port for Process to reply to */


void
WBFree( StartMsg )
  struct WBStartup *StartMsg;
/*  Free up space used by a workbench startup message.  Called whenever
 *  a workbench program replies to the startup message, and whenever
 *  `WBRun()' gets an error
 */
{
  register int i;

  if( StartMsg ) {
    if( WBProcesses ) /* decrement number of processes */
      WBProcesses--;
    if( StartMsg->sm_ArgList ) {
      for (i=0; i<StartMsg->sm_NumArgs; i++) {
        if( StartMsg->sm_ArgList[i].wa_Lock )
          UnLock(StartMsg->sm_ArgList[i].wa_Lock);
        if( StartMsg->sm_ArgList[i].wa_Name )
          FreeStr(StartMsg->sm_ArgList[i].wa_Name);
      }
      FreeMem( StartMsg->sm_ArgList,
                 sizeof(struct WBArg) *StartMsg->sm_NumArgs );
    }
    if( StartMsg->sm_Segment )
      UnLoadSeg(StartMsg->sm_Segment);
    if( StartMsg->sm_ToolWindow )
      FreeStr(StartMsg->sm_ToolWindow );
    FreeMem( StartMsg , sizeof(struct WBStartup) );
  }
}


static void
RunWB( EItem )
  struct ExtMenuItem *EItem;
/* load and run a workbench program
 * This routine is errored in the following ways
 *   1/ No ToolTypes are handled
 *   2/ The TOOL for a PROJECT is expected to have an icon (it may not!).
 *   3/ Current Directory is that of the TOOL not the PROJECT or
 *   4/ User can't select the Current Directory to use (should he?).
 *                                                      -- Anthony Thyssen
 */
{
  BPTR                DirLock = NULL, OldLock = NULL, IconLock;
  struct WBStartup   *StartMsg = NULL;
  struct DiskObject  *DiskObject = NULL;
  char               *Name, buf[128];
  BOOL                Error = TRUE;      /* assume the worst */
  int                 I;

    /* Lock Projects Directory */
  Name = BaseName(EItem->Cmd);
  strcpy(buf, EItem->Cmd);
  strcat(buf, ".info");  /* this will be present the project may not! */
  if( !(IconLock = Lock(buf, ACCESS_READ) ) ) {
    SPrintf(buf, "Can't locate `%s.info'", Name);
    goto finish;
  }
  DirLock = ParentDir(IconLock);
  UnLock(IconLock);
  if( !DirLock ) {
    SPrintf(buf, "Directory invalid for `%s'", EItem->Cmd);
    goto finish;
  }

    /* Load Icon */
  OldLock = CurrentDir( DirLock );
  if( !(DiskObject = GetDiskObject(Name)) ) {
    SPrintf(buf, "Can't locate `%s.info'", Name);
    goto finish;
  }
  switch( DiskObject->do_Type ) {
  case WBTOOL:    I=1; break; /* just the tool */
  case WBPROJECT: I=2; break; /* tool & project */
  default:
    SPrintf(buf, "`%s.info' is not a TOOL or PROJECT!", Name);
    goto finish;
  }

    /* allocate the startup message */
  if( !(StartMsg = (struct WBStartup *)
           AllocMem( sizeof(struct WBStartup), MEMF_PUBLIC|MEMF_CLEAR))) {
    SPrintf(buf, "%s: Not Enough Memory", VERSION);
    goto finish;
  }
  StartMsg->sm_NumArgs = I;
  if( !(StartMsg->sm_ArgList = (struct WBArg *)
         AllocMem( sizeof(struct WBArg)*I, MEMF_PUBLIC|MEMF_CLEAR)) ) {
    SPrintf(buf, "%s: Not Enough Memory", VERSION);
    goto finish;
  }

  I--; /* Fill in the Last WBArg the TOOL/Project we referenced */
  StartMsg->sm_ArgList[I].wa_Lock = DirLock;
  StartMsg->sm_ArgList[I].wa_Name = AllocStr(Name);

  if( I ) {   /* if WBPROJECT - Fill out the Tool */
    StartMsg->sm_ArgList->wa_Lock = DupLock(DirLock); /* same as project */
    StartMsg->sm_ArgList->wa_Name = AllocStr(DiskObject->do_DefaultTool);
    Name = StartMsg->sm_ArgList->wa_Name;    /* Set the new name */
  }

    /* initialize rest of startup message */
  StartMsg->sm_Message.mn_ReplyPort = WBReplyPort;
  StartMsg->sm_Message.mn_Length = sizeof(struct WBStartup);
  StartMsg->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
  StartMsg->sm_ToolWindow = AllocStr(DiskObject->do_ToolWindow);

    /* Insure a decent stack size, some progs have this set to zero */
  if( DiskObject->do_StackSize < EItem->Stack )
    DiskObject->do_StackSize = EItem->Stack;


    /* load in the program */
  if( !(StartMsg->sm_Segment = LoadPrg(Name)) ) {
    SPrintf(buf, "Unable to Load `%s'", Name);
    goto finish;
  }

  /* create process */
  if( !(StartMsg->sm_Process = CreateProc( Name, (LONG)EItem->Pri,
          StartMsg->sm_Segment, DiskObject->do_StackSize)) ) {
    SPrintf(buf, "Can't execute `%s'", Name);
    goto finish;
  }

    /* everything's ok -- start 'er up */
  PutMsg( StartMsg->sm_Process , (struct Message *)StartMsg );
  Error = FALSE;
  WBProcesses++;        /* keep track of unreplied startup messages */

finish:
  if( DiskObject ) FreeDiskObject( DiskObject );
  if( OldLock )    CurrentDir( OldLock );
  if( Error ) {
    if( StartMsg ) WBFree( StartMsg );
    Warn(buf);
  }
}


/*-------------------------------------------------*/

void
RunItem( EItem )
  struct ExtMenuItem *EItem;
{
  switch( EItem->Mode ) {
  case 'R':
  case 'C':
  case 'S':  RunCLI( EItem ); break;
  case 'W':  RunWB( EItem ); break;
  }
}

