/*
*   XCMDLaser
*
*       XCMD pour Term II, permettant le download de fichier PostScript
*       vers une LaserWriter. On utilise les propriétes suivantes de
*       la LaserWriter :
*
*           - émettre un ^T entraine le retour du status de la laser
*           - on doit émettre un ^D en fin de fichier
*           - la laser répond par un ^D quand elle est prête à
*             imprimer un nouveau fichier
*           - émettre un ^C permet d'interrompre un job en cours
*
*       Pour compiler avec 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     /* en secondes */

/*
*   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];

  /*
  *  Initialisation et allocation des ressources
  */
  if(!FindPort("TERM"))
    End("Je n'ai pas trouvé Term II");
  port = CreatePort("LASER",0);
  if(!port)
    End("Impossible de créer mon message port");
  XCMD = CreateXCMD(port);
  if(!XCMD)
    End("Impossible de créer la XCMD");
  AslBase = OpenLibrary(AslName,36);
  if(!AslBase)
    End("Pas de asl.library v36 ou plus");
  struct FileRequester *FileRequester = AllocAslRequest(ASL_FileRequest,NULL);
  if(!FileRequester)
    End("Impossible d'allouer un file requester");
  InitIntuition();


  /*
  *  On sélectionne les fichiers et on les envoie à la laser
  */
  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("Terminé sans erreurs");
  }




/*
*   void End(char *message)
*
*       Termine le programme et libère les ressources
*/
void End(char *message)
  {
  puts(message);
  if(XCMD)      /* Soyant sûr de remettre Term II en lecture */
    {
    /*  On n'utilise pas ExecuteXCMD car en cas
    *   d'erreur on reviendrait ici !
    */
    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 un fichier dont le nom est donné en paramètre
*/
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 octets)",FilePart(filename),file_size);
  GT_SetGadgetAttrs(GadName,window,NULL,GTTX_Text,intui_buffer,TAG_DONE);
  fp = fopen(filename,"r");
  if(!fp)
    End("Impossible d'ouvrir le fichier");
  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);
  /*
  *  On s'assure que Term II ne lira plus le serial device
  *  qu'à notre demande :
  */
  ExecuteXCMD("serial_off",NULL,NULL,NULL);
  ExecuteXCMD("xcmd_swrite",&PRINTER_EOF,(void *)1,NULL);
  size+=i;
  UpdateBox(size,file_size);
  /*
  *     Attendre le ^D de l'imprimante, pendant au maximum
  *     3 minutes
  */
  BOOL pas_fini = TRUE;
  while(pas_fini)
    {
    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)
          {
          pas_fini = FALSE;
          break;
          }
        }
      HandleIntuition();
      }
    if(pas_fini) End("L'imprimante ne répond plus");
    }
  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()
*
*       Cette commande exécute une XCMD, et attend son retour
*       ou quitte le programme en cas d'erreur
*/
void ExecuteXCMD(char *command, void *arg1, void *arg2, void *arg3)
  {
  if(!XCMD) End("Pas de XCMD disponible !");
  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() a échoué");
  }



/*
* void InitIntuition()
*
*    Initialise Intuition, la gadget toolkit, etc...
*/
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("Pas de intuition.library v36 ou plus");

  GadToolsBase = OpenLibrary("gadtools.library",36);
  if(!GadToolsBase)
    End("Pas de gadtools.library v36 ou plus");

  vi = GetVisualInfo(screen, TAG_DONE);
  if(!vi)
    End("Impossible d'obtenir les visual infos");

  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("Impossible d'ouvrir la fenêtre");

  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("La création des gadgets a échoué");

  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()
*
*   Gère les événements transmis par Intuition
*/
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 *)
*
*   Retourne la taille en octets d'un fichier donné en paramètre
*/
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)
*
*   Cette procédure met à jour la boite représentant l'état
*   courant du téléchargement. current = nb d'octets déjà
*   téléchargés, size = nb d'octets total.
*/
void UpdateBox(long current, long size)
  {
  long width = (242 * current) / size;
  RectFill(window->RPort,BOX+4,58,BOX+width+4,70);
  }

