/*
*   XCMDLaser
*
*       XCMD for Term II, allowing the download of PostScript files to
*       an Apple Laser Writer. We are using some functions of the
*       Laser Writer :
*
*           - a ^D must be sent at the end of each file
*           - the printer send a ^D when current job is
*             finished, and a new file can be processed
*
*       To compile with DICE :
*
*           dcc XCMDTools.o XCMDLaser.c -o XCMDLaser
*
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/asl_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/alib_protos.h>
#include <string.h>
#include <stdio.h>
#include "XCMD.h"
#include "XCMDtools.h"



/*
*   DEFINE
*/
#define BUF_SIZE 80
#define BOX 50
#define TIMEOUT_LIMIT 180     /* in seconds */

/*
*   VARIABLES
*/
struct MsgPort *port = NULL;
struct XCMD *XCMD = NULL;
FILE *fp = NULL;
const char PRINTER_EOF  = '\x04';
const char INTERRUPT    = '\x03';
const char STATUS_QUERY = '\x14';
struct FileRequester *FileRequester = NULL;
struct AslBase *AslBase = NULL;
struct Library *GadToolsBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct GadgetList *glist = NULL;
struct Window *window = NULL;
struct Screen *screen = NULL;
void *vi = NULL;
struct Gadget *GadName, *GadStop;;
struct TextAttr Topaz80 = { "topaz.font",8,0,0 };

/*
*   PROTOS
*/
void End(char *);
void DownloadFile(char *);
void ExecuteXCMD(char *, void *, void *, void *);
void InitIntuition(void);
long FileSize(char *);
void HandleIntuition(void);
void UpdateBox(long,long);
void exit(int);

/*
*   MAIN
*/
int main(int argc, char *argv[])
  {

  char buffer_name[256];

  /*
  *  Allocate and initialize ressources
  */
  if(!FindPort("TERM"))
    End("I can't find Term II");
  port = CreatePort("LASER",0);
  if(!port)
    End("I can't create my message port");
  XCMD = CreateXCMD(port);
  if(!XCMD)
    End("I can't create the XCMD");
  AslBase = OpenLibrary(AslName,36);
  if(!AslBase)
    End("I can't find library asl v36 or above");
  struct FileRequester *FileRequester = AllocAslRequest(ASL_FileRequest,NULL);
  if(!FileRequester)
    End("I can't create a file requester");
  InitIntuition();


  /*
  *  Get files and print them
  */
  if(AslRequestTags(FileRequester,ASL_FuncFlags,FILF_MULTISELECT|FILF_PATGAD,
    ASL_Pattern,(ULONG)"#?",
    ASL_Hail,(ULONG)"Multiselect",
    TAG_DONE))
    {
    int nb_files = FileRequester->rf_NumArgs;
    if(nb_files)
      {
      struct WBArg *wb_arg = FileRequester->rf_ArgList;
      while(nb_files--)
        {
        NameFromLock(wb_arg->wa_Lock,buffer_name,256);
        AddPart(buffer_name,wb_arg->wa_Name,256);
        DownloadFile(buffer_name);
        wb_arg++;
        }
      }
    else
      {
      if(FileRequester->rf_File[0] != '\0')
        {
        strcpy(buffer_name,FileRequester->rf_Dir);
        AddPart(buffer_name,FileRequester->rf_File,256);
        DownloadFile(buffer_name);
        }
      }
    }
  End("Done");
  }




/*
*   void End(char *message)
*
*       End program and free ressources
*/
void End(char *message)
  {
  puts(message);
  if(XCMD)      /* This to be sure to make Term II read the serial port */
    {
    /*
    *   We don't use ExecuteXCMD(), for in case of
    *   error we'll be back here for an infinite loop
    */
    XCMD->xcmd_Command = "serial_on";
    if(SendXCMD(XCMD))
      {
      WaitPort(port);
      GetMsg(port);
      }
    }
  if(fp) fclose(fp);
  if(XCMD) DeleteXCMD(XCMD);
  if(port) DeletePort(port);
  if(FileRequester) FreeAslRequest(FileRequester);
  if(AslBase) CloseLibrary(AslBase);
  if(window)
    {
    struct IntuiMessage *msg;
    while(msg = GT_GetIMsg(window->UserPort)) GT_ReplyIMsg(msg);
    CloseWindow(window);
    }
  if(vi) FreeVisualInfo(vi);
  if(glist) FreeGadgets(glist);
  if(screen) UnlockPubScreen(NULL,screen);
  if(GadToolsBase) CloseLibrary(GadToolsBase);
  if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
  exit(0);
  }



/*
*   void DownloadFile(char *filename)
*
*       Download the file whose name is filename
*/
void DownloadFile(char *filename)
  {
  unsigned char write_buffer[BUF_SIZE];
  char intui_buffer[60];
  long file_size = FileSize(filename);
  long size = 0;
  sprintf(intui_buffer,"Downloading %s (%d bytes)",FilePart(filename),file_size);
  GT_SetGadgetAttrs(GadName,window,NULL,GTTX_Text,intui_buffer,TAG_DONE);
  fp = fopen(filename,"r");
  if(!fp)
    End("I can't open file");
  char c;
  int i = 0;
  while((c=getc(fp)) != EOF)
    {
    write_buffer[i] = c;
    i++;
    if(i == BUF_SIZE)
      {
      ExecuteXCMD("xcmd_swrite",write_buffer,(void *)BUF_SIZE,NULL);
      size += BUF_SIZE;
      HandleIntuition();
      UpdateBox(size,file_size);
      i = 0;
      }
    }
  if(i>0)
    ExecuteXCMD("xcmd_swrite",write_buffer,(void *)i,NULL);
  /*
  *  We get sure Term II will read chars only when we
  *  ask for it.
  */
  ExecuteXCMD("serial_off",NULL,NULL,NULL);
  ExecuteXCMD("xcmd_swrite",&PRINTER_EOF,(void *)1,NULL);
  size+=i;
  UpdateBox(size,file_size);
  /*
  *     Wait for a ^D. See time out value
  */
  BOOL not_the_end = TRUE;
  while(not_the_end)
    {
    HandleIntuition();
    int i;
    for(i=0; i<TIMEOUT_LIMIT; i++)
      {
      char eof;
      ExecuteXCMD("xcmd_sread",&eof,(void *)1,(void *)1000000);
      if(((long)XCMD->xcmd_Args[15])>0)
        {
        if(eof == PRINTER_EOF)
          {
          not_the_end = FALSE;
          break;
          }
        }
      HandleIntuition();
      }
    if(not_the_end) End("No answer from printer");
    }
  ExecuteXCMD("serial_on",NULL,NULL,NULL);
  fclose(fp); fp = NULL;
  GT_SetGadgetAttrs(GadName,window,NULL,GTTX_Text,NULL,TAG_DONE);
  SetAPen(window->RPort,(UBYTE)0);
  RectFill(window->RPort,BOX+4,58,BOX+4+242,70);
  SetAPen(window->RPort,(UBYTE)3);
  }




/*
*   void ExecuteXCMD()
*
*       Execute an XCMD and wait for an answer. Quit in case of
*       error.
*/
void ExecuteXCMD(char *command, void *arg1, void *arg2, void *arg3)
  {
  if(!XCMD) End("No XCMD !");
  XCMD->xcmd_Command = command;
  XCMD->xcmd_Args[0] = arg1;
  XCMD->xcmd_Args[1] = arg2;
  XCMD->xcmd_Args[2] = arg3;
  if(SendXCMD(XCMD))
    {
    WaitPort(port);
    GetMsg(port);
    }
  else
    End("SendXCMD() failed");
  }



/*
* void InitIntuition()
*
*       Init intuition
*/
void InitIntuition()
  {
  ExecuteXCMD("xcmd_lock_request",NULL,NULL,NULL);
  screen = XCMD->xcmd_TermScreen;
  UnLock(XCMD->xcmd_TermDir);

  IntuitionBase = OpenLibrary("intuition.library",36);
  if(!IntuitionBase)
    End("No intuition.library v36 or above");

  GadToolsBase = OpenLibrary("gadtools.library",36);
  if(!GadToolsBase)
    End("No gadtools.library v36 or above");

  vi = GetVisualInfo(screen, TAG_DONE);
  if(!vi)
    End("I can't get visual info");

  window = OpenWindowTags(NULL,
    WA_Top, 20,
    WA_Left, 15,
    WA_DragBar, TRUE,
    WA_DepthGadget, TRUE,
    WA_PubScreen, screen,
    WA_Width, 350,
    WA_Height, 120,
    WA_IDCMP, REFRESHWINDOW|TEXTIDCMP|BUTTONIDCMP,
    WA_Title, "Downloading PostScript files (v1.0)",
    WA_Activate, TRUE,
    TAG_DONE);
  if(!window)
    End("I can't open the window");

  struct Gadget *gad = CreateContext(&glist);
  struct NewGadget ng;

  ng.ng_TextAttr = &Topaz80;
  ng.ng_VisualInfo = vi;
  ng.ng_LeftEdge = 10;
  ng.ng_TopEdge = 30;
  ng.ng_Width = 260;
  ng.ng_Height = 10;
  ng.ng_Flags = 0;
  ng.ng_GadgetText = NULL;
  gad = GadName = CreateGadget(TEXT_KIND,gad,&ng,TAG_DONE);

  ng.ng_TopEdge += 60;
  ng.ng_LeftEdge = 130;
  ng.ng_Height = 14;
  ng.ng_Width = 100;
  ng.ng_GadgetText = "STOP";
  gad = GadStop = CreateGadget(BUTTON_KIND,gad,&ng,TAG_DONE);

  if(!gad)
    End("I can't create gadgets");

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

  DrawBevelBox(window->RPort,BOX,55,250,20,GT_VisualInfo,vi,GTBB_Recessed,TRUE,TAG_DONE);
  }

/*
* void HandleIntuition()
*
*       Handle Intuition events
*/
void HandleIntuition()
  {
  struct IntuiMessage *msg;
  BOOL stop = FALSE;
  while(msg = GT_GetIMsg(window->UserPort))
    {
    ULONG class = msg->Class;
    struct Gadget *gadget = msg->IAddress;
    GT_ReplyIMsg(msg);
    switch(class)
      {
      case GADGETUP :
        if(gadget == GadStop)
          stop = TRUE;
        break;
      case REFRESHWINDOW :
        GT_BeginRefresh(window);
        GT_EndRefresh(window,TRUE);
        break;
      default:
        break;
      }
    }
  if(stop)
    {
    ExecuteXCMD("xcmd_swrite",&INTERRUPT,(void *)1,NULL);
    End("Stop by user");
    }
  }

/*
* long FileSize(char *)
*
*   Return size in bytes of a file
*/
long FileSize(char *filename)
  {
  BPTR lock = Lock(filename,ACCESS_READ);
  long size = 0;
  if(lock)
    {
    struct FileInfoBlock *fib = AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR|MEMF_PUBLIC);
    if(fib)
      {
      Examine(lock,fib);
      size = fib->fib_Size;
      FreeMem(fib,sizeof(struct FileInfoBlock));
      }
    UnLock(lock);
    }
  return(size);
  }


/*
* void UpdateBox(long current, long size)
*
*   Update the box reflecting current status of downloading.
*       current = nb of bytes already downloaded
*       size    = nb of bytes to download
*/
void UpdateBox(long current, long size)
  {
  long width = (242 * current) / size;
  RectFill(window->RPort,BOX+4,58,BOX+width+4,70);
  }

