/************************************************************
**
** Application: UnitConv
**
** Title:       c.editunits
**
*************************************************************/

/*
*
* Copyright (c) 2015, Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */
/* from standard clib */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>


/* from osLib: */
#include "OSLib:gadget.h"
#include "OSLib:menu.h"
#include "OSLib:osfile.h"
#include "OSLib:OSFSControl.h"
#include "OSLib:toolbox.h"
#include "OSLib:window.h"
#include "OSLib:writablefield.h"


/* from oslib support */
#include "OSLibSupport:event.h"

/* from CJLib */
#include "CJLib:tbox.h"
#include "CJLib:message.h"
#include "CJLib:etc.h"
#include "CJLib:file.h"

/* from app  */
#include "msglink.h"
#include "res.h"
#include "iconbar.h"
#include "main.h"
#include "defaultdata.h"
#include "unitconv.h"
#include "loaddata.h"
#include "cjoslib.h"
#include "editunits.h"


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

/* Local defines */

/* constant definitions */
/* Res file */
#define EDITUNITS_TEMPLATE	((toolbox_id)"DataEdit")
#define EDITUNITS_GROUP         ((toolbox_c)0x02)
#define EDITUNITS_SET           ((toolbox_c)0x06)
#define EDITUNITS_UNIT_BASE     ((toolbox_c)0x0A)
#define EDITUNITS_FACTOR_BASE   ((toolbox_c)0x13)
#define EDITUNITS_GROUP_INC     ((toolbox_c)0x00)
#define EDITUNITS_GROUP_DEC     ((toolbox_c)0x01)
#define EDITUNITS_SET_INC       ((toolbox_c)0x04)
#define EDITUNITS_SET_DEC       ((toolbox_c)0x05)
#define uevent_EDITUNITS_SAVE   0x100u
#define EDITUNITSMENU_TEMPLATE	((toolbox_id)"DataEditM")
#define uevent_EDITUNITSMENU_ADDGROUP     0x101
#define uevent_EDITUNITSMENU_DELETEGROUP  0x102
#define uevent_EDITUNITSMENU_ADDSET       0x103
#define uevent_EDITUNITSMENU_DELETESET    0x104
#define uevent_EDITUNITSMENU_SAVE         0x105
#define uevent_EDITUNITSMENU_INSERTSET    0x106
#define uevent_EDITUNITSMENU_INSERTGROUP  0x107
#define uevent_EDITUNITSMENU_INSERTITEM   0x108
#define uevent_EDITUNITSMENU_DELETEITEM   0x109

#define UNITITEMS_MAX_INDEX MAX_UC_UNITITEMS - 1

#define BUF_SIZE 96
#define TBUF_SIZE 24
#define UNITNAME_LENGTH 20
#define UNITFACTOR_TABPOS UNITNAME_LENGTH + 2

#define ADD_AT_END FALSE
#define INSERT     TRUE



/************************************************************/
/* global variables */
static toolbox_o Editunits_winid;
static wimp_w    Editunits_winhandle;
static toolbox_position Editunits_win_pos;
static toolbox_o Editunits_menuid;
static osbool    Editunits_is_open;


static unitconv_data ed_data;
  static int suffix;

/************************************************************/
/* Forward function declarations */
static void Editunits_SetupSet ( int gr, int set );
static void Editunits_SetupGroup ( int gr );

/* The Functions */


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

static osbool Editunits_WriteBackSet (void)
{
  conv_group *edptr ;
  int set;
  char tbuf [TBUF_SIZE];
  char tbuf2 [TBUF_SIZE];
  int len, len2;
  int items;
  int i;


  edptr = ed_data.group [ed_data.current_group];
  set = ed_data.current_set;

  /* Read Set title */
  len = writablefield_get_value ( 0, Editunits_winid,
                                  EDITUNITS_SET, tbuf, TBUF_SIZE ) ;
  if (len > 1)
  {
    strcpy ( edptr->set[set].set_name, tbuf );
  }
  else
  {
    /* Report error and exit */
    Msg_Warn ( SETNAMEBLANK);
    return FALSE;
  }
  items = 0;
  for ( i = 0; i < MAX_UC_UNITITEMS; i++)
  {
    len = writablefield_get_value ( 0, Editunits_winid,
                                    i + EDITUNITS_UNIT_BASE, tbuf, TBUF_SIZE ) ;
    len2 = writablefield_get_value ( 0, Editunits_winid,
                                     i + EDITUNITS_FACTOR_BASE, tbuf2, TBUF_SIZE ) ;
    if ((len > 1) && (len2 > 1))
    {
      strcpy ( edptr->set[set].item[items].unit_name, tbuf );
      strcpy ( edptr->set[set].item[items].factor_txt, tbuf2 );
      items++;
    }
    else
    {
      if (len > 1)
      {
        /* Second field blank */
        Msg_Warn ( NOFACTOR );
        return (FALSE);
      }
      else
      {
        if (len2 > 1)
        {
          /* first field blank */
          Msg_Warn ( NOUNITNAME );
          return (FALSE);
        }
      }
    }
  }
  edptr->set[ set ].no_of_items = items;
  return (items > 0);
}



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

static osbool Editunits_WriteBackGroup (void)
{
  conv_group *edptr ;
  char tbuf [TBUF_SIZE];
  int len;

  edptr = ed_data.group [ed_data.current_group];
  /* Read Group title */
  len = writablefield_get_value ( 0, Editunits_winid,
                                  EDITUNITS_GROUP, tbuf, TBUF_SIZE ) ;
  if (len > 1)
  {
    strcpy ( edptr->group_name, tbuf );
  }
  else
  {
    /* Report error and exit */
    Msg_Warn ( NOGROUPNAME );
    return FALSE;
  }
  /* Now write back the displayed set */
  return (Editunits_WriteBackSet ());
}


/* Function to construct a line of data nicely tabbed with spaces, and
   write it to file. The first argument is always a string, the second
   may be string or int. A null pointer for the second string should be
   passed in order for the int to be used. The separating character
   is passed as the last argument */

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

static void Editunits_WriteTabbedLine ( char *buf,
                                        char *s1,
                                        char *s2,
                                        int i1,
                                        char separator,
                                        FILE *fh )
{
  char *p;
  char *q;
  size_t len;
  char tmp[MAX_LEN_ITEMNAME + 2];
  int i;

  sprintf (tmp, "%s%c", s1, separator );
  /* The first string may conceivably have a % or \ character in it.
   * This will screw up the use of the fprintf function to write
   * the data to the file.
   * Thus we need to escape such characters
  */
  len = strlen (tmp);
  p = buf;
  q = tmp;
  while (*q)
  {
    *p = *q;
    if ((*q == '%') || ( *q == '\\' ))
    {
      p++;
      *p = *q;
    }
    p++;
    q++;
  }
  /* now we need to insert the spaces to tab to next field */
  for ( i = 0; i < (UNITFACTOR_TABPOS - (int)len); i++ )
  {
    p[0] = ' ';
    p++;
  }
  if (s2)
  {
    sprintf ( p, "%s\n", s2 );
  }
  else
  {
    sprintf ( p, "%d\n", i1 );
  }
  fprintf (fh, buf );
  return;
}




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

static void delete_object ( char * name )
{
  bits load, exec;
  fileswitch_attr attr;
  int size;
  fileswitch_object_type type;

  type = osfile_delete ( name, &load, &exec, &size, &attr );
  return;
}



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

static void Editunits_ReloadCustomData (void)

{
  int i;
  int length;
  char filename[256];
  fileswitch_object_type type;
  bits load, exec, filetype;
  fileswitch_attr attr;
  char *filebuffer;
  osbool load_file;

  /* Now we need to free current data and reload the edited data */

  /* Release the memory used for the units */
  for ( i = 0; i <= MAX_UC_MENU_ENTRIES; i++ )
  {
    if (uc_data.group[i] != NULL)
    {
      free (uc_data.group[i]);
      uc_data.group[i] = NULL;
    }
  }
  /* Now reload the newly edited data - the custom file should exist,
  ** because it was just saved, but check anyway */
  strcpy ( filename, CUSTOM_FILE );
  type = osfile_read_stamped_no_path ( filename, &load, &exec, &length,
                                       &attr, &filetype ) ;
  if ( type == 1 )
  {
    filebuffer = (char *)malloc ( length ) ;
    if ( filebuffer !=0 )
    {
      /* load the file into memory */
      type = osfile_load_stamped_no_path ( filename, (byte *)filebuffer,
                                           &load, &exec, &length, &attr ) ;
      /* now process the file */
      load_file = Unitconv_ProcessDatafile ( filebuffer, &uc_data ) ;

      // free the buffer
      free ( filebuffer ) ;
    }
  }

  /* now set up special conversions */
  defaultdata_fill_special_arrays();

  /* Now do a unitconv window set up */
  Unitconv_TrashWindow ();
  Unitconv_Initialise ();

  return;
}





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

static void Editunits_SaveCustomData ( char * fname, unitconv_data * ucdat )
{
  FILE  *outfile;
  time_t current ;
  struct tm *converted_time ;
  size_t length ;
  char buf[BUF_SIZE];
  int group;
  int set;
  int item;
  conv_group *edptr ;



  /* Now we can get on with actually saving the new data file */
  outfile = fopen (fname, "w" ) ;
  if (outfile == NULL)
  {
    /* file would not open or create */
    Msg_Warn ( CUSTOMNOWRITE ) ;
    return;
  }
  fprintf (outfile, "#UnitConv:version 1\n") ;
  /* now get the time and convert to suitable format */
  current = time (NULL) ;
  converted_time = localtime (&current) ;
  length = strftime (buf, BUF_SIZE, "# File written at %H:%M:%S on %d %b %Y",
                                        converted_time) ;
  fprintf (outfile, "%s\n\n", buf) ;
  fprintf (outfile, "total_groups:%d\n\n", ed_data.no_of_groups);
  for ( group = 0; group < ucdat->no_of_groups; group++)
  {
    fprintf (outfile, "#------------Start of group %d------------\n\n", group + 1);
    edptr = ucdat->group[group];
    Editunits_WriteTabbedLine ( buf,
                                "Group_title",
                                edptr->group_name,
                                0,
                                ':',
                                outfile );
    Editunits_WriteTabbedLine ( buf,
                                "Group_entries",
                                NULL,
                                edptr->no_of_sets,
                                ':',
                                outfile );
    fprintf (outfile, "\n");
    for ( set = 0; set < edptr->no_of_sets; set++)
    {
      fprintf (outfile, "#---------------------------------------%d\n", set + 1);
      fprintf (outfile, "group_item:\n\n");
//      fprintf (outfile, "#Set name, and number of units\n");
      Editunits_WriteTabbedLine ( buf,
                                  edptr->set[set].set_name,
                                  NULL,
                                  edptr->set[set].no_of_items,
                                  ',',
                                  outfile );
      fprintf (outfile, "\n#Units and corresponding factors\n" );
      for (item = 0; item < edptr->set[set].no_of_items; item++)
      {
        Editunits_WriteTabbedLine ( buf,
                                    edptr->set[set].item[item].unit_name,
                                    edptr->set[set].item[item].factor_txt,
                                    0,
                                    ',',
                                    outfile );
      }
      fprintf (outfile, "\n");
    }
  }
  fprintf (outfile, "#End of data\n\n");
  fclose (outfile) ;
  osfile_set_type (fname, osfile_TYPE_TEXT) ;


/////  Editunits_ReloadCustomData ();


////  Msg_Warn (RESTART);
  return;
}






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

static void Editunits_DumpUnitConvData (void)

{
  char filename[256];

  /* Make filename and inc the suffix */
  sprintf (filename, "%sDataDump%d", DATA_FILE_PATH, suffix++);
  Editunits_SaveCustomData ( filename, &uc_data );

  return;
}





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

static void Editunits_SaveConvData ( void )
{
  FILE  *outfile;
  time_t current ;
  struct tm *converted_time ;
  size_t length ;
  char buf[BUF_SIZE];
  int group;
  int set;
  int item;
  conv_group *edptr ;


  /* First ensure any changes to visible display are stored */
  if (!Editunits_WriteBackGroup ())
    return;

  /* Now deal with backup of custom data file */
  if ( CJLfile_file_exists ( CUSTOM_FILE_BACKUP ) )
  {
    /* delete the existing backup file */
    delete_object ( CUSTOM_FILE_BACKUP );
  }

  /* Now rename existing custom data file to backup name, if it exists */
  if ( CJLfile_file_exists ( CUSTOM_FILE ))
  {
    osfscontrol_rename ( CUSTOM_FILE, CUSTOM_FILE_BACKUP );
  }

  /* Now we can get on with actually saving the new data file */
  outfile = fopen (CUSTOM_FILE, "w" ) ;
  if (outfile == NULL)
  {
    /* file would not open or create */
    Msg_Warn ( CUSTOMNOWRITE ) ;
    return;
  }
  fprintf (outfile, "#UnitConv:version 1\n") ;
  /* now get the time and convert to suitable format */
  current = time (NULL) ;
  converted_time = localtime (&current) ;
  length = strftime (buf, BUF_SIZE, "# File written at %H:%M:%S on %d %b %Y",
                                        converted_time) ;
  fprintf (outfile, "%s\n\n", buf) ;
  fprintf (outfile, "total_groups:%d\n\n", ed_data.no_of_groups);
  for ( group = 0; group < ed_data.no_of_groups; group++)
  {
    fprintf (outfile, "#------------Start of group %d------------\n\n", group + 1);
    edptr = ed_data.group[group];
    Editunits_WriteTabbedLine ( buf,
                                "Group_title",
                                edptr->group_name,
                                0,
                                ':',
                                outfile );
    Editunits_WriteTabbedLine ( buf,
                                "Group_entries",
                                NULL,
                                edptr->no_of_sets,
                                ':',
                                outfile );
    fprintf (outfile, "\n");
    for ( set = 0; set < edptr->no_of_sets; set++)
    {
      fprintf (outfile, "#---------------------------------------%d\n", set + 1);
      fprintf (outfile, "group_item:\n\n");
//      fprintf (outfile, "#Set name, and number of units\n");
      Editunits_WriteTabbedLine ( buf,
                                  edptr->set[set].set_name,
                                  NULL,
                                  edptr->set[set].no_of_items,
                                  ',',
                                  outfile );
      fprintf (outfile, "\n#Units and corresponding factors\n" );
      for (item = 0; item < edptr->set[set].no_of_items; item++)
      {
        Editunits_WriteTabbedLine ( buf,
                                    edptr->set[set].item[item].unit_name,
                                    edptr->set[set].item[item].factor_txt,
                                    0,
                                    ',',
                                    outfile );
      }
      fprintf (outfile, "\n");
    }
  }
  fprintf (outfile, "#End of data\n\n");
  fclose (outfile) ;
  osfile_set_type (CUSTOM_FILE, osfile_TYPE_TEXT) ;


  Editunits_ReloadCustomData ();

////  Editunits_DumpUnitConvData ();
  Msg_Warn (RESTART);
  return;
}




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

static osbool Editunits_KeyPress ( wimp_event_no event,
                                   wimp_block * blk,
                                   toolbox_block * id_block,
                                   void * handle )
{
  wimp_key *wkey;

  wkey = (wimp_key*)blk;
  if (wkey->c == wimp_KEY_F1)
    IconbarShowHelp();
  else
  if (( wkey->c < 32 ) || ( wkey->c > 126 ))
    wimp_process_key (wkey->c);

  return (TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (event);
}



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

static osbool Editunits_MouseClickHandler (wimp_event_no event,
                                          wimp_block *blk,
                                          toolbox_block *id_block,
                                          void *handle)
{
  wimp_pointer *mouse_blk;
  toolbox_o win_id ;
  toolbox_c cmp_id ;
  conv_group *edptr ;
  int inc = 1;


  edptr = ed_data.group[ed_data.current_group];

  mouse_blk = (wimp_pointer *)blk;
  /* was it select or adjust? */
  if (( mouse_blk->buttons & wimp_CLICK_ADJUST ) == wimp_CLICK_ADJUST )
    inc = -1;
  /* Find the Toolbox component id clicked */
  window_wimp_to_toolbox ( 0,
                           Editunits_winhandle,
                           mouse_blk->i,
                           &win_id,
                           &cmp_id ) ;

  switch (cmp_id)
  {
    case EDITUNITS_GROUP_DEC:
      inc = -inc;
    case EDITUNITS_GROUP_INC:
      if (inc > 0)
      {
        if (ed_data.current_group < ( ed_data.no_of_groups - 1 ))
        {
          if (Editunits_WriteBackGroup ())
            Editunits_SetupGroup ( ed_data.current_group + 1 );
        }
      }
      else
      {
        if (ed_data.current_group > 0)
        {
          if (Editunits_WriteBackGroup ())
            Editunits_SetupGroup ( ed_data.current_group - 1 );
        }
      }
      break;

    case EDITUNITS_SET_DEC:
      inc = -inc;
    case EDITUNITS_SET_INC:
      if (inc > 0)
      {
        if (ed_data.current_set < ( edptr->no_of_sets - 1 ))
        {
          if (Editunits_WriteBackSet ())
            Editunits_SetupSet ( ed_data.current_group, ed_data.current_set + 1 );
        }
      }
      else
      {
        if (ed_data.current_set > 0)
        {
          if (Editunits_WriteBackSet ())
            Editunits_SetupSet ( ed_data.current_group, ed_data.current_set - 1 );
        }
      }
      break;

  }

  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (event);
}


/*******************************************************************/
/* Functions to Insert and Delete items
   This is all done solely using the contents of the writable fields
   since there is no way of determining whether the user has already
   made some changes to some of the fields
*/
/*******************************************************************/


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

static void Editunits_InsertItem (void)
{
  wimp_caret cpos;
  toolbox_o wid;
  toolbox_c cmp;
  int row;
  char tbuf[TBUF_SIZE];
  int len, len2;
  int i;

  /* Get caret position */
  wimp_get_caret_position (&cpos);
  window_wimp_to_toolbox ( 0, cpos.w, cpos.i, &wid, &cmp );
  if ( wid == Editunits_winid )
  {
    if ((cmp >= EDITUNITS_UNIT_BASE) && (cmp < (EDITUNITS_FACTOR_BASE + 9)))
    {
      /* in one of the unit or factor fields */
      row = (int)cmp - EDITUNITS_UNIT_BASE;
      if (row > 8) row -= 9;
      /* Is the last row of data blank? */
      len = writablefield_get_value ( 0, Editunits_winid,
                         UNITITEMS_MAX_INDEX + EDITUNITS_UNIT_BASE, tbuf, TBUF_SIZE);
      len2 = writablefield_get_value ( 0, Editunits_winid,
                         UNITITEMS_MAX_INDEX + EDITUNITS_FACTOR_BASE, tbuf, TBUF_SIZE);
      if ((len == 1) && (len2 == 1))
      {
        /* ok to continue */
        if (row < UNITITEMS_MAX_INDEX)
        {
          /* not the last row */
          /* move items up */
          for (i = UNITITEMS_MAX_INDEX; i > row; i--)
          {
            writablefield_get_value ( 0, Editunits_winid,
                                      i - 1 + EDITUNITS_UNIT_BASE, tbuf, TBUF_SIZE );
            writablefield_set_value ( 0, Editunits_winid,
                                      i + EDITUNITS_UNIT_BASE, tbuf );
            writablefield_get_value ( 0, Editunits_winid,
                                      i - 1 + EDITUNITS_FACTOR_BASE, tbuf, TBUF_SIZE );
            writablefield_set_value ( 0, Editunits_winid,
                                      i + EDITUNITS_FACTOR_BASE, tbuf );
          }
          /* now clear the 'inserted' fields */
          writablefield_set_value (0, Editunits_winid, row + EDITUNITS_UNIT_BASE, "");
          writablefield_set_value (0, Editunits_winid, row + EDITUNITS_FACTOR_BASE, "");
        }
      }
      else
      {
        Msg_Warn (NOMOREITEMS);
      }
    }
  }
  else
  {
    Msg_Warn (CARET);
  }
  return;
}





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

static void Editunits_DeleteItem (void)
{
  wimp_caret cpos;
  toolbox_o wid;
  toolbox_c cmp;
  int row;
  int i;
  char tbuf[TBUF_SIZE];

  /* Get caret position */
  wimp_get_caret_position (&cpos);
  window_wimp_to_toolbox ( 0, cpos.w, cpos.i, &wid, &cmp );
  if ( wid == Editunits_winid )
  {
    if ((cmp >= EDITUNITS_UNIT_BASE) && (cmp < (EDITUNITS_FACTOR_BASE + 9)))
    {
      /* in one of the unit or factor fields */
      row = (int)cmp - EDITUNITS_UNIT_BASE;
      if (row > 8) row -= 9;

      if (row < UNITITEMS_MAX_INDEX)
      {
        /* not the last row */
        /* move later items down */
        for (i = row; i < UNITITEMS_MAX_INDEX; i++)
        {
          writablefield_get_value ( 0, Editunits_winid,
                                    i + 1 + EDITUNITS_UNIT_BASE, tbuf, TBUF_SIZE ) ;
          writablefield_set_value ( 0, Editunits_winid,
                                    i + EDITUNITS_UNIT_BASE, tbuf );
          writablefield_get_value ( 0, Editunits_winid,
                                    i + 1 + EDITUNITS_FACTOR_BASE, tbuf, TBUF_SIZE ) ;
          writablefield_set_value ( 0, Editunits_winid,
                                    i + EDITUNITS_FACTOR_BASE, tbuf );
        }
      }
      /* now clear the last field */
      writablefield_set_value ( 0,
                                Editunits_winid,
                                UNITITEMS_MAX_INDEX + EDITUNITS_UNIT_BASE,
                                "" );
      writablefield_set_value ( 0,
                                Editunits_winid,
                                UNITITEMS_MAX_INDEX + EDITUNITS_FACTOR_BASE,
                                "" );

    }

  }
  else
  {
    Msg_Warn (CARET);
  }
  return;
}




/**********************************************/
/* Functions to Add, Insert and Delete Sets */
/**********************************************/


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

static void Editunits_DeleteSet (void)
{
  conv_group *edptr ;
  int s;
  conv_set *s1, *s2;

  edptr = ed_data.group[ed_data.current_group];

  if (!CJL_MsgQueryF ("Q303", NULL,
          "Do you wish to continue to delete the %s set?",
          edptr->set[ed_data.current_set].set_name))
     return;

  if ( ed_data.current_set == edptr->no_of_sets - 1 )
  {
    /* We are showing the last set. We just need to show the previous set
    ** and reduce the total sets by one */
    edptr->no_of_sets -= 1;
    Editunits_SetupSet ( ed_data.current_group, edptr->no_of_sets - 1 );
  }
  else
  {
    /* We are not at the last set - must delete the current one and copy
    ** the later ones down */
    for ( s = ed_data.current_set; s < edptr->no_of_sets - 1; s++ )
    {
      s1 = &edptr->set [s];
      s2 = &edptr->set [s+1];
      *s1 = *s2;
    }
    edptr->no_of_sets -= 1;
    Editunits_SetupSet ( ed_data.current_group, ed_data.current_set );
  }
  return;
}




/* Add a new set */

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

static void Editunits_InsertSet2 (osbool insert)

{
  conv_group *edptr ;
  int set;
  conv_set *s1, *s2;

  if (Editunits_WriteBackSet ())
  {
    edptr = ed_data.group[ed_data.current_group];
    /* Can we add another set */
    if (edptr->no_of_sets < MAX_UC_UNITSETS)
    {
      if (insert)
      {
        /* First copy the current and later sets up one place */
        /* The new added top location is = no_of_sets */
        for ( set = edptr->no_of_sets; set > ed_data.current_set; set-- )
        {
          s1 = &edptr->set [set];
          s2 = &edptr->set [set - 1];
          *s1 = *s2;
        }
        /* the active set id */
        set = ed_data.current_set;
      }
      else
      {
        /* the active set id */
        set = edptr->no_of_sets;
      }
      edptr->no_of_sets++;
      edptr->set[set].no_of_items = 0;
      edptr->set[set].set_name[0] = 0x00;

      Editunits_SetupSet ( ed_data.current_group, set );

    }
    else
    {
      Msg_Warn (NOMORESET);
    }
  }
  return;
}






/* Add a new set before the set that is being displayed */

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

static void Editunits_InsertSet (void)
{
  Editunits_InsertSet2 (INSERT);
  return;
}




/* Add a new set to the end of the list */

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

static void Editunits_AddSet (void)
{
  Editunits_InsertSet2 (ADD_AT_END);
  return;
}


/**********************************************/
/* Functions to Add, Insert and Delete Groups */
/**********************************************/

/* Delete the currently displayed group */

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

static void Editunits_DeleteGroup (void)
{
  int g;


  if (!CJL_MsgQueryF ("Q305", NULL, "Do you wish to continue to delete this "
                             "complete group?"))
     return;

  if (Editunits_WriteBackGroup ())
  {
    /* Free the memory used for this group */
    free ( ed_data.group[ed_data.current_group] );
    /* Now move the other group data down */
    for ( g = ed_data.current_group; g < ed_data.no_of_groups - 1; g++)
    {
      ed_data.group [g] = ed_data.group [g + 1];
    }
    /* Null the top group pointer */
    ed_data.no_of_groups--;
    ed_data.group [ed_data.no_of_groups] = NULL;
    Editunits_SetupGroup ( ed_data.current_group );
  }
  return;
}





/************************************************************/
/* Called by both insert group and add at end */

static void Editunits_AddGroup2 (osbool insert)
{
  conv_group *edptr ;
  int g;


  if (Editunits_WriteBackGroup ())
  {
    if ( ed_data.no_of_groups < MAX_UC_MENU_ENTRIES )
    {
      edptr = (conv_group *)calloc (1, sizeof (struct conv_group));
      if ( edptr == NULL )
      {
        Msg_Warn (GROUPNOMEM);
        return;
      }

      if (insert)
      {
        /* Move the current group data up to leave a hole - just need to
        ** move the pointers, not the individual data */
        for ( g = ed_data.no_of_groups; g > ed_data.current_group; g--)
        {
          ed_data.group [g] = ed_data.group [g - 1];
        }
        /* Now copy the new pointer into the hole */
        ed_data.group[ed_data.current_group] = edptr;
      }
      else
      {
        ed_data.group[ed_data.no_of_groups] = edptr;
      }

      ed_data.no_of_groups++;
      edptr->no_of_sets = 1;
      edptr->set [ 0 ].no_of_items = 0;
      if (insert)
      {
        Editunits_SetupGroup ( ed_data.current_group );
      }
      else
      {
        Editunits_SetupGroup ( ed_data.no_of_groups - 1 );
      }
    }
    else
    {
      Msg_Warn (NOMOREGROUP);
    }
  }
  return;
}




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

static void Editunits_InsertGroup (void)
{
  Editunits_AddGroup2 (INSERT);
  return;
}





/* Add a new group to the end of the list */

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

static void Editunits_AddGroup (void)
{
  Editunits_AddGroup2 (ADD_AT_END);
  return;
}





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

static osbool Editunits_MenuMainHandler ( bits event_code,
                                          toolbox_action *action,
                                          toolbox_block *id_block,
                                          void *handle )
{
  switch (event_code)
  {
    case uevent_EDITUNITSMENU_ADDGROUP:
      Editunits_AddGroup ();
      break;

    case uevent_EDITUNITSMENU_INSERTGROUP:
      Editunits_InsertGroup ();
      break;

    case uevent_EDITUNITSMENU_DELETEGROUP:
      Editunits_DeleteGroup ();
      break;

    case uevent_EDITUNITSMENU_ADDSET:
      Editunits_AddSet ();
      break;

    case uevent_EDITUNITSMENU_INSERTSET:
      Editunits_InsertSet ();
      break;

    case uevent_EDITUNITSMENU_DELETESET:
      Editunits_DeleteSet ();
      break;

    case uevent_EDITUNITSMENU_INSERTITEM:
      Editunits_InsertItem ();
      break;

    case uevent_EDITUNITSMENU_DELETEITEM:
      Editunits_DeleteItem ();
      break;

    case uevent_EDITUNITSMENU_SAVE:
      Editunits_SaveConvData ();
      break;

    case action_MENU_DIALOGUE_COMPLETED:
      event_deregister_toolbox_handler ( Editunits_menuid,
                                         action_ANY,
                                         Editunits_MenuMainHandler,
                                         NULL);
      break;

  }

  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (action);
  IGNORE (event_code);
}






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

static osbool Editunits_MenuOpeningHandler ( bits event_code,
                                             toolbox_action *action,
                                             toolbox_block *id_block,
                                             void *handle )
{
  event_register_toolbox_handler ( Editunits_menuid,
                                   action_ANY,
                                   Editunits_MenuMainHandler,
                                   NULL);

  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (action);
  IGNORE (event_code);
}





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

static osbool Editunits_Handler ( bits event_code,
                                  toolbox_action *action,
                                  toolbox_block *id_block,
                                  void *handle )
{
  int i;


  switch (event_code)
  {
    case action_WINDOW_ABOUT_TO_BE_SHOWN:
      Editunits_is_open = TRUE;
      break;

    case uevent_EDITUNITS_SAVE:
      Editunits_SaveConvData ();
      break;

    case action_WINDOW_DIALOGUE_COMPLETED:
      /* get current position of Gases window */
      CJL_GetTopLeftWinPos ( Editunits_winhandle, &Editunits_win_pos );

      event_deregister_toolbox_handler ( Editunits_menuid,
                                         action_MENU_ABOUT_TO_BE_SHOWN,
                                         Editunits_MenuOpeningHandler,
                                         NULL);
      toolbox_delete_object (0, Editunits_menuid);
      Editunits_menuid = toolbox_NULL_OBJECT;

      event_deregister_toolbox_handler ( Editunits_winid,
                                         action_ANY,
                                         Editunits_Handler,
                                         NULL);
      event_deregister_wimp_handler ( Editunits_winid,
                                      wimp_MOUSE_CLICK,
                                      Editunits_MouseClickHandler,
                                      NULL);
      event_deregister_wimp_handler ( Editunits_winid,
                                      wimp_KEY_PRESSED,
                                      Editunits_KeyPress,
                                      NULL );
      toolbox_delete_object ( 0, Editunits_winid );
      Editunits_winid = toolbox_NULL_OBJECT;
      Editunits_is_open = FALSE;
      /* Release the memory used for the units */
      for ( i = 0; i < ed_data.no_of_groups; i++ )
      {
        if (ed_data.group[i] != NULL)
        {
          free (ed_data.group[i]);
          ed_data.group[i] = NULL;
        }
      }
      break;
  }

  return(TRUE);

  /* for compiler */
  IGNORE (handle);
  IGNORE (id_block);
  IGNORE (action);
}




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

static void Editunits_SetupSet ( int gr, int set )
{
  conv_group *edptr ;
  int i;

  ed_data.current_set = set;
  edptr = ed_data.group[gr];
  /* fill in the set name */
  writablefield_set_value ( 0,
                            Editunits_winid,
                            EDITUNITS_SET,
                            edptr->set[set].set_name );

  /* fill in the data */
  for (i = 0; i < edptr->set[ set ].no_of_items ; i++)
  {
    writablefield_set_value ( 0,
                              Editunits_winid,
                              i + EDITUNITS_UNIT_BASE,
                              edptr->set[set].item[i].unit_name );
    writablefield_set_value ( 0,
                              Editunits_winid,
                              i + EDITUNITS_FACTOR_BASE,
                              edptr->set[set].item[i].factor_txt );

  }
  /* ...and blank the rest */
  for ( i = edptr->set[ set ].no_of_items ; i < MAX_UC_UNITITEMS; i++)
  {
    writablefield_set_value ( 0,
                              Editunits_winid,
                              i + EDITUNITS_UNIT_BASE,
                              "" );
    writablefield_set_value ( 0,
                              Editunits_winid,
                              i + EDITUNITS_FACTOR_BASE,
                              "" );
  }
  if ( Editunits_is_open )
    gadget_set_focus ( 0, Editunits_winid, EDITUNITS_SET );
  return;
}




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

static void Editunits_SetupGroup ( int gr )
{
  conv_group *edptr ;


  ed_data.current_group = gr;
  edptr = ed_data.group[gr];
  /* fill in the group name */
  writablefield_set_value ( 0, Editunits_winid, EDITUNITS_GROUP, edptr->group_name );
  /* Now set up the first set data in the group */
  Editunits_SetupSet ( gr, 0 );
  if ( Editunits_is_open )
    gadget_set_focus ( 0, Editunits_winid, EDITUNITS_GROUP );
  return;
}




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

static void Editunits_Setup ( void )
{
  /* Read current units data
  ** claim memory to suit units data struct
  ** copy current units data into data struct
  ** display the conversion data for group 0, set 0
  */

  int i;
  conv_group *ucptr ;
  conv_group *edptr ;

  /* We do not edit the special group, therefore ignore the last
  ** special group */
  ed_data.no_of_groups = uc_data.no_of_groups - 1;

  /* Copy the data already loaded */
  for ( i = 0; i < ed_data.no_of_groups; i++ )
  {
    /* reserve some memory for each group */
    ed_data.group[i] = ( conv_group * ) malloc ( sizeof ( struct conv_group ));
    /* set the pointers */
    ucptr = uc_data.group[i];
    edptr = ed_data.group[i];
    /* copy the contents */
    *edptr = *ucptr;
  }
  /* Now set up the window with the data for the first group */
  Editunits_SetupGroup ( 0 );
  return;
}




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

void Editunits_Open ( void )
{
  if ( Editunits_winid == toolbox_NULL_OBJECT)
  {
    Editunits_winid = toolbox_create_object ( 0, EDITUNITS_TEMPLATE);
    Editunits_winhandle = window_get_wimp_handle (0, Editunits_winid);
    event_register_toolbox_handler ( Editunits_winid,
                                     action_ANY,
                                     Editunits_Handler,
                                     NULL);
    event_register_wimp_handler ( Editunits_winid,
                                  wimp_MOUSE_CLICK,
                                  Editunits_MouseClickHandler,
                                  NULL);
    event_register_wimp_handler ( Editunits_winid,
                                  wimp_KEY_PRESSED,
                                  Editunits_KeyPress,
                                  NULL );
    /* Also initialise the attached menu */
    if (Editunits_menuid == toolbox_NULL_OBJECT)
    {
      Editunits_menuid = toolbox_create_object ( 0, EDITUNITSMENU_TEMPLATE);
      event_register_toolbox_handler ( Editunits_menuid,
                                       action_MENU_ABOUT_TO_BE_SHOWN,
                                       Editunits_MenuOpeningHandler,
                                       NULL);
    }
    Editunits_Setup ();

  }
  if (Editunits_win_pos.top_left.x == 0)
  {
    toolbox_show_object ( 0,
                          Editunits_winid,
                          toolbox_POSITION_CENTRED,
                          0,
                          toolbox_NULL_OBJECT,
                          toolbox_NULL_COMPONENT ) ;
  }
  else
  {
    toolbox_show_object (0,
                        Editunits_winid,
                        toolbox_POSITION_TOP_LEFT,
                        &Editunits_win_pos,
                        toolbox_NULL_OBJECT,
                        toolbox_NULL_COMPONENT ) ;
  }
  return;
}



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





/*************  End of c.editunits  ***********************/

