/****************************************************************
** Title  :!USBWavePlayer main ** ** Purpose: Modle  d'application Wimp
**   version en c de la copie de  !Clock en basic
**  utilisation OSLIB/oslibsupport et la toolbox avec Acorn DDE
*   !Zap et les manuels d'aides StrongHelp: Oslib, wimp/os etc...
** History:     created:  5th March 2015, modif le Sun 29th March 2015
**  pour tre utiliser comme point de dpart d'une application utilisant
**  la ToolBox. Bonne dmo de dessin de fentre (nulEvent  la seconde)
**  pour animation  ou l'on ne redessine que ce qui est ncessaire....)
**  Copyright:   2015 JMB
*****************************************************************/

#define MAIN
/* On dfinit ce module 'main)pour tre l'entre principale
  des variables globales. Voir la description dans main.h
  pour plus  de dtail. Cette dfinition doit tre faite AVANT que
  main.h soit inclus */

// NOTES
// 1) les includes  sont pris en charge dans Applibs.h
// 2) IGNORE() est une macro dfinie dans utils.h

#include "AppLibs.h" // bibli os
#include "Main.h"// contient les dfinitions de main
#include "debug.h"

 // #define DEBUG FALSE

/*****************************************************************
** forward references
******************************************************************/


/***************************************************************
**     variables globales de main
****************************************************************/
// char gbbufMess[256]; // initialis dans initvar()

 bool   gbQuit;       // le flag global Quit
 wimp_t gbTaskHandle;  // le handle de cette app's task


wimp_icon_state *icon_state; // pour remplir le icon_block de wimp_get_icon_state
 // variables main.c
/* |opting_icon| is usually 0.
 * While dragging an icon from the Options window, or waiting for
 * the DataSaveAck after doing so, it's the icon number of the
 * icon in that window which we dragged.
 */
static int opting_icon=0;

/* Non-obvious fact: |output_name| contains a non-empty string
 * if and only if there is an output file waiting to be saved.
 * In that case, it contains the name of that file.
 * Similarly for |err_name|.
 */
static char output_name[256]="",err_name[256]="";
static long err_zero=0;	/* size of error file before prog runs */

/* We need to save the name of the input file (earlier versions
 * brokenly passed around a pointer into our Wimp message block!).
 * We put it in |input_name|.
 */
static char input_name[256]="";

/* While we're doing this sort of thing, we might as well also
 * have a buffer for putting commands in.
 * It might be useful for other things, too...
 * The longest command we ever issue contains 5 pathnames and
 * some other stuff.
 * This also needs to be big enough for the window block for
 * the Options window; ditto for the Save window. No trouble.
 */
#define scratch_size 1400
static char scratch[scratch_size];

/* Another magic number of a similar sort:
 * This needs to be bigger than the total size of any window (including
 * indirected data).
 * At the moment the Options window is getting close, so be careful.
 */
#define winbuf_size 2200


/* Units.
 */

typedef struct { char *arg; char *name; } Unit;
#define n_units 6
Unit unit_list[n_units] = {
  { "sp", "Scaled points" },
  { "os", "OS units" },
  { "pt", "Points" },
  { "mm", "Millimetres" },
  { "cm", "Centimetres" },
  { "in", "Inches" }
};


/* Various bits of state.  ?*/

static int open_tagfile=0;
static int use_spritefile=0;
static int use_prefix=0;
static char *tagfile_name=0;
static char *spritefile_name=0;
static char *jpeg_prefix=0;
static char *save_ftype=0;
static char *save_name=0;
static char *save_title;
static char *unit_string=0;
static int units=2;	/* the 2 is magic */



int iROversion; // version courante de l'os retourne par toolbox_init...

// variables globales boolennes

//***********  dclaration des fonctions  **************

//-------------------------------------------------------------
//  initialisations des variables globales (dclarations dans main.h)
void initvar(void)
{
  // variable globale buf reserve aux messages (debug)
  gbbufMess = malloc(512);
  sprintf(gbbufMess,"%s","JMB----- Brindas");
  save_title = malloc(32);
  spritefile_name = malloc(256);
  save_ftype =  malloc(12);
  save_name =  malloc(256);
  tagfile_name = malloc(256);
  jpeg_prefix = malloc(253);
  unit_string = malloc(256);

  //variables boolennes

  // init erreurs   appli messages
  #define err_base (1<<30)
  // voir  os_error *Err_MakeError( int iErrNum, char *zMessage );  oslibsupport
  //static os_error(44) out_of_date  = { err_base,"This application requires RISC OS 3." };
  out_of_date  = Mk_Error( err_base, "This application requires RISC OS 3.");
  no_templates = Mk_Error(err_base+1,"I can't open my Templates file.");
  //    /* Error number 2 is no longer needed */
  no_window    = Mk_Error(err_base+3,"My Templates file is broken!");
  cw_failed    = Mk_Error(err_base+4,"I failed to create a window.");
  ci_failed    = Mk_Error( err_base+5,"I failed to create an icon.");
  aargh        = Mk_Error(err_base+6,"Something really nasty went wrong!");
  no_memory    = Mk_Error( err_base+7,"I've run out of memory." );
  im_busy      = Mk_Error( err_base+8,"Sorry, but I'm busy. Try again later.");
  no_errfile   = Mk_Error( err_base+9,"I couldn't open an error log file.");
  save_failed  = Mk_Error( err_base+10,"Something went wrong trying to save that.");
  too_long     = Mk_Error(err_base+11,"That command line is too long." );
  bye_bye      = Mk_Error(err_base+12,"Appli end!" );

}


//------------------------------------------------
// fonctions diverses
//------------------------------------------------

static void update_units(void) {
  strcpy(unit_string,unit_list[units].name);
  button_set_value(0,opt_object, oi_units, unit_string);
  //wimp_seticonstate(opt_handle,oi_units,0,0);
}

/* |lose(err)| reports the error, exits and dies.
 * Its arg should point to an error block of some sort.
 */
 void lose( const void *e)
{
  wimp_report_error((os_error*)e,wimp_ERROR_BOX_OK_ICON,APPNAM);
  wimp_close_down(gbTaskHandle);
}

/* |err(x)| reports the error, but doesn't exit.
 */
void err(const void *e) {
  wimp_report_error((os_error*)e,wimp_ERROR_BOX_OK_ICON,APPNAM);
}

/* |finish()| exits without an error.
 */
 void finish(void) {
  wimp_close_down(gbTaskHandle);
  if (*err_name) remove(err_name);
  if (*output_name) remove(output_name);
  exit(0);
}

/* |err(x)| fills the  the os_error.
 */
os_error Mk_Error( int errnum, char* errtext)
{
  os_error out;
  out.errnum = err_base;  strcpy(out.errmess, errtext );
  return out;
}

/* Turn a control-terminated string into a null-terminated one.
 */
void sanitise(char *s) {
  char c;
  while ((c=*s++)>=32) ;
  s[-1]=0;
}

/* Shade or unshade icons n+1,n+2 in the options window
 * according to the selection state of icon n. Also toggle
 * that state; it's the *new* state that matters.
 * Returns 1<<21 for selected, else 0.
 */
//static int maybe_shade(int n) {
//  int t = wimp_geticonstate(opt_handle,n)->flags.val;
//  int u = (t&=(1<<21))<<1;
//  wimp_seticonstate(opt_handle,n,1<<21,0);
//  wimp_seticonstate(opt_handle,n+1,u,1<<22);
//  wimp_seticonstate(opt_handle,n+2,u,1<<22);
//  return t^(1<<21);
//}

/* Leafname from pathname. */
static const char *leaf(const char *s) {
  const char *t=s;
  char c;
  while ((c=*t++)!=0)
    if (c=='.'||c==':') s=t;
  return s;
}

/* Leafname, or empty string if no path.*/
static const char *leaf0(const char *s) {
  const char *t=leaf(s);
  return t==s ? (const char *)"" : t;	/* (const char *)"" ... ludicrous */
}

/* Leafname of a name in indirected text of icon n in window w.*/
//#define leafname(w,n) leaf(wimp_geticonstate((w),(n))->data.indtext.text)

static const char *leafname(toolbox_o w, toolbox_c b )
{
  button_get_value(0,w,b,gbbufMess,512);

  //return gbbufMess;
  return leaf(gbbufMess);
  }
/*------------------------------------------------------------------------*/

#ifdef THROWBACK
static int using_dde=0;
#endif

// C program to find the size of file

long int  file_size(char *file_name)
{
  //fileswitch_object_type fswotyp;  //fileswitch_NOT_FOUND
  //debug_printf("filename %s", file_name );
  int size;
  if (osfile_read_stamped(file_name ,NULL,NULL, &size, NULL,NULL) != fileswitch_NOT_FOUND){
    return size;
    //debug_printf("taille %d", size);
  }
  return 0;
}



/* When we start a task, we must make sure the command line is
 * no longer than 256c; otherwise something barfs in a big way
 * and crashes the machine irrevocably (well, it does with my
 * machine anyway).     ??? plus ncessaire
 */
static wimp_t start_task(char *s)
{
  return wimp_start_task(s);
}

//int l=strlen(s);
//if (l<256) return wimp_start_task(s);
//#ifdef THROWBACK
//  if (using_dde) return dde_starttask(l,s);
//  else
//#endif
//  err(&too_long); return NULL;
//}


/* Create the error file. le fichier est cr dans le scrap,
 il est automatiquement effac si un nouveau.
 */
static void create_errors(const char *prog) {
  FILE *f=fopen(err_name,"w");
  if (!f) { err(&no_errfile); *err_name=0; return; }
//  sprintf(gbbufMess,"errname %s ", err_name);
//  Message_Info(gbMessage_id, gbbufMess  );
#ifdef THROWBACK
  using_dde=dde_exists();
#endif
  fprintf(f,"Errors and warnings from %s:\n"
            "Input file: %s\n"
            "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
            "~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n",
            prog,input_name);
  // This function obtains the current value of the file position indicator for stream.
  err_zero=ftell(f);
  fclose(f);
}


#ifdef THROWBACK
/* If we have the DDE available, we can do throwback:
 * go through the file and send the relevant info to
 * the DDEUtils SWIs.
 */
static void do_throwback(void) {
  FILE *f=fopen(err_name,"r");
  unsigned int l; char *cp;
  throwback_start();
  throwback_filename(input_name);
  while (fgets(scratch,256,f)) {
    sanitise(scratch);
    if (strncmp(scratch,"Line ",5)) continue;
    l=(unsigned int)strtoul(scratch+5,&cp,10);
    if (*cp==':' && cp[1]==' ' && (l || (cp!=scratch+5))) {
      /* Looks like a real error message. Assume it is. */
      int ty;
      if (cp[2]=='w') { ty=0; cp+=11; }
      else if (cp[2]=='e') { ty=1; cp+=9; }
      else if (cp[2]=='f') { ty=2; cp+=15; }
      else ty=2;	/* this shouldn't happen */
      throwback_error(input_name,l,ty,cp);
    }
  }
  throwback_end();
  fclose(f);
}
#endif


/* Actually save the file. To be more precise: copy it to the
 * right place.
 * Return 0 for failure, 1 for success.
 */
static int do_really_save(char *savename) {
  /* copy from to ~CDF~V */
  //sprintf(scratch,"Copy %s %s ~CDF~V",output_name,save_name);
  sprintf(scratch,"Copy %s %s ~CDF~V",output_name,savename);
  system(scratch);
  *output_name=0;
  //wimp_close_window(save_handle);
  return 1;
}

/* Do the first things needed for a launch.
   gnre un nom de fichier autre que input_name  partir du nom de feuille
*/
static void begin_launch(void) {
  strcpy(save_name,leaf0(input_name));
  //remove Function deletes the file pointed to by {/}filename{/}.
  //0 is returned for success.
  if (*output_name) remove(output_name);
  if (*err_name) remove(err_name);
  //Function generates a string that is not the same as the name of an existing file. It will generate a different string up to <TMP_MAX> times.
  tmpnam(output_name); // scrapfile
  tmpnam(err_name);  // scrapfile
  //Message_Info(gbMessage_id, err_name );
  hourglass_start(50);
  // on cre des noms de fichier: er_name et output_name dans le scrap
}

/* And the last.
 */
static void finish_launch(char c) {
  /* Run mkdrawf or decdrawf. */
  int no_err=1<<21;
  //excute la tache en indirectant celle-ci vers le scrap.
  start_task(scratch);
  hourglass_off();
  /* Get the error file opened by a text editor, or else send its contents
   * to whatever's doing throwback. But don't do any of this if there weren't
   * any diagnostics.
   * We don't delete the error file at this point, because the Filer_Run
   * won't take place instantaneously. It's deleted next time we need to
   * use an error file...
  */
  //Message_Info(gbMessage_id, err_name );

  if (*err_name) {
    if (file_size(err_name)!=err_zero) {
      no_err=0;
#ifdef THROWBACK
      if (using_dde && c=='a') {
        do_throwback();
        remove(err_name);
        *err_name=0; }
      else {
#endif
       sprintf(scratch,"Filer_Run %s",err_name);
       system(scratch);
#ifdef THROWBACK
      }
#endif
    } else { remove(err_name); *err_name=0; }
  }
  /* If appropriate, Filer_Run the output file. */

//  if (wimp_get_icon_state(opt_handle,oi_run_always)->flags.val&(1<<21)
//      || wimp_geticonstate(opt_handle,oi_run)->flags.val&no_err) {
//    sprintf(scratch,"Filer_Run %s",output_name);
//    system(scratch);
  //  }
  if( optionbutton_get_state(0, opt_object, oi_run_always ) || optionbutton_get_state(0, opt_object, oi_run ) ){
    sprintf(scratch,"Filer_Run %s",output_name);
    system(scratch);
  }

//  /* Now open the Save window. */
//  // save window obect show
//  strcpy(save_title,c=='a'?"mkdrawf output":"decdrawf output");
//  *save_ftype=c;
//  {
  //int *block=(int*)scratch;
//    block[0]=save_handle;
//    wimp_getwindowinfo(block);
//    wimp_openwindow(block);
  //  }
  //c = 'f';
  strcpy(save_title,c=='a'?"mkdrawf output":"decdrawf output");
  *save_ftype=c;
  //Message_Info(gbMessage_id, save_title );
  saveas_set_title(0, save_object , save_title);
  saveas_set_file_type(0, save_object ,c=='a'?0xaff:0xfff);
  toolbox_show_object(0, save_object,3,NULL,0,0 );

}

/* Launch mkdrawf. |name| is the name of the input file.
<Drawf32$Dir>.
 */
static void launch_mkdrawf(const char *name) {
  strcpy(input_name,name);
  //sprintf(input_name,"%s_D",name);
 // Message_Info(gbMessage_id,"launch_mkdrawf");
  begin_launch();
  create_errors("mkdrawf");
//  /* mkdrawf input output 2>>errs -t tags */
  sprintf(scratch,"<Drawf32$Dir>.mkdrawf -e %s %s %s%s%s%s",
          input_name,output_name,
          *err_name?"2>>":"",err_name,
          open_tagfile?" -t ":"",open_tagfile?tagfile_name:"");
  finish_launch('a');
}


static void launch_decdrawf(const char *name) {
  strcpy(input_name,name);
  //  sprintf(gbbufMess,"launch_decdrawf: %s", input_name);
  //  Message_Info(gbMessage_id, gbbufMess);
  // creation des pseudos err_name et output_name pour utiliser sur le scrap
  begin_launch();
  //ouverture d'un fichier err_name, en criture sur le scrap
  create_errors("decdrawf");
  /* decdrawf input >output 2>>errs -s sprites -j jpeg */
  //debug_printf("%s , spritefilename: %s", spritefile_name, spritefile_name);
  sprintf(scratch,"<Drawf32$Dir>.decdrawf -e %s >%s %s%s%s%s%s%s%s%s",
          input_name,output_name,
          *err_name?"2>>":"",err_name,
          use_spritefile?" -s ":"",use_spritefile?spritefile_name:"",
          use_prefix?" -j ":"",use_prefix?jpeg_prefix:"",
          units!=2?" -u ":"",units!=2?unit_list[units].arg:"");
  finish_launch('f');
}


/* Shade or unshade icons  n+1,n+2 in the options window
 * according to the selection state of icon n. Also toggle
 * that state; it's the *new* state that matters.
 * Returns 1<<21 for selected, else 0.
 */
static bool maybe_shade(toolbox_c n) {
  bool b = optionbutton_get_state(0, opt_object, n);
  //bits t = gadget_get_flags(0, opt_object, n);
  bits h;
  h= b?!gadget_FADED:gadget_FADED;
  gadget_set_flags(0, opt_object, n+1,h);
  button_set_flags(0, opt_object, n+2, !b << 22,1<<22);

//  sprintf(gbbufMess," maybe_shade: %d, h= %0X, b= %d ", n, h, b);
//  Message_Info(gbMessage_id, gbbufMess);
  return b;
}


/*****************************************************************
                ToolBox EVENT HANDLERS
***************************************************************
Pour info:
 typedef osbool event_toolbox_handler (bits event_code,
       toolbox_action *action, toolbox_block *id_block, void *handle);
OSLIB: ToolboxEvent = toolbox_action, IdBlock = toolbox_block, uint = bits
*****************************************************************/


//static osbool OptionButton_handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
//{
//  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
//
//   //Message_Info(gbMessage_id,"Message test");
//  return true;
//}

//@{------------------------------------------------------------
//             error toolbox  EVENT HANDLERS
//   ( ToolboxEvent = toolbox_action, IdBlock = toolbox_block)
//@}-----------------------------------------------------------
static int TBerror_handler(uint uiEvent_Code, toolbox_action *pxEvent, toolbox_block *pxIdBlock, void *vHandle)
{
  IGNORE(uiEvent_Code);IGNORE(pxEvent);IGNORE(vHandle);IGNORE(pxIdBlock);
   /* Handles an error by raising it */
  // raise_error ((_kernel_oserror *)event_block->data.bytes);toolbox
  Err_OsErrorReport((os_error*) &pxEvent->data.error ); //OslibSupport Err.h
  return 1;
}
//--------------------------------------------------------------
//                Menu Tbox EVENT HANDLERS bbox
//--------------------------------------------------------------
//@{	click on the 'Quit' entry of the iconbar menu
//@}
static osbool IconMenuQuit_handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
  //Message_Info(gbMessage_id,"Fermeture application");

  messagetrans_close_file(&gbMessagesFD); // doit fermer le fichier Messages
  //lose(&bye_bye);
  if (*output_name) { remove(output_name); *output_name=0; }
  if (*err_name) { remove(err_name); *err_name=0; }
  gbQuit = TRUE;
  return TRUE ;
}

//@{ Appel avant l'ouverture de la fentre saveas
//   doit mettre la data adresse de l'objet saveas qui se chargera
//   du process de sauvegarde.
//
//@}

static osbool Show_SaveWin_Handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
  //char suffix[2];
  //  sprintf(gbbufMess,"%s" ,output_name);
  //suffix = save_ftype =='a'?"_T":"_D";
  sprintf(save_name,"%s%s",save_name, *save_ftype =='a'?"_D":"_T" );
  //Message_Info(gbMessage_id,save_name);
  saveas_set_file_name(0,save_object, save_name);
  return true;
}

//@{ Appel pour sauver le fichier
//   fait une copie du fichier du scrap.
//@}
static osbool SaveFile_SaveWin_Handler( uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
  saveas_get_file_name(0, save_object, gbbufMess,512);
  //sprintf(gbbufMess,"save file: %s" ,output_name);
  //Message_Info(gbMessage_id,gbbufMess);
  do_really_save(gbbufMess);
  toolbox_hide_object(0, save_object);
  return true;
}

//@{ Appel  la fermeture de la fentre saveas, en cliquant sur cancel
//   par exemple. Ce handler doit effacer les fichiers du scrap qui ont t gnrs
//   en draguant un fichier text ou draw sur l'iconbar.
//@}
static osbool Close_SaveWin_Handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
  if (*output_name) { remove(output_name); *output_name=0; }
  if (*err_name) { remove(err_name); *err_name=0; }
  return true;
}
//@{ Appel aprs sauvegarde
static osbool Completed_SaveWin_Handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
  //Message_Info(gbMessage_id," save completed");
  return true;
}


//@{
// ToolBox handler: changement d'tat des boutons de la fentre options
//}@
static osbool Opt_Buttons_Handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
      switch(IdBlock->this_cmp) {
      case oi_tagfile_opt:
        open_tagfile=maybe_shade(oi_tagfile_opt); break;
      case oi_sprfile_opt:
        use_spritefile=maybe_shade(oi_sprfile_opt); break;
      case oi_jpegpfx_opt:
         use_prefix=maybe_shade(oi_jpegpfx_opt); break;

    }
  return true;
}

//@{
//  ToolBox handler: fin du drag vers une icne de classeur, pour rcuprer le nom de celui-ci.
// Envoi d'un message vers le classeur de destination.
//}@
static osbool DragEnded_Opt_Buttons_Handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
  draggable_action_drag_ended_block *draggable_block;
  draggable_block = (draggable_action_drag_ended_block *) pxEvent ;
  //sprintf(gbbufMess,"drag end, o: %0x, c: %0x ", draggable_block->ids.toolbox.o, draggable_block->ids.toolbox.c);
  //Message_Info(gbMessage_id,gbbufMess);

  // envoi d'un message recorded au filer
  //debug_printf("opting_icon: %d",opting_icon);
  wimp_message wmess;
  wmess.your_ref = 0; // original message
  wmess.action  =  message_DATA_SAVE;
  //  wmess.data.data_xfer.w = opt_handle;
  wmess.data.data_xfer.w = draggable_block->ids.wimp.w;
  //  wmess.data.data_xfer.i = opting_icon;
  wmess.data.data_xfer.i = draggable_block->ids.wimp.i;
  wmess.data.data_xfer.pos = draggable_block->pos;
  wmess.data.data_xfer.est_size = 0;  //estimated size in bytes
  wmess.data.data_xfer.file_type =  (*save_ftype=='a')?0xAFF:0xFFF;
  sprintf(wmess.data.data_xfer.file_name,"%s", leafname(opt_object, opting_icon +1));
  //Message_Info(gbMessage_id, wmess.data.data_xfer.file_name);
  //opting_icon ? leafname(opt_handle,opting_icon+1): leaf(save_name));
  int tailleMess = (strlen( wmess.data.data_xfer.file_name)+48)&~3 ;
  wmess.size = tailleMess;
  wimp_send_message_to_window(wimp_USER_MESSAGE_RECORDED, &wmess, draggable_block->ids.wimp.w,draggable_block->ids.wimp.i);
  return true;
}


//@{
//  ToolBox handler: dbut du drag vers une icne de classeur, pour rcuprer le nom de celui-ci.
//  attribution d'un valeur  opting_icon. suivant une option tag/sprite/jpeg
//}@
static osbool DragStarded_Opt_Buttons_Handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);

  opting_icon = IdBlock->this_cmp;

//  sprintf(gbbufMess,"drag start icon: %d", IdBlock->this_cmp);
//  Message_Info(gbMessage_id,gbbufMess);
  return true;
}

//@{
//  ToolBox handler: clic sur un des boutons adjuster de la fentre options
//}@
static osbool Adjuster_click__Opt_Buttons_Handler(uint uiEvent_code, toolbox_action *pxEvent, toolbox_block *IdBlock,void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(pxEvent);IGNORE(vhandle);IGNORE(IdBlock);
  adjuster_action_clicked_block * adacclickblk;
  adacclickblk = (adjuster_action_clicked_block*) pxEvent;
  toolbox_c cmp =  IdBlock->this_cmp;
  if(cmp == oi_units_left || cmp == oi_units_right){
    if ( adacclickblk->up){
      if (units<n_units-1) { ++units; update_units();}
    }else {
    if (units>0) {--units; update_units(); }
    }
  }
  return true;
}



/*****************************************************************
                Wimp EVENT HANDLERS
*****************************************************************/
//static osbool Close_SaveWin_Handler(wimp_event_no EventCode, wimp_block * pxEvent, toolbox_block*  pxIdBlock, void* vHandle )
//{
//  IGNORE(EventCode);IGNORE(pxEvent);IGNORE(pxIdBlock);IGNORE(vHandle);
//  Message_Info(gbMessage_id,"Fermeture de la fentre save");
//  return true;
//}
//----------------------------------------------------------------
//  Message_Info(gMessage_id,gbbufMess);


 /****************************************************************
 **    Application        Wimp_Message_Handlers
 ****************************************************************/
 // https://www.riscosopen.org/wiki/documentation/show/Message%20Codes

//WimpMessage Wimp_Message
//  Quit ou PreQuit message reu du Wimp
static int QuitMessage_handler(wimp_message *pxMessage, void *vhandle)
{
    IGNORE(vhandle);IGNORE(pxMessage);
    //Message_Info(gbMessage_id,"Fermeture application");
    messagetrans_close_file(&gbMessagesFD);//doit fermer le fichier MessagesFD
    finish(); // on sort....
    //gbQuit = TRUE;
    return( TRUE );
}
//------------------------------------------------------------------

// affichage des messages d'aide interactive
static osbool HelpMessageEvent(wimp_message *pxMessage, void *vhandle)
{
    IGNORE(vhandle);IGNORE(pxMessage);

//rponse  wimp_message : message_HELP_REQUEST
// rcupre un message du fichier Messages avec le control_block cre
//lors du dmarrage dans main() par toolbox_initialise
   gbbufMess =  messagetrans_lookup(&gbMessagesFD,"Help",
                             gbbufMess,512,0,0,0,0,0);
   // prparation et rponse pour affichage du message d'aide interactif
   // transmis dans header de la structure wimp_message:protocol
   int sizetexte;
   pxMessage->my_ref = pxMessage->your_ref;
   pxMessage->action = message_HELP_REPLY;
   sizetexte = sprintf((char*)pxMessage->data.reserved,"%s",gbbufMess);
   pxMessage->size = (((20 + sizetexte + 4)/4)*4);// word align
   wimp_send_message(wimp_USER_MESSAGE,pxMessage,wimp_BROADCAST);

   return true;
}

// *** handler rponse : Message_Data_Load
static osbool DataLoad_Handler(wimp_message *pxMessage, void *vhandle)
{
  IGNORE(vhandle);IGNORE(pxMessage);
//  wimp_w  fen =  pxMessage->data.data_xfer.w;
//  wimp_i baricon =  pxMessage->data.data_xfer.i;
  //  debug_printf ("baricon %d: fen: %d",baricon,fen);
  // not iconbar icon
  if( pxMessage->data.data_xfer.w != (wimp_w)-2)   return false;
  // filetype  text,dos ou draw rpose  un wimp_message_data_xfer
  switch( pxMessage->data.data_xfer.file_type)
  {
  case 0xFFF:
  case 0xFE4:
      //launch_mkdrawf((char*)(block+11)); break;
    launch_mkdrawf( pxMessage->data.data_xfer.file_name);
    break;
  case 0xAFF:
      //launch_decdrawf((char*)(block+11)); break;
    launch_decdrawf( pxMessage->data.data_xfer.file_name);
    break;
  default:
    return 0;	// don't know how to deal with that...filetype.
  }
  /* Better be polite and acknowledge the message */
  // voir: https://www.riscosopen.org/wiki/documentation/show/User_Message_Recorded
  pxMessage->your_ref = pxMessage->my_ref;
  wimp_send_message(message_DATA_LOAD_ACK, pxMessage, pxMessage->sender); // PRM3,203
  return true;

}
// *** handler rponse : Message_Data_save
static osbool Data_Save_Message_handler(wimp_message *pxMessage, void *vhandle)
{
  IGNORE(vhandle);IGNORE(pxMessage);
    //Message_Info(gbMessage_id,"Data_Save_Message_handler");
  return true;
}

//{@
// handler rponse : Message_Data_save aprs un drag depuis options boutons
// dans ce message contenant la structure wimp_message_data_xfer,
// il y a un filename  recopier dans le boutton oi_oi_tagfile_name/sprfile/jpegpfxname.
// opting_icon peut prendre les 3 valeurs: oi_tagfile_icon= 3, oi_sprfile_icon=8,
// oi_jpegpfx_icon= 11
//}@
static osbool DataSaveAck_Opt_Buttons_Handler(wimp_message *pxMessage, void *vhandle)
{
  IGNORE(vhandle);IGNORE(pxMessage);
  if(opting_icon == 0) return false;
 sprintf(gbbufMess,"ram bar icon: %s",  pxMessage->data.data_xfer.file_name);

 button_set_value(0,opt_object, opting_icon +1, pxMessage->data.data_xfer.file_name);
 switch (opting_icon) {
 case 3:
   sprintf(tagfile_name ,"%s", pxMessage->data.data_xfer.file_name);
   break;
 case 8:
   sprintf(spritefile_name,"%s", pxMessage->data.data_xfer.file_name);
   break;
 case 11:
   sprintf(jpeg_prefix,"%s", pxMessage->data.data_xfer.file_name);
   break;
 }


  //sprintf(gbbufMess,"opting_icon %d: ", opting_icon);
  //button_get_value(0,opt_object, opting_icon +1,gbbufMess, 512);

  //Message_Info(gbMessage_id, gbbufMess);

  opting_icon = 0;
  return true;
}

/****************************************************************
*   Misc_op toolbox event handler
****************************************************************/



/****************************************************************
** Initialise la fentre principale et enregistre ses "tbox, wimp, message event handlers"
****************************************************************/

void  FenOptionsInit(toolbox_id OBJ_TEMPLATE )
{
  opt_object = toolbox_create_object( 0, OBJ_TEMPLATE );
  opt_handle = window_get_wimp_handle( 0, opt_object);

   /* Bits of writable data. */
    //  rcuprer des objets crs avec ResED
  button_get_value(0, opt_object,oi_tagfile_name, tagfile_name, 256);
  button_get_value(0, opt_object,oi_sprfile_name, spritefile_name, 256);
  button_get_value(0, opt_object,oi_jpegpfx_name, jpeg_prefix, 256);
  button_get_value(0, opt_object,oi_units, unit_string, 256);
  //debug_printf( "%0x", saveas_get_file_type(0, save_object));
  //bits ftyp = saveas_get_file_type(0, save_object);
  //sprintf(save_ftype,"%0x", saveas_get_file_type(0, save_object));
  //saveas_get_file_name(0, save_object, save_name, 256);


  /*********       toolbox  handlers  event_ANY  *************/

  event_register_toolbox_handler( opt_object , action_OPTION_BUTTON_STATE_CHANGED , Opt_Buttons_Handler, NULL);
  event_register_toolbox_handler( opt_object , action_DRAGGABLE_DRAG_ENDED, DragEnded_Opt_Buttons_Handler, NULL);
  event_register_toolbox_handler( opt_object , action_DRAGGABLE_DRAG_STARTED, DragStarded_Opt_Buttons_Handler, NULL);
  event_register_toolbox_handler( opt_object , action_ADJUSTER_CLICKED , Adjuster_click__Opt_Buttons_Handler, NULL);

 /*************      wimp handlers   Fenprin     *************/

// enregistre wimp event handlers  click souris:
//     event_register_wimp_handler( event_ANY,
//                                  wimp_MOUSE_CLICK,
//                                  Fenprin_Mouse_Select_Handler,
//                                  NULL);


    /*************      message handlers        *************/

    event_register_message_handler(message_DATA_SAVE_ACK, DataSaveAck_Opt_Buttons_Handler, NULL);
// enregistrement du wimp_message message_HELP_REQUEST n1282 =0x502
    event_register_message_handler (  message_HELP_REQUEST,
                                     HelpMessageEvent, NULL);
}

//@{
//  Initialise l'icon de iconbar et enregistre ses "tbox event handlers"
//  Appele pendant message_TASK_INITIALISE
//@}
void  IconbarInit(toolbox_id OBJ_TEMPLATE )
{
   gbIconBar_id= toolbox_create_object( 0, OBJ_TEMPLATE );// from resevents.h
   toolbox_show_object( 0, gbIconBar_id , 0, 0, 0, 0 );

   /*************   toolbox  handlers    *************/ //QUIT_QUIT ICONMENU_QUIT_EVENT
   event_register_toolbox_handler( event_ANY , ICONMENU_QUIT_EVENT, IconMenuQuit_handler, NULL );
   /*************      wimp handlers     *************/

   /*************      message handlers  *************/
   event_register_message_handler( message_DATA_LOAD, DataLoad_Handler, NULL );

}

//--------------------------------------------------------------
//  templates auto crs TBox  EVENT HANDLERS
//--------------------------------------------------------------
// ce handler est appell lorsqu'un objet a t auto-cre avec le nom
// dutemplate stock dans le toolbox event block: event_block
// Retourne l'id des fentres.
// on peut recuprer le handle des objets,faire des initialisations
// et dclarer les enregistrements event_register_wimp_handler() par objets.

static int FindId_handler( uint uiEvent_code, toolbox_action  *pxEvent,  IdBlock *pxIdBlock,  void *vhandle)
{
  IGNORE(uiEvent_code);IGNORE(vhandle);
  x_exception xx;
  toolbox_class obj_class;
// x_LOCAL_ERROR( Error_instance, 1, "Pas d'object_id avec ce nom" );

  x_TRY( &xx )
  {
    // les noms suivants sont ceux donns avec ResEdit et rechercher auto
    obj_class = toolbox_get_object_class(0,pxIdBlock->self_id);
    switch(obj_class)
    {
      case class_WINDOW :
//        if (!strcmp ( pxEvent->data.created.name, "Options"))
//        {
//          opt_object =  pxIdBlock->self_id;
//          opt_handle = window_get_wimp_handle( 0, opt_object);
//
//        }
        if (!strcmp ( pxEvent->data.created.name, "Message"))
        {
          gbMessage_id =  pxIdBlock->self_id;
        }
        break;
      case class_ICONBAR:
        if (!strcmp ( pxEvent->data.created.name, "IconBar"))
        {
          gbIconBar_id =  pxIdBlock->self_id;

        }
        break;
      case class_MENU:
        if (!strcmp ( pxEvent->data.created.name, "IbarMenu"))
        {
          gbMenuBar_id =  pxIdBlock->self_id;
          event_register_toolbox_handler(gbMenuBar_id, action_QUIT_QUIT, IconMenuQuit_handler, NULL );
        }
        break;
      case class_PROG_INFO:
      if (!strcmp ( pxEvent->data.created.name, "ProgInfo"))
      {
        gbProgInfo_id  =  pxIdBlock->self_id;
      }
      break;
      case class_SAVE_AS:
      if (!strcmp ( pxEvent->data.created.name, "Save"))
      {
        save_object =  pxIdBlock->self_id;
        save_handle = window_get_wimp_handle( 0, save_object );
        event_register_toolbox_handler(save_object, action_SAVE_AS_DIALOGUE_COMPLETED, Close_SaveWin_Handler, NULL);
        event_register_toolbox_handler(save_object, action_SAVE_AS_ABOUT_TO_BE_SHOWN,  Show_SaveWin_Handler, NULL);
        event_register_toolbox_handler(save_object, action_SAVE_AS_SAVE_TO_FILE,  SaveFile_SaveWin_Handler, NULL);
        event_register_toolbox_handler(save_object, action_SAVE_AS_SAVE_COMPLETED,  Completed_SaveWin_Handler, NULL);
      }
      break;
    }
  }


  x_CATCH( &xx )
  {
    Err_ReportError( !Err_FATAL, "Impossible de rcuprer tous les identifiants: %s", xx.error -> errmess );
  }
   return 0;
}

/********************************************************************************
  Message d'Initialisation  reu du  Wimp: message_TASK_INITIALISE
  C'est la premire chose qui est appele une fois que l'application a dmarre
  Il est en gnral mieux de faire toutes les autres initialisations  partir d'ici, comme la toolbox, etc tout est positionn et prt.
  cet handler de message pour l'initialisation a t enregistr dans la fonction main().
*********************************************************************************/

static int InitMessage_Handler( WimpMessage *pxMessage, void *vhandle)
{
  IGNORE(vhandle); //wimp_message oslib
  /* define the cast avec wimp_message_task_initialise */
  #define PXMESSAGEDATA ((wimp_message_task_initialise*)&pxMessage -> data )
  x_exception xy; //structure d'une exception Oslib support
x_TRY( &xy )
{
  // check the task name from main.h
  if( strncmp(  PXMESSAGEDATA -> task_name, APPNAM, strlen( APPNAM ) ) == 0
  		&& pxMessage -> sender == gbTaskHandle )
  {
    // enregistre les messages handlers  grs dans main()
    event_register_message_handler( message_QUIT, QuitMessage_handler, NULL);
    event_register_message_handler( message_PREQUIT, QuitMessage_handler, NULL );
    event_register_message_handler( message_DATA_SAVE_ACK, Data_Save_Message_handler, NULL );
    // enregistre un event handler qui est appel pour les erreurs toolbox
    event_register_toolbox_handler( event_ANY, action_ERROR, TBerror_handler, NULL );
    // enregistre un event handler qui est appel pour les template auto cres
    event_register_toolbox_handler( event_ANY, action_OBJECT_AUTO_CREATED, FindId_handler, NULL );

    // Initialisation des objets
    FenOptionsInit(FENPRIN_TEMPLATE);
    IconbarInit(ICONBAR_TEMPLATE);
  }
}
// exception catcher
x_CATCH( &xy )
{
  Err_ReportError( Err_FATAL, "InitMessageproc Exception" );   // always fatal
}
  return( TRUE );
  #undef PXMESSAGE
}
//------------------------------------------------------------------


// retourne le handle de la premire tche qui correspond  zName
static wimp_t FindTask( char* zName )
{
  taskmanager_task xTask; //oslib type
  int iContext = 0;
  char* pcEnd;

  xTask.name = NULL;

  while( iContext >= 0 && strncmp( zName, xTask.name, strlen(zName) ) != 0 )
  {
    iContext = taskmanager_enumerate_tasks( iContext, &xTask, sizeof( xTask ), &pcEnd );
  }

  // retourne 0 si rien trouv
  return (iContext > 0) ? xTask.task : 0;
}

/**************************************************************
*                       Main entry                            *
***************************************************************/
int main(void)
{
  x_exception x0; //structure d'une exception Oslib
  x_exception x1;

  int iWimp_version = WIMP_VERSION;
  int iEvent_code;

  osspriteop_area* pxSpriteArea; //oslib
  WimpPollBlock xPollBlock;  // (oslibsupport)  wimp_block
  IdBlock xId_block;  // (oslibsupport) toolbox_block

/*liste de Wimp messages  surveiller ; termin par 0 */
//#ifdef HELPFUL
//  static  int aiWimpMessages[] = {message_DATA_SAVE, message_DATA_SAVE_ACK, message_DATA_LOAD, message_DATA_LOAD_ACK,0};
//#else
//  static  int aiWimpMessages[] = {message_DATA_SAVE,message_DATA_SAVE_ACK, message_DATA_LOAD, message_DATA_LOAD_ACK,0};
//#endif
static  int aiWimpMessages[] = {0};
static  int aiToolboxEvents[] = {0}; /*list de Toolbox events  surveiller; termin par 0 */

// prpare un block erreur  lancer si une instance prcedente est trouve

x_LOCAL_ERROR( Error_second_instance, 1, "On ne peut utiliser qu'une seule instance de !AppMode" );

  x_TRY( &x0 ) 	// niveau suprieur de "exception catcher"
  {
    x_TRY( &x1 )	// prpare un "error TRY block"
      			// Si quelque chose ne va pas le contrle est
                        // transfr au CATCH block correspondant
    {
          initvar(); //variables globales: valeur d'initialisation
// nous devons nous assurer que nous ne dmarrons pas une autre instance del'application.
      if( FindTask( APPNAM ) != 0 ) // de main.c
      {
        // si une instance de l'appli est dj en cours, lance un erreur
        // qui est attrape par le bloc catch (deuxime niveau)
	x_THROW( Error_second_instance );
      }
      else
      {
/****************************************************************
 * enregistrons nous auprs de la Toolbox.
 notez la forme "non-x de swi utilise; nous nous appuyons sur Risc-OS
 pour transfrer le contrle  OSLib's error handler,
 qui  son tour transfert le contrle au bloc CATCH.
 gxTaskHandle sera test par InitMessageProc qui recupre les
 messages message_TASK_INITIALISE: sender
*****************************************************************/
        gbTaskHandle = toolbox_initialise( 0,
        			      	   WIMP_VERSION,
                     (wimp_message_list*)aiWimpMessages,
	             (toolbox_action_list*)aiToolboxEvents,
#if 0	// A changez en 1 pour dmontrer " error trapping" lorsque l'initialisation choue.
                      0,
#else
                      APPDIR,
#endif
// variable globale de l'application: messagetrans_control_block
                      &gbMessagesFD,
                      &xId_block,  // toolbox_block
                      &iWimp_version,
                      &pxSpriteArea );   //&pxSpriteArea
           iROversion = iWimp_version;
      }
    } // fin du try block x1

    x_CATCH( &x1 )  	// On entre dans le CATCH block seulement si
    // quelques choses ne vas pas  l'intrieur du TRY block correspondant
    {
      // Cet appel est fait avec un "flag fatal", et ne revient jamais!
      Err_ReportError( Err_FATAL, "Initialisation stoppe : %s,", x1.error -> errmess );
    }	// fin du catch block x1

    // l'initialisation de cette Wimp Task est un succs

/****************************************************************
** initialise la librairie d'venements et positionne les event masks
** le block est le mme que dans toolbox_init
** wimp_poll_flags = pollmask
****************************************************************/
    event_initialise (&xId_block); // dans main()

    event_set_mask(
             	       //Wimp_Poll_NullMask |
         	       Wimp_Poll_PointerLeavingWindowMask
                      | Wimp_Poll_PointerEnteringWindowMask
                      | Wimp_Poll_LoseCaretMask
                      | Wimp_Poll_KeyPressedMask
                      | Wimp_Poll_GainCaretMask
                      | Wimp_Poll_PollWordNonZeroMask
                      | Wimp_Poll_PollWord
                      | Wimp_Poll_PollWordHighPriority
                            ); // wimp_poll_flags

    //--------  enregistre les message handlers del'application  -------
                            // Commence par l: voir ci-dessus l'initialisation de notre programme

   event_register_message_handler( message_TASK_INITIALISE, InitMessage_Handler, NULL);
   event_register_message_handler( message_QUIT, QuitMessage_handler, NULL );
//event_register_message_handler( message_PREQUIT, QuitMessageProc, NULL );

 // on peut en toute scurit rutiliser le bloc exception x1
    x_TRY( &x1 )
    {
      /*********************** Poll loop      ***********************
      //OSLIB: wimp_event_numero wimp_poll<wimp_poll_flags> mask, 	 	 	               <wimp_block> *block, int *pollword);
      *****************************************************************/

/*      oldtime  =  os_read_monotonic_time();
      oldtime +=150; // retarde le premier nulevent
*/
      while( !gbQuit ) // xPollBlock = wimp_block
      {
        event_poll( &iEvent_code, &xPollBlock, 0 );  //event_poll  oslibsuppport
        //event_poll_idle( &iEvent_code, &xPollBlock, oldtime, 0 );
      }
      wimp_close_down(gbTaskHandle);
      exit(0);
    }  // end of try block x1

   // Attrape toutes les exceptions rencontres en manipulant un poll message
    x_CATCH( &x1 )
    {
      if (*output_name) { remove(output_name); *output_name=0; }
      if (*err_name) { remove(err_name); *err_name=0; }
      Err_ReportError( Err_FATAL,  "Exception inattendue main: %s", x1.error -> errmess );
//      Err_OsErrorReport( x1.error );	  // autre mthode pour rapporter l'erreur
    } 	// fin du catch block x1
  } // fin du try block x0

  // exception catcher du niveau suprieur- ne doit jamais tre requis
  x_CATCH( &x0 )
  {
     if (*output_name) { remove(output_name); *output_name=0; }
     if (*err_name) { remove(err_name); *err_name=0; }
    Err_ReportError( Err_FATAL, "Uncaught Exception" );   // always fatal
  } // fin du catch block x0


  return 1;
}

