//////////////////////////////////////////////////////////////////
// Shell
//
// C program shell for rapid application development
// The Flying Pig!
// Started 17/7/2003
//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////
// Includes

#include "oslib/osmodule.h"
#include "oslib/messagetrans.h"
#include "oslib/wimp.h"
#include "oslib/dragasprite.h"
#include "oslib/macros.h"
#include "oslib/osfile.h"
#include "oslib/resolver.h"
#include "oslib/os.h"
#include "oslib/osbyte.h"
//#include "sys/byteorder.h"
#include "sys/select.h"

#include "flexlib/flex.h"

#include "Comp1.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <stddef.h>
#include <arpa/inet.h>

#include <kernel.h>

//////////////////////////////////////////////////////////////////
// Defines

#define WORDALIGN(b) (((b) + 3) & ~3)
#define RANGE(var,min,max)   if ((var) < (min)) var = min; \
                             if ((var) > (max)) var = max;

#define MENU_XPOS_OFFSET     (-64)
/* (96 + 44 * <menu items> + 24 * <menu separators>) */
#define ICONBAR_MENU_YPOS    (96 + 44 * 3 + 24 * 0)
#define RAMTRANSMIT_SIZE     (10 * 1024)
#define FILE_LOAD_MAX        (64)
#define POLL_DELAY           (10)
#define SEND_SMALL           (LINK_SEND_MAX)
#define SEND_LARGE           (1024*20)
#define LINKFILENAME_MAX     (RAMTRANSMIT_SIZE)
#define DATAIN_CHUNK_SIZE    (1024*5)

#define COMMAND_LEN          (1024)
#define BUFFERSIZE           (1024*128)
#define COMMAND_LEN_QUEUE    (1032*10)
#define LISTEN_BACKLOG       (0)
#define DEFAULT_PORT         (6435)
#define END_PORT             (3389)

//////////////////////////////////////////////////////////////////
// Structures

typedef enum
{
  STATE_INVALID = -1,

  STATE_DISCONNECTED,

  STATE_CREATESOCKET,
  STATE_RESOLVING,
  STATE_CONNECT,
  STATE_LISTEN,
  STATE_CONNECTING,
  STATE_ACCEPT,
  STATE_CONNECTED,

  STATE_CANCEL,
  STATE_FINISH,
  STATE_FINISHREAD,
  STATE_FINISHED,
  STATE_FINISHPOST,

  STATE_NUM
} STATE;

typedef enum
{
  SOCK_INVALID = -1,

  SOCK_OK,
  SOCK_ERR,
  SOCK_FAIL,
  SOCK_WAIT,
  SOCK_RECEIVE,
  SOCK_MULTIDONE,
  SOCK_CLOSED,

  SOCK_NUM
} SOCK;

typedef struct _NetLoop NetLoop;

struct _NetLoop
{
  STATE                       eState;
  STATE                       ePrevState;
  char                        szAddress[COMMAND_LEN - 5];
  int                         nAddress;
  socket_s                    sSocket;
  int                         nPort;
  char                        pcBuffer[BUFFERSIZE];
  int                         nReceived;
  NetLoop                     *psSendNetLoop;
  int                         nIcon;
};

//////////////////////////////////////////////////////////////////
// Global variables

static char                   gszProgTitle[128];
static char                   gpcTemp[256];
static char                   *gpcMessages;
//static wimp_w                 gwhMain;
//static wimp_w                 gwhSave;
static wimp_w                 gwhConf;
static wimp_w                 gwhWarn;
static wimp_w                 gwhStat;
static wimp_i                 gihIconBarIcon;
static int                    gnMenuWins = 0;
static wimp_w                 gawhMenuWinHandle[10];
static int                    gnMenuXpos = 0;
static int                    gnMenuYpos = 0;
static wimp_menu              *gpcMenuCurrent = NULL;
static wimp_menu              *gpcIconBarMenu;
static wimp_menu              *gpcWindowMenu;
static wimp_t                 gnTaskHandle;
static bool                   gboDrag = FALSE;
static char                   gacFontRef[255];
static int                    gnMessageMyRef = 0;


static NetLoop                gsNetLoop;
static NetLoop                gsNetLoopClient;
static int                    gnSent;
static int                    gnReceived;

void ConfigSetUpWindow (void);
void ConfigSetFromWindow (void);

void NetworkLoop (NetLoop * psNetLoop);
SOCK CreateSocket (socket_s * psSocket, NetLoop * psNetLoop);
STATE Connect (int nAddress, socket_s * psSocket, int nPort);
STATE CheckConnected (socket_s sSocket);
void PrintState (STATE eState, NetLoop * psNetLoop);
STATE Resolve (char * szHostName, int * pnAddress);
SOCK SendDataUpdate (NetLoop * psNetLoop);
void Cancel (STATE * peState);
SOCK SocketRead (socket_s * psSocket, NetLoop * psNetLoop);
STATE ImmediateUpdate (socket_s * psSocket);
STATE Connected (socket_s * psSocket, NetLoop * psNetLoop);
STATE Listen (socket_s * psSocket);
STATE Accept (socket_s * psSocket, NetLoop * psNetLoop);
void ResetStatus (NetLoop * psNetLoop);
STATE ConnectedEnd (socket_s * psSocket, NetLoop * psNetLoop);

//////////////////////////////////////////////////////////////////
// Main application

// Respond to swi errors
inline void err (os_error * sError)
{
  if (sError)
  {
    ShowWarning (sError);
  }
}

// Main program
int main (/*int argc, char * * argv*/)
{
  wimp_block                  cBlock;
  wimp_version_no             nVersion;
  int                         nCount;
  wimp_event_no               nEvent;
  wimp_MESSAGE_LIST(3) sMessages = {{
//                                      message_DATA_SAVE,
//                                      message_DATA_SAVE_ACK,
//                                      message_DATA_LOAD,
//                                      message_DATA_OPEN,
//                                      message_RAM_FETCH,
//                                      message_RAM_TRANSMIT,
                                      message_HELP,
                                      message_URL_LAUNCH,
//                                      message_LINK_CONTROL,
//                                      message_LINK_OPEN,
//                                      message_LINK_CLOSE,
//                                      message_LINK_SEND,
//                                      message_LINK_DATASAVE,
//                                      message_LINK_RAMFETCH,
 //                                     message_LINK_RAMTRANSMIT,
                                      0u}};
  wimp_message_list *psUserMessages = (wimp_message_list*)&sMessages;
  os_t                        nTime;

  // Load Messages file
  gpcMessages = osmodule_alloc (17 + sizeof(MESSAGES));
  strcpy(gpcMessages + 16, MESSAGES);

  messagetrans_open_file ((messagetrans_control_block*)gpcMessages,
    gpcMessages + 16, 0);

  // Initialise task
  gnTaskHandle = wimp_initialise (wimp_VERSION_RO3, Tag ("Tsk"),
    psUserMessages, & nVersion);

  // Initialise the flex memory
  strncpy (gszProgTitle, Tag("Tsk"), sizeof (gszProgTitle));
  flex_init (gszProgTitle, 0, 0);

  // Load templates
  for (nCount = 0; nCount < 256; nCount++)
  {
    gacFontRef[nCount] = 0;
  }
  wimp_open_template (TEMPLATES);
  gwhWarn = LoadTemplate ("Warning");
  gwhConf = LoadTemplate ("Config");
  gwhStat = LoadTemplate ("Status");

  // Set up general variables
  gsNetLoop.eState = STATE_DISCONNECTED;
  gsNetLoop.ePrevState = STATE_INVALID;
  gsNetLoop.nPort = DEFAULT_PORT;
  strcpy (gsNetLoop.szAddress, "127.0.0.1");
  gsNetLoop.sSocket = NULL;
  gsNetLoop.nReceived = 0;
  gsNetLoop.nIcon = 0;

  gsNetLoopClient.eState = STATE_DISCONNECTED;
  gsNetLoopClient.ePrevState = STATE_INVALID;
  gsNetLoopClient.nPort = END_PORT;
  strcpy (gsNetLoopClient.szAddress, "127.0.0.1");
  gsNetLoopClient.sSocket = NULL;
  gsNetLoopClient.nReceived = 0;
  gsNetLoopClient.psSendNetLoop = NULL;
  gsNetLoopClient.nIcon = 2;

  gsNetLoop.psSendNetLoop = & gsNetLoopClient;
  gsNetLoopClient.psSendNetLoop = & gsNetLoop;

  gnReceived = 0;
  gnSent = 0;
  ResetStatus (& gsNetLoop);
  ResetStatus (& gsNetLoopClient);

  // Create menus
  gpcIconBarMenu = CreateMenu (Tag ("Menu1"));

  // Close templates
  wimp_close_template ();

  // Set up the version info
  SetIconText (VERSION_STRING_LONG, gawhMenuWinHandle[0], 3);

  // Create icon
  gihIconBarIcon = CreateIconbarIcon (Tag ("Icn"), 76, 76);

  // Main polling loop
  //////////////////////////////////////////////////////////////////
  do
  {
    nTime = os_read_monotonic_time () + POLL_DELAY;
//    nEvent = wimp_poll_idle (FLAGS, & cBlock, nTime, NULL);
    nEvent = wimp_poll (FLAGS, & cBlock, NULL);
    switch (nEvent)
    {
      case  0: // Null
        NullPoll (& cBlock);
        break;
      case  1: // Redraw window
        break;
      case  2: // Open window
        OpenWindow (& cBlock);
        break;
      case  3: // Closewindow
        CloseWindow (& cBlock);
        break;
      case  4: // Pointer leaving window
        break;
      case  5: // Pointer entering window
        break;
      case  6:
        MouseClick (& cBlock);
        break;
      case  7: // User drag box
        break;
      case  8: // Keys
        Keys (& cBlock);
        break;
      case  9:
        MenuSelect (& cBlock);
        break;
      case 10: // Scroll request
        break;
      case 11: // Lose caret
        break;
      case 12: // Gain caret
        break;
      case 17: // Receive message
      case 18:
        Receive (& cBlock);
        break;
      case 19: // UserMessage Acknowledge
        Acknowledged (& cBlock);
        break;
      default: // Default
        break;
    }
  } while TRUE;

  //////////////////////////////////////////////////////////////////

  return 0;
}

//////////////////////////////////////////////////////////////////
// Poll 16,17: Message received
void Receive (wimp_block *pcBlock)
{
  switch (pcBlock->message.action)
  {
    case message_QUIT:
      Quit ();
      break;
    case message_HELP:
      Help (pcBlock);
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Poll 19: Message acknowledge received
void Acknowledged (wimp_block *pcBlock)
{
  switch (pcBlock->message.action)
  {
    case message_RAM_FETCH:
//      RamFetchReturned (pcBlock);
      break;
    case message_URL_LAUNCH:
      OpenURLReturned (pcBlock);
      break;
  }
  gnMessageMyRef = 0;
}

//////////////////////////////////////////////////////////////////
// Quit task
void Quit (void)
{
  int                         nCount;
  int                         nRemove;

  // Free the fonts
  for (nCount = 0; nCount < 256; nCount++)
  {
    if (gacFontRef[nCount])
    {
      for (nRemove = 0; nRemove < gacFontRef[nCount]; nRemove++)
      {
        xfont_lose_font ((font_f)nCount);
      }
    }
  }

  // Close any open sockets
  if (gsNetLoop.eState != STATE_DISCONNECTED)
  {
    gsNetLoop.eState = STATE_FINISH;
    NetworkLoop (& gsNetLoop);
  }

  if (gsNetLoopClient.eState != STATE_DISCONNECTED)
  {
    gsNetLoopClient.eState = STATE_FINISH;
    NetworkLoop (& gsNetLoopClient);
  }

  // Close the program
  wimp_close_down (gnTaskHandle);
}

//////////////////////////////////////////////////////////////////
// Poll 6: Process mouse clicks
void MouseClick (wimp_block *pcBlock)
{
  int                         nXpos       = pcBlock->pointer.pos.x;
//  int                         nYpos       = pcBlock->pointer.pos.y;
  wimp_mouse_state            nButton     = pcBlock->pointer.buttons;
  wimp_w                      whWindow    = pcBlock->pointer.w;
  wimp_i                      ihIcon      = pcBlock->pointer.i;
//  char                        *szFileIcon;

  if (whWindow == wimp_ICON_BAR)
  {
    switch (nButton)
    {
      case wimp_CLICK_SELECT:
        OpenWindowInit (gwhStat);
        break;
      case wimp_CLICK_MENU:
        OpenMenu (gpcIconBarMenu, nXpos + MENU_XPOS_OFFSET,
          ICONBAR_MENU_YPOS);
        break;
    }
  }

  if (whWindow == gwhWarn)
  {
    switch (ihIcon)
    {
      case (wimp_i)0: // OK
        CloseWindowHandle (gwhWarn);
        break;
    }
  }

  if (whWindow == gwhConf)
  {
    switch (ihIcon)
    {
      case (wimp_i)0: // Cancel
        ConfigSetUpWindow ();
        if (nButton & wimp_CLICK_SELECT)
        {
          CloseWindowHandle (gwhConf);
        }
        break;
      case (wimp_i)1: // OK
        ConfigSetFromWindow ();
        if (nButton & wimp_CLICK_SELECT)
        {
          CloseWindowHandle (gwhConf);
        }
        break;
    }
  }

  if (whWindow == gwhStat)
  {
    switch (ihIcon)
    {
      case (wimp_i)4: // Disconnect
        if (gsNetLoop.eState != STATE_DISCONNECTED)
        {
          gsNetLoop.eState = STATE_FINISH;
        }
        if (gsNetLoopClient.eState != STATE_DISCONNECTED)
        {
          gsNetLoopClient.eState = STATE_FINISH;
        }
        break;
      case (wimp_i)5: // Connect
        if (gsNetLoop.eState == STATE_DISCONNECTED)
        {
          gsNetLoop.eState = STATE_CREATESOCKET;
        }
        if (gsNetLoopClient.eState == STATE_DISCONNECTED)
        {
          gsNetLoopClient.eState = STATE_CREATESOCKET;
        }
        break;
    }
  }

  if (whWindow == gawhMenuWinHandle[0])
  {
    switch (ihIcon)
    {
      case (wimp_i)8: // Email
        OpenURL (Tag("Email"));
        if (nButton != wimp_CLICK_ADJUST)
        {
          CloseMenu ();
        }
        break;
      case (wimp_i)9: // Website
        OpenURL (Tag("Website"));
        if (nButton != wimp_CLICK_ADJUST)
        {
          CloseMenu ();
        }
        break;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Poll 8: Act on user key presses
void Keys (wimp_block *pcBlock)
{
  wimp_w                      whWindow;
  wimp_i                      ihIcon;
  wimp_key_no                 nKey;

  whWindow = pcBlock->key.w;
  ihIcon = pcBlock->key.i;
  nKey = pcBlock->key.c;

  switch (nKey)
  {
    case wimp_KEY_RETURN:
//      if (whWindow == gwhSave)
//      {
//        gnSaveFileType = 0xfff;
//        SaveSave ();
//        CloseMenu ();
//      }
      if (whWindow == gwhWarn)
      {
        CloseWarning ();
      }
      if (whWindow == gwhConf)
      {
        ConfigSetFromWindow ();
        CloseWindowHandle (gwhConf);
      }
      break;
    case wimp_KEY_ESCAPE:
      if (whWindow == gwhWarn)
      {
        CloseWarning ();
      }
      if (whWindow == gwhConf)
      {
        ConfigSetUpWindow ();
        CloseWindowHandle (gwhConf);
      }
      break;
    default:
      xwimp_process_key (nKey);
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Poll 9: Act on menu selections
void MenuSelect (wimp_block *pcBlock)
{
  wimp_pointer                sPointer;

  xwimp_get_pointer_info (& sPointer);

  if (gpcMenuCurrent == gpcIconBarMenu)
  {
    switch (pcBlock->selection.items[0])
    {
      case 1: // Configure
        ConfigSetUpWindow ();
        OpenWindowInitCentre (gwhConf);
        xwimp_set_caret_position (gwhConf, (wimp_i)3, 0, 0, -1, -1);
        break;
      case 2: // Quit
        Quit ();
        break;
      default:
        break;
    }
  }

  if (sPointer.buttons & wimp_CLICK_ADJUST)
  {
    OpenMenu (gpcMenuCurrent, gnMenuXpos, gnMenuYpos);
  }
}

//////////////////////////////////////////////////////////////////
// Poll 2: Open window given window block
void OpenWindow (wimp_block *pcBlock)
{
  xwimp_open_window (& pcBlock->open);
}

//////////////////////////////////////////////////////////////////
// Open a window on screen that is currently closed
void OpenWindowInit (wimp_w whWindow)
{
  wimp_window_state           sState;

  sState.w = whWindow;
  xwimp_get_window_state (& sState);
  sState.next = wimp_TOP;
  xwimp_open_window ((wimp_open *)& sState);
}

//////////////////////////////////////////////////////////////////
// Open a window in the centre of the screen
void OpenWindowInitCentre (wimp_w whWindow)
{
  int                         nHeight;
  int                         nWidth;
  int                         nEigFactor;
  int                         nSize;
  int                         nPosition;
  wimp_window_state           sState;

  sState.w = whWindow;
  xwimp_get_window_state (& sState);
  sState.next = wimp_TOP;

  nWidth = sState.visible.x1 - sState.visible.x0;
  nHeight = sState.visible.y1 - sState.visible.y0;

  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR,
    & nEigFactor, NULL);
  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT,
    & nSize, NULL);
  nPosition = ((nSize << nEigFactor) - nWidth) / 2;
  sState.visible.x0 = nPosition;
  sState.visible.x1 = nPosition + nWidth;

  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR,
    & nEigFactor, NULL);
  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT,
    & nSize, NULL);
  nPosition = ((nSize << nEigFactor) - nHeight) / 2;
  sState.visible.y0 = nPosition;
  sState.visible.y1 = nPosition + nHeight;

  xwimp_open_window ((wimp_open *)& sState);
}

//////////////////////////////////////////////////////////////////
// Open a given menu at the given position on screen
void OpenMenu (wimp_menu * pcMenu, int nXpos, int nYpos)
{
  xwimp_create_menu (pcMenu, nXpos, nYpos);

  gnMenuXpos = nXpos;
  gnMenuYpos = nYpos;
  gpcMenuCurrent = pcMenu;
}

//////////////////////////////////////////////////////////////////
// Poll 3: Close a window
void CloseWindow (wimp_block *pcBlock)
{
  wimp_close_window (pcBlock->close.w);
}

//////////////////////////////////////////////////////////////////
// Close window given handle
void CloseWindowHandle (wimp_w whWindow)
{
  wimp_close_window (whWindow);
}

//////////////////////////////////////////////////////////////////
// Close the currently open menu
void CloseMenu (void)
{
  xwimp_create_menu ((wimp_menu*)-1, 0, 0);

  gnMenuXpos = 0;
  gnMenuYpos = 0;
  gpcMenuCurrent = NULL;
}

//////////////////////////////////////////////////////////////////
// Create an iconbar icon
wimp_i CreateIconbarIcon (char * szSprite, int nWidth, int nHeight)
{
  wimp_i                      ihIcon;
  wimp_icon_create            sIconBlock;

  sIconBlock.w = wimp_ICON_BAR_RIGHT;
  sIconBlock.icon.extent.x0 = 0;
  sIconBlock.icon.extent.y0 = 0;
  sIconBlock.icon.extent.x1 = nWidth;
  sIconBlock.icon.extent.y1 = nHeight;
  sIconBlock.icon.flags = wimp_ICON_SPRITE |
                   (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT);
  strncpy(sIconBlock.icon.data.sprite, szSprite, 12);

  ihIcon = wimp_create_icon (& sIconBlock);

  return ihIcon;
}

//////////////////////////////////////////////////////////////////
// Create window from Templates file
wimp_w LoadTemplate (char * szWindowTitle)
{
  wimp_window                 *pcWindow;
  char *                      pcIndirected;
  int                         nWindowSize;
  int                         nIndirectedSize;
  char                        szTitle[12];
  wimp_w                      whWindow;

  wimp_load_template (0, NULL, NULL, NULL, szWindowTitle, 0,
    & nWindowSize, & nIndirectedSize);

  pcWindow = (wimp_window*)malloc (nWindowSize);
  pcIndirected = malloc (nIndirectedSize);
  strncpy (szTitle, szWindowTitle, 12);

  wimp_load_template (pcWindow, pcIndirected, pcIndirected + nIndirectedSize,
    gacFontRef, szWindowTitle, 0, NULL, NULL);

  whWindow = wimp_create_window (pcWindow);

  free((void *)pcWindow);

  return whWindow;
}

//////////////////////////////////////////////////////////////////
// Create window from Templates file with sprites
wimp_w LoadTemplateSprites (char * szWindowTitle, osspriteop_area * pcSpriteArea)
{
  wimp_window                 *pcWindow;
  char *                      pcIndirected;
  int                         nWindowSize;
  int                         nIndirectedSize;
  char                        szTitle[12];
  wimp_w                      whWindow;

  wimp_load_template (0, NULL, NULL, NULL, szWindowTitle, 0,
    & nWindowSize, & nIndirectedSize);

  pcWindow = (wimp_window*)malloc (nWindowSize);
  pcIndirected = malloc (nIndirectedSize);
  strncpy (szTitle, szWindowTitle, 12);

  wimp_load_template (pcWindow, pcIndirected, pcIndirected + nIndirectedSize,
    gacFontRef, szWindowTitle, 0, NULL, NULL);

  if (pcSpriteArea)
  {
    pcWindow->sprite_area = pcSpriteArea;
  }

  whWindow = wimp_create_window (pcWindow);

  free((void *)pcWindow);

  return whWindow;
}

//////////////////////////////////////////////////////////////////
// Create a menu from a text string - usually kept in the messages file
wimp_menu * CreateMenu (char * szMenu)
{
  wimp_menu                   *pcMenu;
  char *                      pcBuffer;
  char *                      nAt = 0;
  char *                      nAt2 = 0;
  int                         nWidth = 0;
  wimp_menu_flags             uFlags;
  char                        szMenuString[512];
  char *                      pcMenuString;
  int                         nMenuItems;
  char                        *szItem;
  int                         nMenuEntry;
  int                         nStringLen;

  nMenuItems = -1;
  szItem = szMenu;
  while (szItem)
  {
    nMenuItems++;
    szItem = strchr(szItem + 1, ',');
  }

  pcMenu = (wimp_menu*)malloc (wimp_SIZEOF_MENU(nMenuItems));
  pcBuffer = (char *)malloc (strlen(szMenu));

  nAt = strchr (szMenu, ',');
  if (!nAt)
  {
    nAt = (char *)strlen (szMenu);
  }
  else
  {
    nAt -= (int)szMenu;
  }
  strncpy (szMenuString, szMenu, (int)nAt);
  szMenuString[(int)nAt] = 0;

  if (strlen (szMenuString) >= 12)
  {
    strcpy (pcBuffer, szMenuString);
    pcMenu->title_data.indirected_text.text = pcBuffer;
    pcBuffer += strlen (szMenuString) + 1;
    uFlags = wimp_MENU_TITLE_INDIRECTED;
  }
  else
  {
    strncpy (pcMenu->title_data.text, szMenuString, 12);
    uFlags = 0u;
  }
  pcMenu->title_fg = wimp_COLOUR_BLACK;
  pcMenu->title_bg = wimp_COLOUR_LIGHT_GREY;
  pcMenu->work_fg = wimp_COLOUR_BLACK;
  pcMenu->work_bg = wimp_COLOUR_WHITE;

  pcMenu->height = 44;
  pcMenu->gap = 0;

  nMenuEntry = 0;
  do
  {
    pcMenu->entries[nMenuEntry].sub_menu = (wimp_menu*)-1;
    pcMenu->entries[nMenuEntry].icon_flags = wimp_ICON_TEXT
                   | wimp_ICON_FILLED
                   | (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT);
    nAt2 = nAt + 1;

    nAt = strchr (szMenu + (int)nAt2, ',');
    if (!nAt)
    {
      nAt = (char *)strlen (szMenu) + 1;
    }
    else
    {
      nAt -= (int)szMenu;
    }
    strncpy (szMenuString, szMenu + (int)nAt2, (int)nAt - (int)nAt2);
    szMenuString[(int)nAt - (int)nAt2] = 0;

    pcMenuString = szMenuString;

    if (pcMenuString[0] == '+')
    {
      uFlags = wimp_MENU_TICKED;
      pcMenuString++;
    }
    if (pcMenuString[0] == '-')
    {
      uFlags |= wimp_MENU_SEPARATE;
      pcMenuString++;
    }
    if (pcMenuString[0] == '|')
    {
      uFlags |= wimp_MENU_WRITABLE;
      pcMenuString++;
    }
    if (pcMenuString[0] == '>')
    {
      pcMenuString++;
      if (gnMenuWins < (int)sizeof(gawhMenuWinHandle))
      {
        gawhMenuWinHandle[gnMenuWins] = LoadTemplate (pcMenuString);
        pcMenu->entries[nMenuEntry].sub_menu
                   = (wimp_menu*)gawhMenuWinHandle[gnMenuWins];

        gnMenuWins++;
      }
    }

    nStringLen = (int)strlen (pcMenuString);
    if (strlen (pcMenuString) >= 12)
    {
      pcMenu->entries[nMenuEntry].icon_flags |= wimp_ICON_INDIRECTED;
      pcMenu->entries[nMenuEntry].data.indirected_text.text = pcBuffer;
      pcMenu->entries[nMenuEntry].data.indirected_text.validation = NULL;
      pcMenu->entries[nMenuEntry].data.indirected_text.size = nStringLen + 1;
      strcpy (pcBuffer, pcMenuString);
      pcBuffer += nStringLen + 1;
    }
    else
    {
      strncpy (pcMenu->entries[nMenuEntry].data.text, pcMenuString, 12);
    }

    pcMenu->entries[nMenuEntry].menu_flags = uFlags;
    if (nStringLen > nWidth)
    {
      nWidth = nStringLen;
    }
    nMenuEntry++;
    uFlags = 0u;
  } while ((int)nAt < (int)strlen (szMenu));

  pcMenu->width = (nWidth + 1) * 16;
  pcMenu->entries[nMenuEntry - 1].menu_flags |= wimp_MENU_LAST;

  return pcMenu;
}

//////////////////////////////////////////////////////////////////
// Attach a submenu to a menu
void SetSubMenu (wimp_menu * pcMain, int nItem, wimp_menu * pcSub)
{
  pcMain->entries[nItem].sub_menu = pcSub;
}

//////////////////////////////////////////////////////////////////
// Set the greyed out status of a menu item
void SetMenuItemGreyness (bool boState, wimp_menu * pcMenu, int nItem)
{
  if (boState)
  {
    pcMenu->entries[nItem].icon_flags |= wimp_ICON_SHADED;
  }
  else
  {
    pcMenu->entries[nItem].icon_flags &= ~wimp_ICON_SHADED;
  }
}

//////////////////////////////////////////////////////////////////
// Set the ticked status of a menu item
void SetMenuItemTicked (bool boTicked, wimp_menu * pcMenu, int nItem)
{
  if (boTicked)
  {
    pcMenu->entries[nItem].menu_flags |= wimp_MENU_TICKED;
  }
  else
  {
    pcMenu->entries[nItem].menu_flags &= ~wimp_MENU_TICKED;
  }
}

//////////////////////////////////////////////////////////////////
// Get the text string from an icon
char * GetIconText (wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;
  char                        *szString;
  int                         nTerminate = 0;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_INDIRECTED)
  {
    szString = sIconState.icon.data.indirected_text.text;
    while (szString[nTerminate] >= 32)
    {
      nTerminate++;
    }
    szString[nTerminate] = 0;
  }
  else
  {
    szString = sIconState.icon.data.text;
    while ((szString[nTerminate] >= 32) && (nTerminate < 12))
    {
      nTerminate++;
    }
    szString[nTerminate] = 0;
  }
  return szString;
}

//////////////////////////////////////////////////////////////////
// Set the text string for an icon
void SetIconText (char * szText, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_INDIRECTED)
  {
    strcpy (sIconState.icon.data.indirected_text.text, szText);
  }
  else
  {
    strncpy (sIconState.icon.data.text, szText, 12);
    sIconState.icon.data.text[11] = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, 0, 0);
}

//////////////////////////////////////////////////////////////////
// Get the selection state from an icon
bool GetIconSelectionState (wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;
  bool                        boState;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_SELECTED)
  {
    boState = TRUE;
  }
  else
  {
    boState = FALSE;
  }

  return boState;
}

//////////////////////////////////////////////////////////////////
// Set the selection state from an icon
void SetIconSelectionState (bool boState, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_flags             uFlagsEOR;

  if (boState)
  {
    uFlagsEOR = wimp_ICON_SELECTED;
  }
  else
  {
    uFlagsEOR = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, uFlagsEOR, wimp_ICON_SELECTED);
}

//////////////////////////////////////////////////////////////////
// Grey out or ungrey an icon
void SetIconGreyness (bool boState, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_flags             uFlagsEOR;

  if (boState)
  {
    uFlagsEOR = wimp_ICON_SHADED;
  }
  else
  {
    uFlagsEOR = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, uFlagsEOR, wimp_ICON_SHADED);
}

//////////////////////////////////////////////////////////////////
// Response to a Help message
void Help (wimp_block *pcBlock)
{
  wimp_w                      whWindow;
  wimp_i                      ihIcon;
  char                        szHelpText[236];
  int                         nStrLen;
  char                        szTag[256];
  int                         nWinNum = 0;
  wimp_selection              sSelection;
  bool                        boGeneral;

  whWindow = *(wimp_w*)(pcBlock->message.data.reserved + 12);
  ihIcon = *(wimp_i*)(pcBlock->message.data.reserved + 16);

  // Warning window
  if (whWindow == gwhWarn)
  {
    nWinNum = 3;
  }

  if (nWinNum)
  {
    // Window
    szHelpText[0] = 0;
    sprintf (szTag, "H%dI%d", nWinNum, ihIcon);
    strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
    nStrLen = strlen(szHelpText);
    if (nStrLen == 0)
    {
      sprintf (szTag, "H%d", nWinNum);
      strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
    }
    szHelpText[sizeof(szHelpText) - 1] = 0;
    nWinNum = -1;
  }
  else
  {
    // Menu
    wimp_get_menu_state (wimp_GIVEN_WINDOW_AND_ICON, & sSelection,
      whWindow, ihIcon);

    boGeneral = FALSE;
    if (sSelection.items[0] != -1)
    {
      // Iconbar menu
      if (gpcMenuCurrent == gpcIconBarMenu)
      {
        nWinNum = 6;
      }

      // Main window menu
      if (gpcMenuCurrent == gpcWindowMenu)
      {
        nWinNum = 7;
      }
    }
    if (nWinNum)
    {
      if (boGeneral)
      {
        sprintf (szTag, "H%d", nWinNum);
      }
      else
      {
        if (sSelection.items[1] != -1)
        {
          sprintf (szTag, "H%dI%dI%d", nWinNum, sSelection.items[0],
            sSelection.items[1]);
        }
        else
        {
          sprintf (szTag, "H%dI%d", nWinNum, sSelection.items[0]);
        }
      }

      strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
      nWinNum = -1;
    }
  }

  if (nWinNum == -1)
  {
    pcBlock->message.size = WORDALIGN((24 + strlen(szHelpText)));
    pcBlock->message.your_ref = pcBlock->message.my_ref;
    pcBlock->message.action = 0x503;
    strncpy (pcBlock->message.data.reserved, szHelpText, sizeof(szHelpText));

    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
      pcBlock->message.sender);
  }
}

//////////////////////////////////////////////////////////////////
// Obtain string from Messages file
char * Tag (char * szTag)
{
  messagetrans_lookup ((messagetrans_control_block*)gpcMessages,
    szTag, gpcTemp, 256, 0, 0, 0, 0, NULL);

  return gpcTemp;
}

//////////////////////////////////////////////////////////////////
// Obtain string from Messages file if it exists
char * TagCheck (char * szTag)
{
  osbool                      boMore = FALSE;
  int                         nUsed = 0;
  char                        *szResult;
  os_error                    *psError;

  xmessagetrans_enumerate_tokens ((messagetrans_control_block*)gpcMessages,
    szTag, gpcTemp, 256, 0, & boMore, & nUsed, NULL);

  if (!boMore)
  {
    strcpy (gpcTemp, "");
    szResult = gpcTemp;
  }
  else
  {
    psError = xmessagetrans_lookup ((messagetrans_control_block*)gpcMessages,
      szTag, gpcTemp, 256, NULL, NULL, NULL, NULL, & szResult, & nUsed);

    if (psError)
    {
      strcpy (gpcTemp, "");
      szResult = gpcTemp;
    }
  }

  return szResult;
}

//////////////////////////////////////////////////////////////////
// Load a file into a block of memory
char * LoadFile (char * szFilename, int * pnSize)
{
  char                        *pcMemory;
  int                         nSize;
  os_error                    *psError;
  fileswitch_object_type      eObjectType;

  xosfile_read_stamped_no_path (szFilename, & eObjectType, NULL, NULL,
    & nSize, NULL, NULL);

  if ((eObjectType != fileswitch_NOT_FOUND) && (nSize > 0))
  {
    if (pnSize)
    {
      *pnSize = nSize;
    }
    pcMemory = malloc (nSize);

    if (pcMemory)
    {
      psError = xosfile_load_stamped_no_path (szFilename, pcMemory, NULL,
        NULL, NULL, NULL, NULL);

      if (psError)
      {
        free (pcMemory);
        pcMemory = NULL;
        err (psError);
      }
    }
    else
    {
      ShowWarningTag ("Er3");
    }
  }
  else
  {
    pcMemory = NULL;
  }

  return pcMemory;
}

//////////////////////////////////////////////////////////////////
// Load a file into a block of flex memory
int LoadFileFlex (char * szFilename, char * * ppcMemory, int * pnSize)
{
  int                         nSize;
  int                         nSuccess = 0;
  fileswitch_object_type      eObjectType;
  os_error                    *psError;

  xosfile_read_stamped_no_path (szFilename, & eObjectType, NULL, NULL,
    & nSize, NULL, NULL);

  if ((eObjectType != fileswitch_NOT_FOUND) && (nSize > 0))
  {
    if (pnSize)
    {
      *pnSize = nSize;
    }
    nSuccess = flex_alloc ((flex_ptr) ppcMemory, nSize);

    if ((nSuccess == 1) && (*ppcMemory))
    {
      psError = xosfile_load_stamped_no_path (szFilename, * ppcMemory, NULL,
        NULL, NULL, NULL, NULL);

      if (psError)
      {
        flex_free ((flex_ptr) ppcMemory);
        *ppcMemory = NULL;
        nSuccess = -1;
        err (psError);
      }
    }
    else
    {
      ShowWarningTag ("Er3");
      nSuccess = 0;
      *ppcMemory = NULL;
    }
  }
  else
  {
    nSuccess = -1;
    *ppcMemory = NULL;
  }

  return nSuccess;
}

//////////////////////////////////////////////////////////////////
// Load a sprite file into a block of memory
osspriteop_area * LoadSprites (char * szFilename)
{
  osspriteop_area             *pcMemory;
  int                         nSize;

  xosfile_read_stamped_no_path (szFilename, NULL, NULL, NULL, & nSize,
    NULL, NULL);

  if (nSize > 0)
  {
    pcMemory = (osspriteop_area*)malloc (nSize);

    if (pcMemory)
    {
      pcMemory->size = nSize;
      pcMemory->sprite_count = 0;
      pcMemory->first = 16;
      pcMemory->used = 16;
      xosspriteop_clear_sprites (osspriteop_USER_AREA, pcMemory);
      err (xosspriteop_load_sprite_file (osspriteop_USER_AREA, pcMemory,
        szFilename));
    }
  }
  else
  {
    pcMemory = NULL;
  }

  return pcMemory;
}

//////////////////////////////////////////////////////////////////
// Display an error from the Messages file in a warning box
void ShowWarningTag (char * szTag)
{
    xos_bell  ();
    REPORT (Tag(szTag));
    SetIconText (Tag(szTag), gwhWarn, 1);
    OpenWindowInitCentre (gwhWarn);
    CloseMenu ();

    xwimp_set_caret_position (gwhWarn, wimp_ICON_WINDOW, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Display an error in a warning box
void ShowWarning (os_error * sError)
{
    xos_bell  ();
    REPORT (sError->errmess);
    SetIconText (sError->errmess, gwhWarn, 1);
    OpenWindowInitCentre (gwhWarn);
    CloseMenu ();

    xwimp_set_caret_position (gwhWarn, wimp_ICON_WINDOW, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Close the warning message box
void CloseWarning (void)
{
  CloseWindowHandle (gwhWarn);
}

//////////////////////////////////////////////////////////////////
// Initiate a sprite drag from the contents of the given icon
void DragSprite (wimp_w whWindow, wimp_i ihIcon, char * szIconName)
{
  osspriteop_area             *pcSpriteArea;
  int                         nXinc;
  int                         nYinc;
  wimp_icon_state             sIconState;
  wimp_window_info            sWindowInfo;

  if (!gboDrag)
  {
    sIconState.w = whWindow;
    sIconState.i = ihIcon;
    xwimp_get_icon_state (& sIconState);

    if (((sIconState.icon.flags & (wimp_ICON_TEXT
      | wimp_ICON_SPRITE | wimp_ICON_INDIRECTED))
      == (wimp_ICON_SPRITE | wimp_ICON_INDIRECTED)))
    {
      pcSpriteArea = sIconState.icon.data.indirected_sprite.area;
    }
    else
    {
      pcSpriteArea = NULL;
    }

    sWindowInfo.w = whWindow;
    xwimp_get_window_info_header_only (& sWindowInfo);

    if (!pcSpriteArea)
    {
      pcSpriteArea = sWindowInfo.sprite_area;
    }

    nXinc = sWindowInfo.visible.x0 - sWindowInfo.xscroll;
    nYinc = sWindowInfo.visible.y1 - sWindowInfo.yscroll;

    sIconState.icon.extent.x0 += nXinc;
    sIconState.icon.extent.y0 += nYinc;
    sIconState.icon.extent.x1 += nXinc;
    sIconState.icon.extent.y1 += nYinc;

    xdragasprite_start ((dragasprite_HPOS_CENTRE | dragasprite_VPOS_CENTRE
      | dragasprite_BOUND_POINTER | dragasprite_DROP_SHADOW),
      pcSpriteArea, szIconName, & sIconState.icon.extent, NULL);

    gboDrag = TRUE;
  }
}

//////////////////////////////////////////////////////////////////
// Initiate a outline drag from the dimensions of the given icon
void DragBox (wimp_w whWindow, wimp_i ihIcon)
{
  int                         nXinc;
  int                         nYinc;
  wimp_icon_state             sIconState;
  wimp_window_state           sWindowState;
  wimp_drag                   sDragInfo;

  if (!gboDrag)
  {
    sIconState.w = whWindow;
    sIconState.i = ihIcon;
    xwimp_get_icon_state (& sIconState);

    sWindowState.w = whWindow;
    xwimp_get_window_state (& sWindowState);

    nXinc = sWindowState.visible.x0 - sWindowState.xscroll;
    nYinc = sWindowState.visible.y1 - sWindowState.yscroll;

    sDragInfo.w = 0;
    sDragInfo.type = wimp_DRAG_USER_FIXED;
    sDragInfo.initial.x0 = sIconState.icon.extent.x0 + nXinc;
    sDragInfo.initial.y0 = sIconState.icon.extent.y0 + nYinc;
    sDragInfo.initial.x1 = sIconState.icon.extent.x1 + nXinc;
    sDragInfo.initial.y1 = sIconState.icon.extent.y1 + nYinc;
    sDragInfo.bbox.x0 = 0;
    sDragInfo.bbox.y0 = 0;
    sDragInfo.bbox.x1 = 0;
    sDragInfo.bbox.y1 = 0;
    sDragInfo.handle = NULL;
    sDragInfo.draw = NULL;
    sDragInfo.undraw = NULL;
    sDragInfo.redraw = NULL;

    xwimp_drag_box (& sDragInfo);

    gboDrag = TRUE;
  }
}

//////////////////////////////////////////////////////////////////
// Initiate a URL link
void OpenURL (char * szURL)
{
  wimp_message                sMessage;

  sMessage.your_ref = 0;
  sMessage.action = 0x4af80;

  strcpy ((char *)sMessage.data.reserved, szURL);

  sMessage.size = WORDALIGN(strlen(szURL) + 21);

  xwimp_send_message (wimp_USER_MESSAGE_RECORDED, & sMessage,
    wimp_BROADCAST);

  gnMessageMyRef = sMessage.my_ref;
}

//////////////////////////////////////////////////////////////////
// No URL lancher responded, so we have to run one ourselves
void OpenURLReturned (wimp_block *pcBlock)
{
  char                        szAlias[255];
  char                        szVar[255];
  int                         nUsed;

  if (gnMessageMyRef == pcBlock->message.my_ref)
  {
    if (strncmp (pcBlock->message.data.reserved, "mailto:", 7) == 0)
    {
      strcpy (szAlias, "URLOpen_MailTo");
    }
    else
    {
      strcpy (szAlias, "URLOpen_HTTP");
    }

    sprintf (szVar, "Alias$%s", szAlias);
    xos_read_var_val (szVar, 0, -1, 0, os_VARTYPE_STRING, & nUsed,
      NULL, NULL);

    if (nUsed)
    {
      sprintf (szVar, "%s %s", szAlias, pcBlock->message.data.reserved);
      err (xwimp_start_task (szVar, NULL));
    }
  }
}

#if defined _DEBUG
//////////////////////////////////////////////////////////////////
// Report a debug message
void Report (char * szMessage)
{
  _kernel_swi_regs            sRegs;

  sRegs.r[0] = (int)szMessage;
  _kernel_swi (Report_Text0, & sRegs, & sRegs);
}

//////////////////////////////////////////////////////////////////
// Report a debug message with variable
void ReportVar (char * szFormat, int nVariable)
{
  _kernel_swi_regs            sRegs;
  char                        szReport[255];

  sprintf (szReport, szFormat, nVariable);
  sRegs.r[0] = (int)szReport;
  _kernel_swi (Report_Text0, & sRegs, & sRegs);
}
#endif // _DEBUG

//////////////////////////////////////////////////////////////////
// Set up the configuration window
void ConfigSetUpWindow (void)
{
  char                        szText[1024];

  sprintf (szText, "%d", gsNetLoop.nPort);
  SetIconText (szText, gwhConf, 3);

  sprintf (szText, "%d", gsNetLoopClient.nPort);
  SetIconText (szText, gwhConf, 5);
}

//////////////////////////////////////////////////////////////////
// Set configuration from the configuration window
void ConfigSetFromWindow (void)
{
  sscanf (GetIconText (gwhConf, 3), "%d", & gsNetLoop.nPort);

  sscanf (GetIconText (gwhConf, 5), "%d", & gsNetLoopClient.nPort);
}

//////////////////////////////////////////////////////////////////
// Poll 0: Null Poll
void NullPoll (wimp_block *pcBlock)
{
  pcBlock = pcBlock;

  NetworkLoop (& gsNetLoop);
  NetworkLoop (& gsNetLoopClient);
}

//////////////////////////////////////////////////////////////////
// Main network program
void NetworkLoop (NetLoop * psNetLoop)
{
  SOCK                        eSockError;

  switch (psNetLoop->eState)
  {
    case STATE_DISCONNECTED:
      break;
    case STATE_CREATESOCKET:
      eSockError = CreateSocket (& psNetLoop->sSocket, psNetLoop);
      switch (eSockError)
      {
        case SOCK_FAIL:
          Cancel (& psNetLoop->eState);
          psNetLoop->eState = STATE_CANCEL;
          break;
        case SOCK_ERR:
          Cancel (& psNetLoop->eState);
          psNetLoop->eState = STATE_CANCEL;
          break;
        default:
          psNetLoop->eState = STATE_RESOLVING;
          break;
      }
      break;
    case STATE_RESOLVING:
      psNetLoop->eState = Resolve (psNetLoop->szAddress,
        & psNetLoop->nAddress);
      break;
    case STATE_CONNECT:
//      *peState = Connect (gnAddress, & gsSocket, gnPort);
      psNetLoop->eState = STATE_LISTEN;
      break;
    case STATE_LISTEN:
      psNetLoop->eState = Listen (& psNetLoop->sSocket);
      break;
    case STATE_CONNECTING:
      psNetLoop->eState = CheckConnected (psNetLoop->sSocket);
      break;
    case STATE_ACCEPT:
      psNetLoop->eState = Accept (& psNetLoop->sSocket, psNetLoop);
      break;
    case STATE_CONNECTED:
      psNetLoop->eState = Connected (& psNetLoop->sSocket, psNetLoop);
      SendDataUpdate (psNetLoop);
      break;
    case STATE_CANCEL:
      psNetLoop->eState = ImmediateUpdate (& psNetLoop->sSocket);
      break;
    case STATE_FINISH:
      // Close socket
      err (xsocket_shutdown (psNetLoop->sSocket, socket_SHUTDOWN_SEND));
      psNetLoop->eState = STATE_FINISHREAD;
      break;
    case STATE_FINISHREAD:
      psNetLoop->eState = ConnectedEnd (& psNetLoop->sSocket, psNetLoop);
      break;
    case STATE_FINISHED:
      err (xsocket_close (psNetLoop->sSocket));
      SetIconText ("", gwhStat, psNetLoop->nIcon);
      psNetLoop->eState = STATE_FINISHPOST;
      break;
    case STATE_FINISHPOST:
      psNetLoop->eState = STATE_DISCONNECTED;
      break;
    default:
      break;
  }

  if (psNetLoop->eState != psNetLoop->ePrevState)
  {
    PrintState (psNetLoop->eState, psNetLoop);
    psNetLoop->ePrevState = psNetLoop->eState;
  }
}

//////////////////////////////////////////////////////////////////
// Create Socket
SOCK CreateSocket (socket_s * psSocket, NetLoop * psNetLoop)
{
  socket_sockaddr            sAddress;
  char                       cArg;
  os_error                   *psError;
  SOCK                       eReturn;
  unsigned int               auArg[2];

  eReturn = SOCK_OK;

  if (psSocket)
  {
    psError = xsocket_creat (socket_AF_INET, socket_SOCK_STREAM,
      socket_IPPROTO_TCP, psSocket);
//      socket_IPPROTO_IP, psSocket);

    if (psError)
    {
      err (psError);
      eReturn = SOCK_FAIL;
    }
    else
    {
      memset (& sAddress, 0, sizeof (sAddress));
      sAddress.sockaddr.af = socket_AF_INET;
      sAddress.sockaddr_in.af = socket_AF_INET;
      sAddress.sockaddr_in.port = htons (psNetLoop->nPort);
      sAddress.sockaddr_in.addr = htonl (0);

      psError = xsocket_bind (*psSocket, & sAddress, 16);

      if (psError)
      {
        err (psError);
        eReturn = SOCK_ERR;
      }
      else
      {
        cArg = 1;
        psError = xsocket_ioctl (*psSocket, socket_FIONBIO, & cArg);

        if (psError)
        {
          err (psError);
          eReturn = SOCK_ERR;
        }
        else
        {
          auArg[0] = 1;
          psError = xsocket_setsockopt (*psSocket, socket_SOL_SOCKET,
            socket_SO_REUSEADDR, (void*)(auArg), 1 * sizeof (unsigned int));

          if (psError)
          {
            err (psError);
            eReturn = SOCK_ERR;
          }

          auArg[0] = 0;
          auArg[1] = 1;
          psError = xsocket_setsockopt (*psSocket, socket_SOL_SOCKET,
            socket_SO_LINGER, (void*)(auArg), 2 * sizeof (unsigned int));

          if (psError)
          {
            err (psError);
            eReturn = SOCK_ERR;
          }
        }
      }
    }
  }
  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Connect to the mail server
STATE Connect (int nAddress, socket_s * psSocket, int nPort)
{
  socket_sockaddr            sAddress;
  os_error                   *psError;
  STATE                      eNextState;

  memset (& sAddress, 0, sizeof (sAddress));
  sAddress.sockaddr.af = socket_AF_INET;
  sAddress.sockaddr_in.af = socket_AF_INET;

  sAddress.sockaddr_in.port = htons (nPort);
//  ((nPort & 0xff) << 8)
//    + ((nPort & 0xff00) >> 8);

  sAddress.sockaddr_in.addr = nAddress;

  psError = xsocket_connect (* psSocket, & sAddress, sizeof (sAddress));

  eNextState = STATE_CONNECTING;
  if (psError)
  {
    if ((psError->errnum == socket_EISCONN)
      || (psError->errnum == (socket_EISCONN + IYONIX_SOCKET_ERR)))
    {
      eNextState = STATE_CONNECTED;
    }
    else
    {
      if ((psError->errnum != socket_EWOULDBLOCK)
        && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
        && (psError->errnum != socket_EALREADY)
        && (psError->errnum != (socket_EALREADY + IYONIX_SOCKET_ERR))
        && (psError->errnum != socket_EINPROGRESS)
        && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
      {
        REPORTVAR ("Error %d", psError->errnum);
        err (psError);
        Cancel (NULL);
//        eNextState = STATE_FINISH;
        eNextState = STATE_CANCEL;
      }
    }
  }
  else
  {
    eNextState = STATE_CONNECTED;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Connect to the mail server
STATE CheckConnected (socket_s sSocket)
{
  os_error                   *psError;
  STATE                      eNextState;
  socket_fdset               sRead;
//  socket_fdset               sWrite;
//  socket_fdset               sExcept;
  int                        nFound;
  socket_timeval             sTimeVal;

  FD_ZERO (& sRead);
//  FD_ZERO (& sWrite);
//  FD_ZERO (& sExcept);
//  memset (& sRead, 0, sizeof (sRead));
//  memset (& sWrite, 0, sizeof (sWrite));
//  memset (& sExcept, 0, sizeof (sExcept));

  FD_SET ((unsigned int)(sSocket), (fd_set*)(& sRead));
//  FD_SET ((unsigned int)(sSocket), (fd_set*)(& sWrite));
//  FD_SET ((unsigned int)(sSocket), (fd_set*)(& sExcept));
//  sRead |= (1 << (unsigned int)sSocket);
//  sWrite |= (1 << (unsigned int)sSocket);
//  sExcept |= (1 << (unsigned int)sSocket);

  sTimeVal.sec = 0;
  sTimeVal.usec = 0;

//  psError = xsocket_select (((unsigned int)sSocket) + 1, & sRead, & sWrite,
//    & sExcept, & sTimeVal, & nFound);

  psError = xsocket_select (((unsigned int)sSocket) + 1, & sRead, NULL,
    NULL, & sTimeVal, & nFound);

  eNextState = STATE_CONNECTING;
  if (psError)
  {
    REPORTVAR ("Error %d", psError->errnum);
    err (psError);
    Cancel (NULL);
    eNextState = STATE_CANCEL;
  }
  else
  {
#if defined _DEBUG
    if (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sRead)))
    {
      REPORT ("Found on Read");
    }
//    if (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sWrite)))
//    {
//      REPORT ("Found on Write");
//    }
//    if (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sExcept)))
//    {
//      REPORT ("Found on Except");
//      eNextState = STATE_FINISH;
//    }
#endif

    if (nFound > 0)
    {
//      if ((FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sRead)))
//        || (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sWrite))))
      if ((FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sRead))))
      {
        REPORTVAR ("Found %d", nFound);
        eNextState = STATE_ACCEPT;
      }
    }
  }

  return eNextState;
}


//////////////////////////////////////////////////////////////////
// Print the given state
void PrintState (STATE eState, NetLoop * psNetLoop)
{
  switch (eState)
  {
    case STATE_DISCONNECTED:
      REPORT ("Disconnected");
      SetIconText (Tag ("St1"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_CREATESOCKET:
      REPORT ("Creating socket");
      SetIconText (Tag ("St2"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_RESOLVING:
      REPORT ("Resolving");
      SetIconText (Tag ("St3"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_CONNECT:
      REPORT ("Connect");
      SetIconText (Tag ("St4"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_LISTEN:
      REPORT ("Listen");
      SetIconText (Tag ("St5"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_CONNECTING:
      REPORT ("Connecting");
      SetIconText (Tag ("St6"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_ACCEPT:
      REPORT ("Accept");
      SetIconText (Tag ("St7"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_CONNECTED:
      REPORT ("Connected");
      SetIconText (Tag ("St8"), gwhStat, (psNetLoop->nIcon + 1));
      break;

    case STATE_CANCEL:
      REPORT ("Cancelled");
      SetIconText (Tag ("St9"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_FINISH:
      REPORT ("Finishing");
      SetIconText (Tag ("St10"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_FINISHREAD:
      REPORT ("Finish reading");
      SetIconText (Tag ("St10"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_FINISHED:
      REPORT ("Finished");
      SetIconText (Tag ("St10"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    case STATE_FINISHPOST:
      REPORT ("Finish pause");
      SetIconText (Tag ("St11"), gwhStat, (psNetLoop->nIcon + 1));
      break;
    default:
      REPORT ("Invalid");
      SetIconText (Tag ("St0"), gwhStat, (psNetLoop->nIcon + 1));
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Resolve a host name
STATE Resolve (char * szHostName, int * pnAddress)
{
  _kernel_swi_regs           sRegs;
  STATE                      eNextState;
  os_error                   sError;

  sRegs.r[0] = (int)szHostName;
  _kernel_swi (Resolver_GetHost, & sRegs, & sRegs);

  if (sRegs.r[0] == 0)
  {
    REPORT ("HOST: ");
    REPORT (szHostName);
    eNextState = STATE_CONNECT;
    if (pnAddress)
    {
      *pnAddress = ***(int***)((char*)(sRegs.r[1] + 16));
    }
  }
  else
  {
    if (((int)(sRegs.r[0])) == 36)
    {
      eNextState = STATE_RESOLVING;
    }
    else
    {
      REPORT ("HOST: ");
      REPORT (szHostName);
      sError.errnum = 0;
      strcpy (sError.errmess, "Server lookup failed");
      err (& sError);
      Cancel (NULL);
//      eNextState = STATE_FINISH;
      eNextState = STATE_CANCEL;
    }
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Read any incoming data from a socket
SOCK SocketRead (socket_s * psSocket, NetLoop * psNetLoop)
{
  int                        nRead;
  os_error                   *psError = NULL;
  int                        nBufferSize;
  SOCK                       eReturn = SOCK_WAIT;

  nBufferSize = BUFFERSIZE - psNetLoop->nReceived;
  psError = xsocket_read (*psSocket, psNetLoop->pcBuffer
    + psNetLoop->nReceived, nBufferSize,
    & nRead);

  if (!psError)
  {
    // Do stuff with the data
    if (nRead == 0)
    {
      eReturn = SOCK_CLOSED;
    }
    else
    {
      psNetLoop->nReceived += nRead;
      eReturn = SOCK_RECEIVE;
      gnReceived += nRead;
    }
  }

  if (psError)
  {
    if ((psError->errnum != socket_EWOULDBLOCK)
      && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
      && (psError->errnum != socket_EINPROGRESS)
      && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
    {
      REPORTVAR ("Read Error %d", psError->errnum);
      err (psError);
      eReturn = SOCK_FAIL;
    }
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Continue sending a message if there is one
SOCK SendDataUpdate (NetLoop * psNetLoop)
{
  int                        nCc;
  os_error                   *psError;
  SOCK                       eReturn;
  int                        nShift;

  eReturn = SOCK_OK;

  if ((psNetLoop->nReceived > 0) && (psNetLoop->psSendNetLoop))
  {
    // Continue sending the command
    nCc = 0;
    psError = xsocket_send (psNetLoop->psSendNetLoop->sSocket,
      psNetLoop->pcBuffer, psNetLoop->nReceived, 0, & nCc);

#if defined _DEBUG
    if ((psNetLoop->nReceived - nCc) != 0)
    {
      REPORTVAR ("Shifting %d", (psNetLoop->nReceived - nCc));
    }
#endif
    for (nShift = nCc; nShift < psNetLoop->nReceived; nShift++)
    {
      (psNetLoop->pcBuffer)[nShift - nCc] = (psNetLoop->pcBuffer)[nShift];
    }
    psNetLoop->nReceived -= nCc;

    gnSent += nCc;

    if (psNetLoop->nReceived > 0)
    {
      eReturn = SOCK_WAIT;
    }

    if (psError)
    {
      if ((psError->errnum != socket_EWOULDBLOCK)
        && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
        && (psError->errnum != socket_EINPROGRESS)
        && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
      {
        REPORTVAR ("Update error number %d", psError->errnum);
        err (psError);
        psNetLoop->nReceived = 0;
        eReturn = SOCK_FAIL;
      }
    }
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Cancels operation
void Cancel (STATE * peState)
{
  if (peState)
  {
    *peState = STATE_CANCEL;
  }
}

//////////////////////////////////////////////////////////////////
// Handles update state for an error or cancel
STATE ImmediateUpdate (socket_s * psSocket)
{
  STATE                      eNextState = STATE_FINISH;

  psSocket = psSocket;

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Read data and deal with it
STATE Connected (socket_s * psSocket, NetLoop * psNetLoop)
{
  STATE                      eNextState = STATE_CONNECTED;
  SOCK                       eReturn;
  eReturn = SocketRead (psSocket, psNetLoop);

  if (eReturn == SOCK_FAIL)
  {
    eNextState = STATE_CANCEL;
  }

  if (eReturn == SOCK_CLOSED)
  {
    eNextState = STATE_FINISH;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Read data and deal with it
STATE ConnectedEnd (socket_s * psSocket, NetLoop * psNetLoop)
{
  STATE                      eNextState = STATE_FINISHREAD;
  SOCK                       eReturn;
  eReturn = SocketRead (psSocket, psNetLoop);

  if (eReturn == SOCK_FAIL)
  {
    eNextState = STATE_FINISHED;
  }

  if (eReturn == SOCK_CLOSED)
  {
    eNextState = STATE_FINISHED;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Set the socket to listen
STATE Listen (socket_s * psSocket)
{
  STATE                      eNextState = STATE_LISTEN;
  os_error                   *psError;

  psError = xsocket_listen (*psSocket, LISTEN_BACKLOG);

  if (psError)
  {
    eNextState = STATE_FINISH;
  }
  else
  {
    eNextState = STATE_CONNECTING;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Accept an incoming connection
STATE Accept (socket_s * psSocket, NetLoop * psNetLoop)
{
  STATE                      eNextState = STATE_ACCEPT;
  os_error                   *psError;
  socket_s                   sNewSocket;
  int                        nAddrLen;
  socket_sockaddr            sClientAddr;

  memset (& sClientAddr, 0, sizeof (sClientAddr));
  nAddrLen = sizeof (sClientAddr);

  psError = xsocket_accept (*psSocket, & sClientAddr, & nAddrLen,
    & sNewSocket);

  if (psError)
  {
    if ((psError->errnum != socket_EWOULDBLOCK)
      && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
      && (psError->errnum != socket_EINPROGRESS)
      && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
    {
      REPORTVAR ("Update error number %d", psError->errnum);
      err (psError);
      eNextState = STATE_FINISH;
    }
  }
  else
  {
    // We're connected!
    ResetStatus (psNetLoop);

    SetIconText (inet_ntoa(*(struct in_addr*)
      (&sClientAddr.sockaddr_in.addr)), gwhStat, psNetLoop->nIcon);
    eNextState = STATE_CONNECTED;
    // Close the listening connection
    err (xsocket_shutdown (psNetLoop->sSocket, socket_SHUTDOWN_SEND));
    err (xsocket_close (psNetLoop->sSocket));
    psNetLoop->sSocket = sNewSocket;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Reset the status window
void ResetStatus (NetLoop * psNetLoop)
{
  wimp_window_state           sState;

  psNetLoop->nReceived = 0;
  gnSent = 0;
  gnReceived = 0;
  SetIconText (Tag ("St0"), gwhStat, (psNetLoop->nIcon + 1));
  SetIconText ("", gwhStat, psNetLoop->nIcon);

  sState.w = gwhStat;
  xwimp_get_window_state (& sState);
  xwimp_open_window ((wimp_open *)& sState);
}
