/* CastAWote local
 *
 * Implements a 'local' version of CastAWote. For SysOps mainly.
 *
 *  (C) Copyright 1996 Richard Murray
 *
 * Version 1.07, Mon, 26th Aug 1996
 * Amended 2009/03/14 to implement change to "CastA*W*ote".
 *
 *
 *
 *
 * My first attempt at a true multitasking program, without the weird hacks.
 * This is actually the second version. First time I used RISC_OSLib. Second time
 * (now) I used DeskLib and did more in eight hours of 'hacking' than I managed in
 * four days of RISC_OSLib - hey, and RISC_OSLib has a manual! [1] Eight hours
 * for 'this' may seem lame, but I've *NEVER* coded in DeskLib until today. Way-hey!
 * I think I'm gonna like this. Sex in binary? Erm... Not quite, but close!
 *
 * [1] - Which is incorrect, sparse and generally not-so-good.
 *
 * NOTE: IMPORTANT: When I say DeskLib, I mean release 2.30 and not Desk or
 *                  any clones which may introduce compatibility problems.
 */



/* Included libraries */
#include "DeskLib:WimpSWIs.h"          /* Low-level WIMP commands         */
#include "DeskLib:Window.h"            /* Window handling automation      */
#include "DeskLib:Core.h"              /* useful core functions           */
#include "DeskLib:Dialog2.h"           /* windows on menus made easy      */
#include "DeskLib:Error.h"             /* Error despatcher                */
#include "DeskLib:Event.h"             /* Event despatcher                */
#include "DeskLib:EventMsg.h"          /* Wimp Message event dispatcher   */
#include "DeskLib:File.h"              /* Low level file handling         */
#include "DeskLib:Handler.h"           /* Default/example event handlers  */
#include "DeskLib:Icon.h"              /* Icon handling automation        */
#include "DeskLib:Kbd.h"               /* Read hotkeys                    */
#include "DeskLib:Menu.h"              /* Menu create & show support      */
#include "DeskLib:Msgs.h"              /* Message translation code        */
#include "DeskLib:Resource.h"          /* Handles finding resource files  */
#include "DeskLib:Screen.h"            /* Getting screen size info etc    */
#include "DeskLib:Template.h"          /* Template loading and caching    */
/* #include "DeskLib:Time.h" */        /* "TIME"                          */
#include <stdarg.h>		       /* Variable-args routines 	  */
#include <stdlib.h>                    /* Standard ANSI library           */
#include <stdio.h>		       /* Standard ANSI I/O functions 	  */
#include <string.h>                    /* ANSI string handling            */
#include "ctype.h"		       /* C string types 		  */
#include "kernel.h"		       /* For SWI calls 		  */
/* Probably more to come... in time... */




/* Preprocessor definitions */
#define mainmenu_INFO            0
#define mainmenu_CONFIG    	 1
#define mainmenu_QUIT            2
#define VoteModule_LoadVotes     0x16F00
#define VoteModule_SaveVotes     0x16F01
#define VoteModule_Refresh       0x16F02
#define VoteModule_GetVote       0x16F03
#define VoteModule_PutVote       0x16F04
#define VoteModule_AddVoteOpt    0x16F05
#define VoteModule_GetVotesCount 0x16F06
#define VoteModule_Statistics	 0x16F07
#define VoteModule_VoteOp	 0x16F08
#define VoteModule_AmendVote	 0x16F09
#define VoteModule_GetVoteHeader 0x16F0A
#define OS_ReadMonotonicTime     0x00042
#define wimp_sendmessage         0x16F00


/* Variable definitions */
struct votebuffer {
   char creator[61];
   char system[61];
   int  voted_for;
   char question[61];
   char software_opts[61];
   char addinfo_one[61];
   char addinfo_two[61];
   char addinfo_three[61];
   char option_one_text[61];
   int  option_one_rslt;
   char option_two_text[61];
   int  option_two_rslt;
   char option_three_text[61];
   int  option_three_rslt;
   char option_four_text[61];
   int  option_four_rslt;
   char option_five_text[61];
   int  option_five_rslt;
   char option_six_text[61];
   int  option_six_rslt;
   char option_seven_text[61];
   int  option_seven_rslt;
   char option_eight_text[61];
   int  option_eight_rslt;
   char reserved_data_block[24];
   char vote_is_local[1];
   char vote_is_hidden[1];
   char vote_is_inaccessible[1];
   char vote_is_locked[1];
   char vote_flags_other[12];
   int  vote_data_crc;
   char vote_data_key[1];
   char reserved_developer[1];
   char reserved_user[1];
   char vote_datafile_type[1];
};
typedef struct votebuffer votebuffer;
static struct votebuffer *votedata = NULL; /* Votedata is our 'in use' vote definition */
static struct votebuffer addvote;
static int  maxvotes = 220;
static int  questions = 0;
static int  currvote = 1;
static int  junk_v = 0;
static char junk_s[255];
static char heapentry[255];
static char username[36] = "";
static char regname_pre[26] ={170, 177, 173, 186, 184, 182, 172, 171, 186, 173, 186, 187, 0};
static char regname_post[26] = "Angela Chase";
static char programversion[] = "1.07 (26-08-1996)";
static int  currentaction = FALSE;
static int  ticker = 0;
static int  registered = FALSE;
static int  debugging = FALSE;
static int  nvplog = 0;
static int  nvplocal = 0;
static int  confver = 0;
static char fidoaddr[20]="";
static window_handle vote_window;
static window_handle result_window;
static window_handle addvote_window;
static window_handle menu_window;
static window_handle genesis_window;
static window_handle pane_window;
static window_handle configure_window;
static BOOL finished = FALSE;
static menu_ptr mainmenu = NULL;
static dialog2_block *info;
static _kernel_swi_regs r_in, r_out;




/* Define procedures */
static void create_system_workspace(void);
static void lose_system_workspace(void);
static void initialise_resources(void);
static void build_windows(void);
static void initialise_windows_icons_events(void);
static BOOL open_handler(event_pollblock *event, void *reference);
static BOOL close_handler(event_pollblock *event, void *reference);
static BOOL null_handler(event_pollblock *event, void *reference);
static void pane_move(int leftpos, int toppos);
static BOOL mainmenu_choice(event_pollblock *event, void *reference);
static BOOL show_infowindow(event_pollblock *event, void *reference);
static BOOL iconbar_handler(event_pollblock *event, void *reference);
static void info_handler(dialog2_block *dialog2);
static BOOL menu_click_handler(event_pollblock *event, void *reference);
static BOOL vote_click_handler(event_pollblock *event, void *reference);
static BOOL addvote_click_handler(event_pollblock *event, void *reference);
static BOOL pane_click_handler(event_pollblock *event, void *reference);
static BOOL configure_click_handler(event_pollblock *event, void *reference);
static void default_icons_tout(void);
static void default_icons_vote(void);
static void default_icons_result(void);
static void default_icons_addvote(void);
static void default_icons_menu(void);
static void adjust_result_slider(int icon, int percentage);
static void actionerror(int thisaction, int actionnow);
static int  error_confirm(int errnum, char *errmsg, ...);
static int  searchforvote(void);
static int  searchforvoteback(void);
static int  fill_vote(void);
static void grabvotedata(void);
static int  calculate_results(void);
static void selectvote(int thisouropt);
static void debug(char *text, ...);
static BOOL desktop_save(event_pollblock *event, void *reference);
static void mostcommon(void);
static void lowercase(char string[]);
static void convertregistered(void);
static void setupconfigure(void);
static void loadconfigure(void);
static void saveconfigure(void);
static void readstring(FILE *file, char *string);
static int  readword(FILE *file);
static void writestring(FILE *file, char *string);
static void writeword(FILE *file, int num);
static void buildandprocessnewvote(void);
static void drawaddvote(void);



/* The program */
int main(int argc, char *argv[])
{
   _kernel_getenv("CAW$Local$Debug", junk_s, 255);
   lowercase(junk_s);
   if (strcmp(junk_s, "yes") == NULL) debugging = TRUE;

   create_system_workspace();
   initialise_resources();
   build_windows();
   initialise_windows_icons_events();
   convertregistered();

   loadconfigure();

   /* Set up vote bits. Get username, if passed. */
   if (argc == 2)
   {
      strcpy(username, argv[1]);
   }

   debug("CAW_Local initialised.\n");

   if (strlen(username) == 0)
   {
      Error_Report(0, "CAW_Local has no username to work with...");
      setupconfigure();
      Window_Show(configure_window, open_CENTERED);
   }

   _kernel_swi(VoteModule_GetVotesCount, &r_in, &r_out);
   questions = r_out.r[0];
   maxvotes  = r_out.r[1];

   default_icons_tout();

   /* Disable 'addvote' icon if not registered */
   /* don't bother any more... if (registered == FALSE) Icon_SetShade(menu_window, 0, 1); */

   /* This next part of code may seen a little odd... It is designed to pop
      up the Genesis window for three seconds, whilst allowing the program
      (and all others) to function properly.
      The NULL event is temporarily claimed, because otherwise the WIMP
      will only return when something happens, which may be longer than the
      three seconds required... */
   Event_Claim(event_NULL, event_ANY, event_ANY, null_handler, NULL);
   _kernel_swi(OS_ReadMonotonicTime, &r_in, &r_out);
   ticker = r_out.r[0] + 300;
   while (ticker > r_out.r[0] && !finished)
   {
      Wimp_Poll(event_mask, &event_lastevent);
      Event_Process(&event_lastevent);
      _kernel_swi(OS_ReadMonotonicTime, &r_in, &r_out);
   }
   Wimp_CloseWindow(genesis_window);
   Event_Release(event_NULL, event_ANY, event_ANY, null_handler, NULL);


   /* And begin 'poll' */
   while( !finished )
   {
      Wimp_Poll(event_mask, &event_lastevent);
      Event_Process(&event_lastevent);
   }

   if (finished == 255)
   {
      Error_Report(0, "If everybody hacked a program like that, then development on it would soon cease. And if that were to happen, you'd have got yourself right up sh*t street... Wouldn't you now?");
      exit(EXIT_FAILURE);
   }

   debug("CAW_Local closing down.\n");

   return 1;
}




static void create_system_workspace(void)
{
   /* This reserves memory for vote data. [0] is the cast-your-vote block and [1] is
      for vote adding. NOTE - THIS IS BACKWARDS TO ON-SCREEN LAYOUT.
      If memory can't be claimed, we die with a fatal whine... Otherwise register
      a routine to tidy up on exit. */

   debug("Trying to allocate memory for vote buffer...");
   votedata = calloc(1, sizeof( struct votebuffer ));
   if ( !votedata )
      Error_ReportFatal(0, "Cannot allocate memory required for vote buffer [try closing down unused tasks]");
   atexit(lose_system_workspace);
   debug("done it!\n");

   return;
}



static void lose_system_workspace(void)
{
   /* This will 'tidy' the buffer upon exiting. */

   if (votedata)
   {
      debug("Freeing up buffer space.\n");
      free(votedata);
      votedata = NULL;
   }

   return;
}



static void initialise_resources(void)
{
   /* Here we perform the basic initialisations, getting the library functions into
      order and defining the templates file */

   Resource_Initialise("CAWlocal");
   Event_Initialise("CAWlocal");

   Resource_LoadSprites();
   EventMsg_Initialise();

   Msgs_LoadFile("Messages"); /* Not really used yet - for 'automatic help'. */

   Screen_CacheModeInfo();

   Template_Initialise();
   Template_UseSpriteArea(resource_sprites);
   Template_LoadFile("Templates");

   return;
}



static void build_windows(void)
{
   /* Create the windows and attach handlers, or whine fatal if template not found.
      Please excuse the peculiar error reporting method. The idea here is that the
      compiler optimises the "Cannot found [...]" text and slots in the appropriate
      window name as needed. */

   genesis_window = Window_CreateAndShow("genesis", 45, open_CENTERED);
   if (genesis_window == 0)
      Error_ReportFatal(0, "Cannot find template definition \'%s\'", "genesis");
   Icon_SetText(genesis_window,1,"Version 1.07");

   vote_window = Window_Create("vote", 45);
   if (vote_window == 0)
      Error_ReportFatal(0, "Cannot find template definition \'%s\'", "vote");
   Event_Claim(event_CLICK, vote_window, event_ANY, vote_click_handler, NULL);

   result_window = Window_Create("result", 45);
   if (result_window == 0)
      Error_ReportFatal(0, "Cannot find template definition \'%s\'", "result");

   addvote_window = Window_Create("addvote", 45);
   if (addvote_window == 0)
      Error_ReportFatal(0, "Cannot find template definition \'%s\'", "addvote");
   Event_Claim(event_CLICK, addvote_window, event_ANY, addvote_click_handler, NULL);

   menu_window = Window_Create("menu", 45);
   if (menu_window == 0)
      Error_ReportFatal(0, "Cannot find template definition \'%s\'", "menu");
   Event_Claim(event_CLICK, menu_window, event_ANY, menu_click_handler, NULL);

   pane_window = Window_Create("activepane", 0);
   if (pane_window == 0)
      Error_ReportFatal(0, "Cannot find template definition \'%s\'", "activepane");
   Event_Claim(event_CLICK, pane_window, event_ANY, pane_click_handler, NULL);

   configure_window = Window_Create("configure", 0);
   if (configure_window == 0)
      Error_ReportFatal(0, "Cannot find template definition \'%s\'", "configure");
   Event_Claim(event_CLICK, configure_window, event_ANY, configure_click_handler, NULL);

   return;
}



static void initialise_windows_icons_events(void)
{
   /* This is the eclectic mess left over of the initialise procedures. Basically create
      this iconbar icon and the menu and chuck in a few handlers for them.
      Here we also define a few 'generic' window events so our windows have basic
      functionality even though nothing has yet been coded for them! :-) */

   icon_handle iconbaricon;

   mainmenu  = Menu_New("CAV local", ">Info,Configure,Quit");
   Event_Claim(event_MENU,  event_ANY, event_ANY, mainmenu_choice, NULL);

   info = Dialog2_CreateDialogBlock("info", 42, 42, info_handler, NULL, NULL);
   Menu_Warn(mainmenu, mainmenu_INFO, TRUE, show_infowindow, info);

   iconbaricon = Icon_BarIconUser("!caw_local", iconbar_RIGHT, (unsigned int *) resource_sprites);
   Event_Claim(event_CLICK, -2, iconbaricon, iconbar_handler, NULL);

   /* Default window handlers... Yes guys - remove these and the windows will 'freeze'. */
   Event_Claim(event_CLOSE, event_ANY, event_ANY, close_handler, NULL);
   Event_Claim(event_REDRAW, event_ANY, event_ANY, Handler_NullRedraw, NULL);
   Event_Claim(event_OPEN, event_ANY, event_ANY, open_handler, NULL);
   Window_AutoHelp(event_ANY, event_ANY); /* Sure beats a massive messy CASE structure! */

   /* This is so the screen/font data is correct upon mode changes... */
   EventMsg_Claim(message_MODECHANGE, event_ANY, Handler_ModeChange, NULL);

   /* To set up data at DeskTop quit, for next initialise... */
   EventMsg_Claim(message_SAVEDESK, event_ANY, desktop_save, NULL);

   return;
}



static BOOL open_handler(event_pollblock *event, void *reference)
{
   /* So it's a sad way to hack on a 'pane'. Tough titty. */

   window_handle winmove;
   int masterleft;
   int mastertop;

   reference = reference;

   Wimp_OpenWindow(&event->data.openblock);

   winmove = event->data.openblock.window;
   masterleft = event->data.openblock.screenrect.min.x;
   mastertop = event->data.openblock.screenrect.max.y;

   if ( (int)winmove == (int)vote_window)
   {
      pane_move(masterleft, mastertop);
   }

   if ( (int)winmove == (int)result_window)
   {
      pane_move(masterleft, mastertop);
   }

   return TRUE;
}



static BOOL close_handler(event_pollblock *event, void *reference)
{
   /* Handles window closed events so we can trap 'currentaction' and
      tidy up the pane - if open. */

   window_handle winhandle;

   reference = reference;

   winhandle = event->data.openblock.window;

   Wimp_CloseWindow(winhandle);

   if ( (int)winhandle == (int)addvote_window)
   {
      currentaction = FALSE;
   }

   if ( (int)winhandle == (int)vote_window)
   {
      Wimp_CloseWindow(pane_window);
      currentaction = FALSE;
   }

   if ( (int)winhandle == (int)result_window)
   {
      Wimp_CloseWindow(pane_window);
      currentaction = FALSE;
   }

   return TRUE;
}



static BOOL null_handler(event_pollblock *event, void *reference)
{
   /* 'Null' event handler... This sole purpose of this routine is to provide
      a handler for the 'Null' event, even though it doesn't do anything.
      Pointless? Well, if the NULL event is not temporarily claimed, the
      three second timer on the 'Genesis' window would fail (presumably due
      to use of PollIdle and NULL being masked out). */

   event = event;
   reference = reference;

   return TRUE;
}



static void pane_move(int leftpos, int toppos)
{
   /* Calculate where the pane is supposed to go, and deal with it...
      The pane will go off of screen left only HALFWAY. :-) No restrictions
      on the screen bottom.
      I would ideally like the pane to behave like the !Draw pane, but I have
      a feeling it would flicker something chronic. */

   window_state windatum;
   int width;
   int height;

   Wimp_GetWindowState(pane_window, &windatum);
   width = windatum.openblock.screenrect.max.x - windatum.openblock.screenrect.min.x;
   height = windatum.openblock.screenrect.max.y - windatum.openblock.screenrect.min.y;
   windatum.openblock.screenrect.max.x = leftpos;
   if (leftpos < (width/2) ) leftpos = (width/2);
   windatum.openblock.screenrect.min.x = leftpos - width;
   windatum.openblock.screenrect.max.y = toppos;
   windatum.openblock.screenrect.min.y = toppos - height;
   Wimp_OpenWindow(&windatum.openblock);
   return;
}



static BOOL mainmenu_choice(event_pollblock *event, void *reference)
{
   /* Here we deal with the main menu. Basically the top pulls up an 'info'
      window attached to the menu, and the bottom quits. Oh, and if somehow
      another menu option appears (!?), we'll moan blue murder and then die.
      Don't worry about the rather weird error message. If this error ever
      occurs, the text of the message will be the least of your worries! */

   mouse_block ptr;

   if (menu_currentopen != mainmenu) return FALSE;

   debug("Iconbar menu selection %d.\n", event->data.selection[0]);

   switch(event->data.selection[0])
   {

      case mainmenu_INFO   : Dialog2_OpenDialogMenuLeaf(event, (dialog2_block *)reference);
         	             break;
      case mainmenu_CONFIG : setupconfigure();
                             Window_Show(configure_window, open_CENTERED);
      	   		     break;
      case mainmenu_QUIT   : finished = TRUE;
                             break;
      default              : Error_ReportFatal(0, "Panic! Invalid menu entry accessed. This menu should have only three entries - INFO, CONFIGURE and QUIT...\?");
                             break;
   }

   Wimp_GetPointerInfo(&ptr);  /* If ADJUST was used to select, then keep the menu open. */
   if (ptr.button.data.adjust) Menu_ShowLast();

   return TRUE;
}



static BOOL show_infowindow(event_pollblock *event, void *reference)
{
   /* Right menu? [be paranoid!] If so, open the info window (as a dialogue). */
   if ( (menu_currentopen != mainmenu) || (event->data.message.data.menuwarn.selection[0] != mainmenu_INFO)) return FALSE;

   Dialog2_OpenDialogMenuLeaf(event, (dialog2_block *)reference);

   return TRUE;
}



static BOOL iconbar_handler(event_pollblock *event, void *reference)
{
  /* If [MENU], bring up menu. If [SELECT], open menu window. [ADJUST] does nothing. */

  reference = reference;

  if (event->data.mouse.button.data.menu)
     Menu_Show(mainmenu, event->data.mouse.pos.x,-1);

  if (event->data.mouse.button.data.select)
     Window_Show(menu_window, open_WHEREVER);

  return TRUE;
}



static void info_handler(dialog2_block *dialog2)
{
   Icon_SetText(dialog2->window, 4, programversion);
   Icon_SetText(dialog2->window, 17, regname_post);

   if (registered == FALSE)
   {
      Icon_SetBackColour(dialog2->window, 17, 11);
      Icon_SetForeColour(dialog2->window, 17, 9);
   }
   else
   {
      Icon_SetBackColour(dialog2->window, 17, 1);
      Icon_SetForeColour(dialog2->window, 17, 7);
   }

   return;
}


static BOOL menu_click_handler(event_pollblock *event, void *reference)
{
   /* Not a menu click as in Info/Quit, rather a click on the menu window. Thinking
      about it now, creating a window called 'menu' was a bit daft wasn't it!? :-)
      Ah well... This deals with clicks on the window. We allow only [SELECT] and
      [ADJUST]...
      Each option is trapped to allow only the required function. One thing at a
      time. The pane initial position is sorted out as well. Pane is 'shown' then
      moved - otherwise it doesn't work...? */

   int icon = -1;
   int masterleft;
   int mastertop;
   window_state windatum;
   int action;

   reference = reference;

   if ( !(event->data.mouse.button.data.select) && !(event->data.mouse.button.data.adjust) ) return FALSE;

   _kernel_swi(VoteModule_GetVotesCount, &r_in, &r_out);
   questions = r_out.r[0];
   maxvotes  = r_out.r[1];

   icon = event->data.mouse.icon;

   switch(icon)
   {
      case 0 : if (currentaction != FALSE)
      	       {
	          if (currentaction == 1)
	          {
	             actionerror(1, 255);
	          }
	          else
	          {
       	             actionerror(1, currentaction);
       	          }
      	          break;
      	       }
               Window_Show(addvote_window, open_WHEREVER);
      	       currentaction = 1;
      	       break;
      case 1 : if (questions == 0)
               {
                  debug("No votes present!\n");
                  Error_Report(0, "There are no votes in this database.");
                  break;
               }
               if (currentaction != FALSE)
      	       {
      	          if (currentaction == 2)
	          {
	             actionerror(2, 255);
	          }
	          else
	          {
       	             actionerror(2, currentaction);
       	          }
      	          break;
      	       }
      	       currvote = 1;
      	       action = searchforvote();
      	       currvote = action;
	       action = fill_vote();
      	       if (action == TRUE)
      	       {
      	          Window_Show(vote_window, open_WHEREVER);
       	          currentaction = 2;
                  Window_Show(pane_window, open_WHEREVER);
                  Wimp_GetWindowState(vote_window, &windatum);
                  masterleft = windatum.openblock.screenrect.min.x;
   	          mastertop = windatum.openblock.screenrect.max.y;
 	          pane_move(masterleft, mastertop);
       	       }
      	       break;
      case 2 : if (questions == 0)
               {
                  debug("No votes present!\n");
                  Error_Report(0, "There are no votes in this database.");
                  break;
               }
               if (currentaction != FALSE)
      	       {
      	          if (currentaction == 3)
	          {
	             actionerror(3, 255);
	          }
	          else
	          {
       	             actionerror(3, currentaction);
       	          }
      	          break;
      	       }
      	       currvote = 1;
      	       calculate_results();
      	       Window_Show(result_window, open_WHEREVER);
               Window_Show(pane_window, open_WHEREVER);
               Wimp_GetWindowState(result_window, &windatum);
               masterleft = windatum.openblock.screenrect.min.x;
	       mastertop = windatum.openblock.screenrect.max.y;
	       pane_move(masterleft, mastertop);
      	       currentaction = 3;
      	       break;
      case 3 : if (currentaction != FALSE)
               {
                  if (error_confirm(0, "You have not finished what your were doing. Are you sure you want to quit?") == 1)
                  {
                     finished = TRUE;
                  }
               }
               else finished = TRUE;
      	       break;
      default: Error_Report(0, "Stop poking your pointer 'round these points. You might make Number %d angry!",icon);
     	       break;
   }
   return TRUE;
}



static BOOL vote_click_handler(event_pollblock *event, void *reference)
{
   /* Handler for clicks on the 'vote' window. */

   int icon = -1;

   reference = reference;

   if ( !(event->data.mouse.button.data.select) && !(event->data.mouse.button.data.adjust) ) return(FALSE);

   icon = event->data.mouse.icon;

   switch(icon)
   {
      case 13 : selectvote(1);
      	      	break;
      case 15 : selectvote(2);
      	      	break;
      case 17 : selectvote(3);
      	      	break;
      case 19 : selectvote(4);
      	      	break;
      case 21 : selectvote(5);
      	      	break;
      case 23 : selectvote(6);
      	      	break;
      case 25 : selectvote(7);
      	      	break;
      case 27 : selectvote(8);
      	      	break;
      case 29 : currvote++;
                junk_v = searchforvote();
      	        currvote = junk_v;
                junk_v = fill_vote();
                break;
      case 30 : selectvote(0);
      	        break;
      default : break;
   }
   return TRUE;
}



static BOOL addvote_click_handler(event_pollblock *event, void *reference)
{
   /* Handler for clicks on the 'addvote' window. */

   int icon = -1;

   reference = reference;

   if ( !(event->data.mouse.button.data.select) && !(event->data.mouse.button.data.adjust) ) return(FALSE);

   icon = event->data.mouse.icon;

   switch(icon)
   {
      case  4 : /* Vote is local */
      	      	break;
      case 29 : buildandprocessnewvote();
      	      	break;
      case 30 : default_icons_addvote();
      	      	break;
      case 31 : Wimp_CloseWindow(addvote_window);
      	      	currentaction = FALSE;
      	      	break;
      default : break;
   }
   return TRUE;
}



static BOOL pane_click_handler(event_pollblock *event, void *reference)
{
   /* Handler for clicks on the 'pane' window. Results only implemented. */

   int icon = -1;
   int go_to;

   reference = reference;

   _kernel_swi(VoteModule_GetVotesCount, &r_in, &r_out);
   questions = r_out.r[0];
   maxvotes  = r_out.r[1];

   if ( !(event->data.mouse.button.data.select) && !(event->data.mouse.button.data.adjust) ) return(FALSE);

   icon = event->data.mouse.icon;

   if (currentaction == 2)
   {
      switch(icon)
      {
         case 0 : currvote--;
                  junk_v = searchforvoteback();
       	          currvote = junk_v;
                  junk_v = fill_vote();
                  break;
         case 1 : currvote++;
                  junk_v = searchforvote();
       	          currvote = junk_v;
                  junk_v = fill_vote();
                  break;
         case 2 : go_to = Icon_GetInteger(pane_window, 3);
      	      	  if (go_to != 0) currvote = go_to;
      	      	  if (currvote < 1) currvote = 1;
      	      	  if (currvote > questions) currvote = questions;
      	      	  if (currvote == questions)
      	      	     junk_v = searchforvoteback();
      	      	  else
       	      	     junk_v = searchforvote();
       	          currvote = junk_v;
                  junk_v = fill_vote();
      	      	  break;
         case 4 : currvote = 1;
                  junk_v = searchforvote();
       	          currvote = junk_v;
                  junk_v = fill_vote();
                  break;
         case 5 : currvote = questions;
                  junk_v = searchforvoteback();
       	          currvote = junk_v;
                  junk_v = fill_vote();
                  break;
      	 case 6 : Wimp_CloseWindow(vote_window);
      	          Wimp_CloseWindow(pane_window);
                  currentaction = FALSE;
                  break;
         default: break;
      }
   }


   if (currentaction == 3)
   {
      switch(icon)
      {
      	 case 0 : currvote--;
      	      	  if (currvote < 1) currvote=1;
      	      	  junk_v = calculate_results();
      	      	  break;
      	 case 1 : currvote++;
      	      	  if (currvote > questions) currvote = questions;
      	      	  junk_v = calculate_results();
      	      	  break;
      	 case 2 : go_to = Icon_GetInteger(pane_window, 3);
      	      	  if (go_to != 0) currvote = go_to;
      	      	  if (currvote < 1) currvote = 1;
      	      	  if (currvote > questions) currvote = questions;
      	      	  junk_v = calculate_results();
      	      	  break;
      	 case 4 : currvote = 1;
      	      	  junk_v = calculate_results();
      	      	  break;
      	 case 5 : currvote = questions;
      	      	  junk_v = calculate_results();
      	      	  break;
      	 case 6 : Wimp_CloseWindow(result_window);
      	          Wimp_CloseWindow(pane_window);
                  currentaction = FALSE;
                  break;
         default: break;
      }
   }
   return TRUE;
}



static BOOL configure_click_handler(event_pollblock *event, void *reference)
{
   /* Handler for clicks on the 'configure' window. */

   int icon = -1;

   reference = reference;

   if ( !(event->data.mouse.button.data.select) && !(event->data.mouse.button.data.adjust) ) return(FALSE);

   icon = event->data.mouse.icon;

   switch(icon)
   {
      case 8 : nvplog = nvplog ^ 1;
      	       Icon_SetSelect(configure_window, 8, nvplog);
      	       break;
      case 9 : nvplocal = nvplocal ^ 1;
      	       Icon_SetSelect(configure_window, 9, nvplocal);
      	       break;
      case 10: Error_Report(0, "AutoScan not implemented yet...");
      	       break;
      case 11: nvplog = 0;
      	       nvplocal = 0;
      	       username[0] = '\0';
      	       fidoaddr[0] = '\0';
      	       setupconfigure();
      	       break;
      case 12: loadconfigure();
      	       setupconfigure();
      	       break;
      case 13: saveconfigure();
               Wimp_CloseWindow(configure_window);
      	       break;
      default: break;
   }
   return TRUE;
}



static void default_icons_tout(void)
{
   /* Quickie routine to call all the other 'default' functions in turn... Saves time.
      Not that I can see anybody calling this that often!?
      The settext thingies are set here. One line didn't look worthy enough to have
      its own routine! */

   default_icons_vote();
   default_icons_result();
   default_icons_addvote();
   default_icons_menu();
   Icon_SetText(pane_window, 3, "");
   return;
}



static void default_icons_vote(void)
{
   /* Sets all the icons and title and option in the vote window to a default 'blank'
      state. */

   Icon_SetText(vote_window, 2, "");
   Icon_SetText(vote_window, 3, "");
   Icon_SetText(vote_window, 7, "");
   Icon_SetText(vote_window, 8, "");
   Icon_SetText(vote_window, 9, "");
   Icon_SetText(vote_window, 10, "");
   Icon_SetText(vote_window, 14, "");
   Icon_SetText(vote_window, 16, "");
   Icon_SetText(vote_window, 18, "");
   Icon_SetText(vote_window, 20, "");
   Icon_SetText(vote_window, 22, "");
   Icon_SetText(vote_window, 24, "");
   Icon_SetText(vote_window, 26, "");
   Icon_SetText(vote_window, 28, "");
   Icon_SetSelect(vote_window, 4, 0);
   Window_SetTitle(vote_window, "CastAWote Local");
   return;
}



static void default_icons_result(void)
{
   /* Sets all the icons and title and option in the result window to a default 'blank'
      state. Also calls routine to set the slidy-things to zero. */

   Icon_SetText(result_window, 2, "");
   Icon_SetText(result_window, 6, "");
   Icon_SetText(result_window, 7, "0 out of 0 (0%)");
   Icon_SetText(result_window, 11, "");
   Icon_SetText(result_window, 12, "0 out of 0 (0%)");
   Icon_SetText(result_window, 16, "");
   Icon_SetText(result_window, 17, "0 out of 0 (0%)");
   Icon_SetText(result_window, 21, "");
   Icon_SetText(result_window, 22, "0 out of 0 (0%)");
   Icon_SetText(result_window, 26, "");
   Icon_SetText(result_window, 27, "0 out of 0 (0%)");
   Icon_SetText(result_window, 31, "");
   Icon_SetText(result_window, 32, "0 out of 0 (0%)");
   Icon_SetText(result_window, 36, "");
   Icon_SetText(result_window, 37, "0 out of 0 (0%)");
   Icon_SetText(result_window, 41, "");
   Icon_SetText(result_window, 42, "0 out of 0 (0%)");
   Window_SetTitle(result_window, "Results for vote ? (of ?)");
   adjust_result_slider( 9, 0);
   adjust_result_slider( 14, 0);
   adjust_result_slider( 19, 0);
   adjust_result_slider( 24, 0);
   adjust_result_slider( 29, 0);
   adjust_result_slider( 34, 0);
   adjust_result_slider( 39, 0);
   adjust_result_slider( 44, 0);
   return;
}



static void default_icons_addvote(void)
{
   /* Sets all the icons and title and option in the addvote window to a default 'blank'
      state. */

   strcpy(addvote.creator, username);
   strcpy(addvote.system, "");
   strcpy(addvote.question, "");
   strcpy(addvote.addinfo_one, "");
   strcpy(addvote.addinfo_two, "");
   strcpy(addvote.addinfo_three, "");
   strcpy(addvote.option_one_text, "");
   strcpy(addvote.option_two_text, "");
   strcpy(addvote.option_three_text, "");
   strcpy(addvote.option_four_text, "");
   strcpy(addvote.option_five_text, "");
   strcpy(addvote.option_six_text, "");
   strcpy(addvote.option_seven_text, "");
   strcpy(addvote.option_eight_text, "");
   Icon_SetText(addvote_window, 2, addvote.creator);
   Icon_SetText(addvote_window, 3, addvote.system);
   Icon_SetText(addvote_window, 7, addvote.question);
   Icon_SetText(addvote_window, 8, addvote.addinfo_one);
   Icon_SetText(addvote_window, 9, addvote.addinfo_two);
   Icon_SetText(addvote_window, 10, addvote.addinfo_three);
   Icon_SetText(addvote_window, 14, addvote.option_one_text);
   Icon_SetText(addvote_window, 16, addvote.option_two_text);
   Icon_SetText(addvote_window, 18, addvote.option_three_text);
   Icon_SetText(addvote_window, 20, addvote.option_four_text);
   Icon_SetText(addvote_window, 22, addvote.option_five_text);
   Icon_SetText(addvote_window, 24, addvote.option_six_text);
   Icon_SetText(addvote_window, 26, addvote.option_seven_text);
   Icon_SetText(addvote_window, 28, addvote.option_eight_text);
   Icon_SetSelect(addvote_window, 4, 0);
   Window_SetTitle(addvote_window, "Adding a vote");
   return;
}



static void default_icons_menu(void)
{
   /* Sets all the icons and title and option in the menu window to a default 'blank'
      state. One day this will not blank, but instead will set up the actual default
      text to go in here. You know - the NVP info, number of votes etc... */

   _kernel_swi(VoteModule_GetVotesCount, &r_in, &r_out);
   questions = r_out.r[0];
   maxvotes = r_out.r[1];

   Icon_printf(menu_window, 5, "%d", questions);
   Icon_SetText(menu_window, 9, "-");
   Icon_SetText(menu_window, 11, fidoaddr);
   if (strlen(fidoaddr) == 0)
      Icon_SetText(menu_window, 11, "Unavailable");
   mostcommon();
   return;
}



static void adjust_result_slider(int icon, int percentage)
{
   /* Painful to create (until I realised that 'int' loses the stuff after the
      decimal point! :-) ) but quite 'fun' hacking around at low-level. I was
      surprised DeskLib didn't feed me a "resize icon" function, it has practically
      everything else! Ah - maybe that is what the slider is for? But it seemed
      more hassle to code the slider than this.
      It should all make sense... */

   icon_handle iconhandle;
   icon_block base;
   icon_createblock slider;
   window_redrawblock redrawit;
   int basesize = 0;
   int slidersize = 0;
   float multiply = 0;

   if (percentage < 0) percentage = 0;
   if (percentage > 99) percentage = 99;

   Wimp_GetIconState(result_window, (icon-1), &base);
   Wimp_GetIconState(result_window, icon, &slider.icondata);

   redrawit.window = result_window;
   redrawit.rect = slider.icondata.workarearect;
   redrawit.rect.max.x += 4;

   slidersize = slider.icondata.workarearect.max.x - slider.icondata.workarearect.min.x;
   basesize = base.workarearect.max.x - base.workarearect.min.x;
   multiply = (float)basesize / 100;
   multiply = multiply * (float)percentage;
   slider.icondata.workarearect.max.x = slider.icondata.workarearect.min.x + (int)multiply;

   slider.window = result_window;
   Wimp_DeleteIcon(result_window, icon);
   Wimp_ForceRedraw(&redrawit);

   Wimp_CreateIcon(&slider, &iconhandle);
   Wimp_SetIconState(result_window, 9, 0, 0);

   debug("Result Slider : icon %d; slidersize %d ; basesize %d ; multiply %d\n", icon, slidersize, basesize, (int)multiply);

   return;
}



static void actionerror(int thisaction, int actionnow)
{
   /* This provides suitable replies for the 'currentaction' trapping.
      If 'actionnow' is 255 then the message is "you are already..."
      otherwise it is "You can't do # whilst you are doing #". */

   char ohdear[255];

   if (actionnow == 255)
   {
      strcpy(ohdear, "You are already ");
      switch (thisaction)
      {
         case 1 : strcat(ohdear, "adding a vote...");
      	          break;
         case 2 : strcat(ohdear, "casting your vote...");
       	          break;
         case 3 : strcat(ohdear, "viewing results...");
      	          break;
         default: strcat(ohdear, "{undefined!?}...");
       	          break;
      }
   }
   else
   {
      strcpy(ohdear, "Sorry, you cannot ");
      switch (thisaction)
      {
         case 1 : strcat(ohdear, "add votes ");
      	          break;
         case 2 : strcat(ohdear, "cast your vote ");
      	          break;
         case 3 : strcat(ohdear, "view results ");
      	          break;
         default: strcat(ohdear, "{undefined!?} ");
      	          break;
      }
      strcat(ohdear, "while you are ");
      switch (actionnow)
      {
         case 1 : strcat(ohdear, "adding a vote...");
      	          break;
         case 2 : strcat(ohdear, "casting your vote...");
      	          break;
         case 3 : strcat(ohdear, "viewing results...");
      	          break;
         default: strcat(ohdear, "{undefined!?}...");
      	          break;
      }
   }
   Error_Report(0, ohdear);
   return;
}


static int error_confirm(int errnum, char *errmsg, ...)
{
   /* Reports a simple error with "OK"/"Cancel" icons, and returns status.
      'Cancel' is highlighted so if you click on it without thinking, it will
      be 'safe' (because OK quits). */

   va_list va;
   os_error error;
   error_flags eflags;
   int reply=0;

   va_start(va, errmsg);
   vsprintf(error.errmess, errmsg, va);
   va_end(va);
   error.errnum = errnum;
   eflags.value = 31;

   reply = Wimp_ReportErrorR(&error, eflags.value, event_taskname);

   return reply;
}


static int fill_vote(void)
{
   /* Grab vote data and sling into 'vote' window. */

   char buffer[46];

   if (currvote > questions)
   {
      Error_Report(0, "There are no more votes in the database.");
      Wimp_CloseWindow(vote_window);
      Wimp_CloseWindow(pane_window);
      currentaction = FALSE;
      return FALSE;
   }

   grabvotedata();

   Icon_Shade(vote_window, 13);
   Icon_Shade(vote_window, 15);
   Icon_Shade(vote_window, 17);
   Icon_Shade(vote_window, 19);
   Icon_Shade(vote_window, 21);
   Icon_Shade(vote_window, 23);
   Icon_Shade(vote_window, 25);
   Icon_Shade(vote_window, 27);

   if (strlen(votedata->option_one_text) != NULL) Icon_Unshade(vote_window, 13);
   if (strlen(votedata->option_two_text) != NULL) Icon_Unshade(vote_window, 15);
   if (strlen(votedata->option_three_text) != NULL) Icon_Unshade(vote_window, 17);
   if (strlen(votedata->option_four_text) != NULL) Icon_Unshade(vote_window, 19);
   if (strlen(votedata->option_five_text) != NULL) Icon_Unshade(vote_window, 21);
   if (strlen(votedata->option_six_text) != NULL) Icon_Unshade(vote_window, 23);
   if (strlen(votedata->option_seven_text) != NULL) Icon_Unshade(vote_window, 25);
   if (strlen(votedata->option_eight_text) != NULL) Icon_Unshade(vote_window, 27);

   Icon_SetText(vote_window, 2, votedata->creator);
   Icon_SetText(vote_window, 3, votedata->system);
   if (strstr(votedata->system, fidoaddr) == NULL)
   {
      Icon_SetSelect(vote_window, 4, 0);
   }
   else
   {
      Icon_SetSelect(vote_window, 4, 1);
   }
   Icon_SetText(vote_window, 7, votedata->question);
   Icon_SetText(vote_window, 8, votedata->addinfo_one);
   Icon_SetText(vote_window, 9, votedata->addinfo_two);
   Icon_SetText(vote_window, 10, votedata->addinfo_three);
   Icon_SetText(vote_window, 14, votedata->option_one_text);
   Icon_SetText(vote_window, 16, votedata->option_two_text);
   Icon_SetText(vote_window, 18, votedata->option_three_text);
   Icon_SetText(vote_window, 20, votedata->option_four_text);
   Icon_SetText(vote_window, 22, votedata->option_five_text);
   Icon_SetText(vote_window, 24, votedata->option_six_text);
   Icon_SetText(vote_window, 26, votedata->option_seven_text);
   Icon_SetText(vote_window, 28, votedata->option_eight_text);

   sprintf(buffer, "CastAWote Local  ;  vote #%d of #%d", currvote, questions);
   Window_SetTitle(vote_window, buffer);

   mostcommon();

   return TRUE;
}



static int searchforvote(void)
{
   /* Search for next available vote, from currvote onwards... */
   int  reply = 0;
   int  loop;
   int  temp;

   r_in.r[0] = 5;
   r_in.r[1] = (int)username;
   _kernel_swi(VoteModule_VoteOp, &r_in, &r_out);
   reply = r_out.r[0];
   debug("'searchforvote' VoteOp replied %d.\n", reply);

   if (reply != 1)
   {
      switch(reply)
      {
         case 0 : Error_ReportFatal(0, "\'VoteOp 5\' failed");
              	  break;
         case 2 : Error_ReportFatal(0, "\'HasVoted\' file cannot be opened");
              	  break;
         case 3 : Error_ReportFatal(0, "User name too long (\?)");
              	  break;
         case 4 : Error_Report(0, "User name \'%s\' not found, will create new entry...", username);
         default: Error_Report(0, "Whilst searching votes, unrecognised error code %d was received.", reply);
                  break;
      }
   }

   for (loop=0; loop != 220; loop++) heapentry[loop]=255;
   heapentry[221] = '\0';

   if (reply == 1 || reply == 4)
   {
      strcpy(junk_s, (char*) r_out.r[1]);

      for (loop=0; junk_s[loop] != '\0'; loop++)
      {
         temp = junk_s[loop];
         heapentry[temp-20] = temp;
      }
      heapentry[221] = '\0';
    }

    reply = -1;
    temp = currvote;

    do
    {
       debug("Verifying vote %d... = %d   (255 is unvoted).\n", temp, heapentry[temp]);
       if (heapentry[temp] == 255) reply = temp;
       temp++;
    } while (reply == -1);

    return reply;
}



static int searchforvoteback(void)
{
   /* Search for next available vote, from currvote BACKWARDS... */
   int  reply = 0;
   int  loop;
   int  temp;

   r_in.r[0] = 5;
   r_in.r[1] = (int)username;
   _kernel_swi(VoteModule_VoteOp, &r_in, &r_out);
   reply = r_out.r[0];
   debug("'searchforvoteback' VoteOp replied %d.\n", reply);

   if (reply != 1)
   {
      switch(reply)
      {
         case 0 : Error_ReportFatal(0, "\'VoteOp 5\' failed");
              	  break;
         case 2 : Error_ReportFatal(0, "\'HasVoted\' file cannot be opened");
              	  break;
         case 3 : Error_ReportFatal(0, "User name too long (\?)");
              	  break;
         case 4 : Error_Report(0, "User name \'%s\' not found, will create new entry...", username);
         default: Error_Report(0, "Whilst searching votes, unrecognised error code %d was received.", reply);
                  break;
      }
   }

   for (loop=0; loop != 220; loop++) heapentry[loop]=255;
   heapentry[221] = '\0';

   if (reply == 1 || reply == 4)
   {
      strcpy(junk_s, (char*) r_out.r[1]);

      for (loop=0; junk_s[loop] != '\0'; loop++)
      {
         temp = junk_s[loop];
         heapentry[temp-20] = temp;
      }
      heapentry[221] = '\0';
    }

    reply = -1;
    temp = currvote;

    do
    {
       debug("Verifying vote %d... = %d   (255 is unvoted).\n", temp, heapentry[temp]);
       if (heapentry[temp] == 255) reply = temp;
       temp--;
    } while (reply == -1 && temp != 0);

    if (temp == 0) reply = searchforvote();

    return reply;
}



static void grabvotedata(void)
{
   /* Grab vote data from VoteModule and place in votedata struct. */

   struct votebuffer *votetemp = NULL;

   r_in.r[0] = currvote;
   _kernel_swi(VoteModule_GetVote, &r_in, &r_out);

   if (r_out.r[0] != 1) Error_ReportFatal(0, "Tried to get an 'out of range' vote");

   votetemp = (struct votebuffer *)r_out.r[1];

   strcpy(votedata->creator, votetemp->creator);
   strcpy(votedata->system, votetemp->system);
   votedata->voted_for = votetemp->voted_for;
   strcpy(votedata->question, votetemp->question);
   strcpy(votedata->software_opts, votetemp->software_opts);
   strcpy(votedata->addinfo_one, votetemp->addinfo_one);
   strcpy(votedata->addinfo_two, votetemp->addinfo_two);
   strcpy(votedata->addinfo_three, votetemp->addinfo_three);
   strcpy(votedata->option_one_text, votetemp->option_one_text);
   votedata->option_one_rslt = votetemp->option_one_rslt;
   strcpy(votedata->option_two_text, votetemp->option_two_text);
   votedata->option_two_rslt = votetemp->option_two_rslt;
   strcpy(votedata->option_three_text, votetemp->option_three_text);
   votedata->option_three_rslt = votetemp->option_three_rslt;
   strcpy(votedata->option_four_text, votetemp->option_four_text);
   votedata->option_four_rslt = votetemp->option_four_rslt;
   strcpy(votedata->option_five_text, votetemp->option_five_text);
   votedata->option_five_rslt = votetemp->option_five_rslt;
   strcpy(votedata->option_six_text, votetemp->option_six_text);
   votedata->option_six_rslt = votetemp->option_six_rslt;
   strcpy(votedata->option_seven_text, votetemp->option_seven_text);
   votedata->option_seven_rslt = votetemp->option_seven_rslt;
   strcpy(votedata->option_eight_text, votetemp->option_eight_text);
   votedata->option_eight_rslt = votetemp->option_eight_rslt;
   votedata->vote_is_local[0] = votetemp->vote_is_local[0];

   return;
}



static int calculate_results(void)
{
   /* Grab vote data and make up the results. */

   char buffer[46];
   float percentage = 0;
   int  finale = 0;

   if (currvote > questions)
   {
      Error_Report(0, "There are no more votes in the database.");
      Wimp_CloseWindow(vote_window);
      Wimp_CloseWindow(pane_window);
      currentaction = FALSE;
      return FALSE;
   }

   grabvotedata();

   /* The easy bits */
   Icon_SetText(result_window, 2, votedata->question);
   Icon_SetText(result_window, 6, votedata->option_one_text);
   Icon_SetText(result_window, 11, votedata->option_two_text);
   Icon_SetText(result_window, 16, votedata->option_three_text);
   Icon_SetText(result_window, 21, votedata->option_four_text);
   Icon_SetText(result_window, 26, votedata->option_five_text);
   Icon_SetText(result_window, 31, votedata->option_six_text);
   Icon_SetText(result_window, 36, votedata->option_seven_text);
   Icon_SetText(result_window, 41, votedata->option_eight_text);

   /* The 'not-quite-so-easy' bits :-) */
   if ((votedata->option_one_rslt) > 0)
   {
      percentage = (float)votedata->option_one_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 7, "%d out of %d (%d%%)", votedata->option_one_rslt, votedata->voted_for, finale);
   adjust_result_slider(9, finale);
   adjust_result_slider(9, finale);
   percentage = 0;
   finale = 0;

   if ((votedata->option_two_rslt) > 0)
   {
      percentage = (float)votedata->option_two_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 12, "%d out of %d (%d%%)", votedata->option_two_rslt, votedata->voted_for, finale);
   adjust_result_slider(14, finale);
   adjust_result_slider(14, finale);
   percentage = 0;
   finale = 0;

   if ((votedata->option_three_rslt) > 0)
   {
      percentage = (float)votedata->option_three_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 17, "%d out of %d (%d%%)", votedata->option_three_rslt, votedata->voted_for, finale);
   adjust_result_slider(19, finale);
   adjust_result_slider(19, finale);
   percentage = 0;
   finale = 0;

   if ((votedata->option_four_rslt) > 0)
   {
      percentage = (float)votedata->option_four_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 22, "%d out of %d (%d%%)", votedata->option_four_rslt, votedata->voted_for, finale);
   adjust_result_slider(24, finale);
   adjust_result_slider(24, finale);
   percentage = 0;
   finale = 0;

   if ((votedata->option_five_rslt) > 0)
   {
      percentage = (float)votedata->option_five_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 27, "%d out of %d (%d%%)", votedata->option_five_rslt, votedata->voted_for, finale);
   adjust_result_slider(29, finale);
   adjust_result_slider(29, finale);
   percentage = 0;
   finale = 0;

   if ((votedata->option_six_rslt) > 0)
   {
      percentage = (float)votedata->option_six_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 32, "%d out of %d (%d%%)", votedata->option_six_rslt, votedata->voted_for, finale);
   adjust_result_slider(34, finale);
   adjust_result_slider(34, finale);
   percentage = 0;
   finale = 0;

   if ((votedata->option_seven_rslt) > 0)
   {
      percentage = (float)votedata->option_seven_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 37, "%d out of %d (%d%%)", votedata->option_seven_rslt, votedata->voted_for, finale);
   adjust_result_slider(39, finale);
   adjust_result_slider(39, finale);
   percentage = 0;
   finale = 0;

   if ((votedata->option_eight_rslt) > 0)
   {
      percentage = (float)votedata->option_eight_rslt / (float)votedata->voted_for;
   }
   else
   {
      percentage = 0;
   }
   finale = (int) ((float)percentage * 100);
   Icon_printf(result_window, 42, "%d out of %d (%d%%)", votedata->option_eight_rslt, votedata->voted_for, finale);
   adjust_result_slider(44, finale);
   adjust_result_slider(44, finale);
   percentage = 0;
   finale = 0;

   sprintf(buffer, "Results for vote #%d of #%d", currvote, questions);
   Window_SetTitle(result_window, buffer);

   return TRUE;
}



static void selectvote(int thisouropt)
{
   /* If the 'thisouropt' > 0 then update that vote option. Then mark this
      vote as having been voted upon.
      If = 0, then simply mark as voted upon without updating (a 'skip' function).
      */
   int  stat = 0;
   int  offset = 0;
   FILE *fp;

   if (thisouropt != 0)
   {
      r_in.r[0] = currvote;
      r_in.r[1] = thisouropt;
      _kernel_swi(VoteModule_AddVoteOpt, &r_in, &r_out);
      stat = r_out.r[0];
      switch (stat)
      {
         case 1 : break;
         case 2 : Error_Report(0, "Vote number is apparently invalid, while updating vote counter.");
                  break;
         case 3 : Error_Report(0, "Option is apparently invalid, while updating vote counter.");
                  break;
         default: Error_Report(0, "Whilst updating vote counter, unrecognised error code %d was received.", stat);
                  break;
      }

      if (stat != 1) return;

      _kernel_swi(VoteModule_Refresh, &r_in, &r_out);

      if (nvplog == 1)
      {
         if (( fp = fopen("<CastAWoteA$Dir>.NVP_Log","a") ) == NULL)
         {
            Error_Report(0, "Unable to open NVP log file to update vote pointer...");
         }
         else
         {
            strcpy(junk_s, votedata->question);
            junk_s[30] = '\0';
            fprintf(fp, "%-30s::%d ~ %s\n", junk_s, thisouropt, username);
            fclose(fp);
         }
      }
   }

   heapentry[currvote] = currvote+20;
   offset = 0;
   for (stat=0; stat != 221; stat++)
   {
      if (heapentry[stat] != 255)
      {
         junk_s[offset] = heapentry[stat];
         offset++;
      }
      junk_s[offset] = '\0';
   }

   r_in.r[0] = 6;
   r_in.r[1] = (int)username;
   r_in.r[2] = (int)junk_s;
   _kernel_swi(VoteModule_VoteOp, &r_in, &r_out);
   stat = r_out.r[0];

   switch (stat)
   {
      case 1 : break;
      case 2 : Error_Report(0, "Temporary file 'XHasVoted' cannot be opened, while updating voted heap.");
               break;
      case 3 : Error_Report(0, "New (updated) 'HasVoted' file cannot be opened, while updating voted heap.");
               break;
      case 5 : Error_Report(0, "Username is too long, while updating voted heap.");
      	       break;
      case 6 : Error_Report(0, "Heap entry is too long, while updating voted heap.");
      	       break;
      default: Error_Report(0, "Whilst updating voted heap, unrecognised error code %d was received.", stat);
               break;
   }
   if (stat != 1) return;

   currvote++;
   junk_v = searchforvote();
   currvote = junk_v;
   junk_v = fill_vote();

   return;
}



static void debug(char *text, ...)
{
   va_list params;
   char debugtext[250];
   message_block msg;

   if (debugging != TRUE) return;

   va_start(params, text);
   vsprintf(debugtext, text, params);

   strcpy((char *)msg.data.bytes, debugtext);

   msg.header.action  = 0x804C0;
   msg.header.size    = 256;
   msg.header.myref   = 0;
   msg.header.yourref = 0;

   Wimp_SendMessage(event_SEND, &msg, 0, 0);

   va_end(params);

   return;
}


static BOOL desktop_save(event_pollblock *event, void *reference)
{
   /* Doesn't do a whole lot ATM... */

   file_handle channel;
   BOOL error;
   message_block message;
   char command_line[] = "| Test line, inserted by CAWlocal. Will insert <CastAWote$Dir> expansion soon!";

   reference = reference;

   channel = (file_handle) event->data.message.data.words[0];

   error = Error_Check( File_WriteBytes(channel, command_line, strlen(command_line)));

   if ( !error ){
      if (Error_Check( File_WriteByte(channel, '\n')))
         error = TRUE;
   }

   if (error) { /* acknowledge message */
      message.header.yourref = message.header.myref;
      Wimp_SendMessage(event_USERMESSAGEACK, &message, message.header.sender, NULL);
   }

   return TRUE;
}



void mostcommon(void)
{
   /* Calculates the most common vote and most common voter... */
   int  mcq = -1;
   int  temp = 0;
   int  loop = 0;
   int  mostcommonq = 0;

   /* Most common question (*LAST* match highest number of voted-on's) */
   for (loop=1; loop<=questions; loop++)
   {
      struct votebuffer *votetemp = NULL;
      r_in.r[0] = loop;
      _kernel_swi(VoteModule_GetVote, &r_in, &r_out);
      if (r_out.r[0] != 1) Error_ReportFatal(0, "Tried to get an 'out of range' vote");

      votetemp = (struct votebuffer *)r_out.r[1];
      temp = votetemp->voted_for;

      if (temp > mcq)
      {
         mcq = temp;
         mostcommonq = loop;
      }
   }

   /* Most common voter (*LAST* match largest HASVOTED string) */

   /* VoteModule does not support 'wildcard' access to vote heap */

   Icon_printf(menu_window, 7, "%d", mostcommonq);

   return;
}



static void lowercase(char string[])
{
   int i = 0;

   while (string[i] != 0)
   {
      string[i] = tolower(string[i]);
      i++;
   }

   return;
}



static void convertregistered(void)
{
   /* Convert the 'registered' text here... */
   char temp[]={117, 0, 0, 0, 103};

   junk_v = 0;

   temp[1] = 110;
   temp[2] = 114;
   temp[3] = 101;

   if (regname_pre[0] != 170) finished = 255;
   if (regname_pre[2] != 173) finished = 255;
   if (regname_pre[4] != 184) finished = 255;
   if (regname_pre[6] != 172) finished = 255;
   if (regname_pre[8] != 186) finished = 255;
   if (regname_pre[9] != 173) finished = 255;

   while (regname_pre[junk_v] != 0)
   {
      regname_post[junk_v] = regname_pre[junk_v] ^ -1;
      junk_v++;
   }
   regname_post[junk_v] = 0;

   strcpy(junk_s, regname_post);
   lowercase(junk_s);

   if (strstr(junk_s, temp)==NULL)
   {
      /* User /is/ registered! */
      registered = TRUE;
   }
   else
   {
      /* Nope! */
      registered = FALSE;
   }

   return;
}



static void setupconfigure(void)
{
   /* Sets up the configuration window... */

   Icon_Shade(configure_window, 9);
   Icon_SetForeColour(configure_window, 9, 2);

   Icon_SetSelect(configure_window, 8, nvplog);
   Icon_SetSelect(configure_window, 9, nvplocal);
   Icon_SetText(configure_window, 3, username);
   Icon_SetText(configure_window, 7, fidoaddr);
   return;
}



static void loadconfigure(void)
{
   /* Load the configuration file... */

   FILE *fp;

   if (( fp = fopen("<CAWlocal$Dir>.Configure","rb") ) == NULL)
   {
      Error_Report(0, "Please configure this program (in the iconbar menu) before use...");
      return;
   }

   readstring(fp, junk_s);
   if (strcmp(junk_s, "!CAW_Local") != NULL)
   {
      Error_ReportFatal(0, "The configuration file is not a valid CAW_Local configuration file!?");
   }

   confver = readword(fp);
   if (confver != 1)
   {
      Error_Report(0, "The configuration file is of a later format than supported, so extended features will not be used. Saving your configuration will lose unsupported additions.");
   }

   nvplog = readword(fp);
   nvplocal = readword(fp);
   readstring(fp, username);
   readstring(fp, fidoaddr);

   fclose(fp);

   return;
}



static void saveconfigure(void)
{
   /* Save the configuration file... Type 1. */

   FILE *fp;

   if (( fp = fopen("<CAWlocal$Dir>.Configure","wb") ) == NULL)
   {
      Error_Report(0, "Unable to save configuration file...");
      return;
   }

   writestring(fp, "!CAW_Local");
   writeword(fp, 1);

   writeword(fp, nvplog);
   writeword(fp, nvplocal);
   Icon_GetText(configure_window, 3, username);
   Icon_GetText(configure_window, 7, fidoaddr);
   writestring(fp, username);
   writestring(fp, fidoaddr);

   fclose(fp);

   Icon_SetText(menu_window, 11, fidoaddr);
   if (strlen(fidoaddr) == 0)
      Icon_SetText(menu_window, 11, "Unavailable");

   return;
}



void readstring(FILE *file, char *string)
{
   int len, i;
   i = fgetc(file);
   if (i != 0x00)
   {
      fclose(file);
      Error_ReportFatal(0, "Configuration file possibly corrupted [ read_string() seeking \'&00\' ]");
   }

   len = fgetc(file);
   if (len > 0)
   {
      for (i = len-1; i >= 0; i--)
      {
         string[i] = fgetc(file);
      }
   }
   string[len] = '\0';

   return;
}



int readword(FILE *file)
{
   int num;
   num = fgetc(file);
   if (num != 0x40)
   {
      fclose(file);
      Error_ReportFatal(0, "Configuration file possibly corrupted [ read_word() seeking \'&40\' ]");
   }

   num = fgetc(file);
   num = (num << 8) | fgetc(file);
   num = (num << 8) | fgetc(file);
   num = (num << 8) | fgetc(file);

   return num;
}



void writestring(FILE *file, char *string)
{
   int len = strlen(string);
   int i;
   fputc(0x00, file);
   fputc(len, file);
   for (i = len-1; i >= 0; i--)
   {
      fputc(string[i], file);
   }

   return;
}



void writeword(FILE *file, int num)
{
   fputc(0x40, file);
   fputc((0xff & (num >> 24)), file);
   fputc((0xff & (num >> 16)), file);
   fputc((0xff & (num >> 8)), file);
   fputc((0xff & num), file);

   return;
}



static void buildandprocessnewvote(void)
{
   /* Build up the new vote, validify it, and then send it to VoteModule */

   int  reply = 0;
   FILE *fp;

   Icon_GetText(addvote_window, 2, addvote.creator);
   sprintf(addvote.system, "CastAWote Local at %s", fidoaddr);
   addvote.voted_for = 0;
   Icon_GetText(addvote_window, 7, addvote.question);
   sprintf(addvote.software_opts, "CastAWote Local 1.07 (%s) 0000000000000000", regname_post);
   Icon_GetText(addvote_window, 8, addvote.addinfo_one);
   Icon_GetText(addvote_window, 9, addvote.addinfo_two);
   Icon_GetText(addvote_window, 10, addvote.addinfo_three);
   Icon_GetText(addvote_window, 14, addvote.option_one_text);
   addvote.option_one_rslt = 0;
   Icon_GetText(addvote_window, 16, addvote.option_two_text);
   addvote.option_two_rslt = 0;
   Icon_GetText(addvote_window, 18, addvote.option_three_text);
   addvote.option_three_rslt = 0;
   Icon_GetText(addvote_window, 20, addvote.option_four_text);
   addvote.option_four_rslt = 0;
   Icon_GetText(addvote_window, 22, addvote.option_five_text);
   addvote.option_five_rslt = 0;
   Icon_GetText(addvote_window, 24, addvote.option_six_text);
   addvote.option_six_rslt = 0;
   Icon_GetText(addvote_window, 26, addvote.option_seven_text);
   addvote.option_seven_rslt = 0;
   Icon_GetText(addvote_window, 28, addvote.option_eight_text);
   addvote.option_eight_rslt = 0;
   addvote.vote_is_local[0] = 'N';
   addvote.vote_is_hidden[0] = 'N';
   addvote.vote_is_inaccessible[0] = 'N';
   addvote.vote_is_locked[0] = 'N';
   addvote.vote_data_crc = 0;

   /* This next bit of madness will shift around the options stuck in the wrong
      place so they are a contiguous lump. Better than hacking off the unwanted
      ones! */
   if (strlen(addvote.option_three_text) == 0)
   {
      strcpy(addvote.option_three_text, addvote.option_four_text);
      strcpy(addvote.option_four_text, addvote.option_five_text);
      strcpy(addvote.option_five_text, addvote.option_six_text);
      strcpy(addvote.option_six_text, addvote.option_seven_text);
      strcpy(addvote.option_seven_text, addvote.option_eight_text);
      strcpy(addvote.option_eight_text, "");
   }
   if (strlen(addvote.option_four_text) == 0)
   {
      strcpy(addvote.option_four_text, addvote.option_five_text);
      strcpy(addvote.option_five_text, addvote.option_six_text);
      strcpy(addvote.option_six_text, addvote.option_seven_text);
      strcpy(addvote.option_seven_text, addvote.option_eight_text);
      strcpy(addvote.option_eight_text, "");
   }
   if (strlen(addvote.option_five_text) == 0)
   {
      strcpy(addvote.option_five_text, addvote.option_six_text);
      strcpy(addvote.option_six_text, addvote.option_seven_text);
      strcpy(addvote.option_seven_text, addvote.option_eight_text);
      strcpy(addvote.option_eight_text, "");
   }
   if (strlen(addvote.option_six_text) == 0)
   {
      strcpy(addvote.option_six_text, addvote.option_seven_text);
      strcpy(addvote.option_seven_text, addvote.option_eight_text);
      strcpy(addvote.option_eight_text, "");
   }
   if (strlen(addvote.option_seven_text) == 0)
   {
      strcpy(addvote.option_seven_text, addvote.option_eight_text);
      strcpy(addvote.option_eight_text, "");
   }

   /* Update after */
   drawaddvote();

   /* Checks for the existance of certain string and.... */
   if (strlen(addvote.creator) == 0)
   {
      Error_Report(0, "You must have a creator's name!");
      strcpy(addvote.creator, username);
      drawaddvote();
      return;
   }

   if (strlen(addvote.question) == 0)
   {
      Error_Report(0, "You must have a question!");
      return;
   }

   if (strlen(addvote.option_one_text) == 0 || strlen(addvote.option_two_text) == 0)
   {
      Error_Report(0, "You must have an option one and an option two...");
      return;
   }

   r_in.r[0] = (int)&addvote;
   _kernel_swi(VoteModule_PutVote, &r_in, &r_out);
   reply = r_out.r[0];

   if (reply != 1)
   {
      switch(reply)
      {
         case 2 : Error_Report(0, "Unable to add this question - there is no more room! Please delete some of the older votes.");
              	  return;
              	  break; /* Hmm... Is this used? */
         default: Error_Report(0, "This vote was rejected by VoteModule (reason code %d). Please inform Richard Murray of the vote you tried to add and the reason code returned.", reply);
                  return;
                  break;
      }
   }

   _kernel_swi(VoteModule_Refresh, &r_in, &r_out);

   /* Assume all okay at this stage, so see if NVP_Log update required... */
   if (nvplog == 1)
   {
      if (( fp = fopen("<CastAWoteA$Dir>.NVP_Log","a") ) == NULL)
      {
         Error_Report(0, "Unable to open NVP log file to write new question...");
      }
      else
      {
         fprintf(fp, "NEW VOTE:\n");
         fprintf(fp, "  %s\n", addvote.creator);
         fprintf(fp, "  %s\n", addvote.system);
         fprintf(fp, "  %s\n", addvote.question);
         fprintf(fp, "  %s\n", addvote.software_opts);
         fprintf(fp, "  %s\n", addvote.addinfo_one);
         fprintf(fp, "  %s\n", addvote.addinfo_two);
         fprintf(fp, "  %s\n", addvote.addinfo_three);
         fprintf(fp, "  %s\n", addvote.option_one_text);
         fprintf(fp, "  %s\n", addvote.option_two_text);
         fprintf(fp, "  %s\n", addvote.option_three_text);
         fprintf(fp, "  %s\n", addvote.option_four_text);
         fprintf(fp, "  %s\n", addvote.option_five_text);
         fprintf(fp, "  %s\n", addvote.option_six_text);
         fprintf(fp, "  %s\n", addvote.option_seven_text);
         fprintf(fp, "  %s\n", addvote.option_eight_text);
         fprintf(fp, ":END VOTE\n");
         fclose(fp);
      }
   }

   /* Update the counters */
   _kernel_swi(VoteModule_GetVotesCount, &r_in, &r_out);
   questions = r_out.r[0];
   maxvotes  = r_out.r[1];

   Icon_printf(menu_window, 5, "%d", questions);

   /* And all done! Not /that/ painful now, was it? */
   Error_Report(0, "Your vote has been added to the database...");

   return;
}



static void drawaddvote(void)
{
   strcpy(addvote.creator, username);
   Icon_SetText(addvote_window, 2, addvote.creator);
   Icon_SetText(addvote_window, 3, addvote.system);
   Icon_SetText(addvote_window, 7, addvote.question);
   Icon_SetText(addvote_window, 8, addvote.addinfo_one);
   Icon_SetText(addvote_window, 9, addvote.addinfo_two);
   Icon_SetText(addvote_window, 10, addvote.addinfo_three);
   Icon_SetText(addvote_window, 14, addvote.option_one_text);
   Icon_SetText(addvote_window, 16, addvote.option_two_text);
   Icon_SetText(addvote_window, 18, addvote.option_three_text);
   Icon_SetText(addvote_window, 20, addvote.option_four_text);
   Icon_SetText(addvote_window, 22, addvote.option_five_text);
   Icon_SetText(addvote_window, 24, addvote.option_six_text);
   Icon_SetText(addvote_window, 26, addvote.option_seven_text);
   Icon_SetText(addvote_window, 28, addvote.option_eight_text);
   Icon_SetSelect(addvote_window, 4, 0);
   Window_SetTitle(addvote_window, "Add a new vote...");

   return;
}
