/*
 * POPstar: Acorn POP3 client / SMTP sender
 * Wimp Interface Code
 *
 *  Joseph Heenan, 1996-8
 *  Darren Salt, 2003-4
 *
 * $Id: popstar,v 1.53 2003/02/02 11:23:27 joseph Exp $
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "memleak.h"

#ifdef MemCheck_MEMCHECK
#include "MemCheck:MemCheck.h"
#endif

#include "kernel.h"
#include "swis.h"

#include "wimplib.h"


#include "defines.h"


/*#include "choices.h"*/
#include "config.h"
#include "err.h"
#include "log.h"
#include "msgs.h"
#include "pop.h"
#include "smtpsend.h"
#include "status.h"
#include "stopquit.h"
#include "user.h"
#include "treport.h"
#include "riscnet.h"
#include "voyager.h"
#include "oversize.h"
#include "sockwatch.h"
#include "ftrunc.h"
#include "defines.h"

#include "macros.h"

#include "wimpclib.h"

#include "popstar.h"

const char frontend_taskname[] = TASK_NAME;
int frontend_debug = 0; /* extra debugging */

static const int wimp_messages [] = {
       Wimp_MPreQuit,
       Wimp_MOpenUrl,
       Wimp_MMenusDeleted,
       URI_MReturnResult,
       Riscnet_Offline,
       Riscnet_Mail_RequestFetch,
       Riscnet_Mail_SendNew,
       /* Wimp_MQuit is implicit */
       0};

static unsigned int pollmask = 0;

static int frontend_infowin = -1;
static int frontend_oversizewin = -1;
static int frontend_iconbar = -1;
static WimpMenu *frontend_mainmenu = NULL;
static enum frontend_menus { M_NONE, M_MAIN, M_INFO } frontend_openmenu = M_NONE;
static int frontend_menuopening = 0; /* set to true when we've opened a menu whilst another one of our menus is open */
static int frontend_noicon = 0; /* default to icon */
static int frontend_quit   = 0; /* default to not quitting */


/* verbatim from main.c */

/* busy is true if a fetch/send is in progress, indicating that we
 * should ask the user to confirm a quit or shutdown request
 */
static bool busy = false;

static unsigned int send_period, fetch_period, checkvar_period;
static bool send_pending = false;
static bool fetch_pending = false;
static unsigned int next_fetch, next_send, next_idle;
static unsigned int last_poll;
static bool last_online_state = false;
/* end of code from main.c */

#define MAINMENU ">Info,Fetch,>Fetch and send,Send,Show status...,Reload config,Show Log," \
                 "Help...,Quit", \
                 frontend_infowin, (void *) -1

#define MAINMENU_INFO         0
#define MAINMENU_FETCH        1
#define MAINMENU_FETCHSEND    2
#define MAINMENU_SEND         3
#define MAINMENU_STATUS       4
#define MAINMENU_RELOADCONF   5
#define MAINMENU_SHOWLOG      6
#define MAINMENU_HELP         7
#define MAINMENU_QUIT         8

#define INFOWIN_AUTHOR        2
#define INFOWIN_WEBSITE       8
#define INFOWIN_VERSION       3

#define ERRORWIN_CANCEL       5
#define ERRORWIN_CONTINUE     1
#define ERRORWIN_MSG          0



static int popstar_keyalt(void)
{
  int state;
  if ( _swix( OS_Byte, _INR(0,2) | _OUT(1), 202, 0, 255, &state ) ) return 0;
  return state & 1;
}



static int frontend_createmenu( enum frontend_menus which, WimpMenu *menu, int x, int y )
{
  if ( frontend_openmenu != M_NONE && frontend_openmenu != which )
    frontend_menuopening = 1; /* if we're opening a menu whilst we have another open */

  E_CHECK_RETURN( -1, wimp_create_menu( menu, x, y ) );

  frontend_openmenu = which;
  return 0;
}

static void main_make_busy(bool chicon)
{
  if ( chicon )
  {
    if ( frontend_iconbar != -1 )
      wimpc_seticontext( -1, frontend_iconbar, "&popstar" );
    busy = true;
  }
  pollmask = pollmask & ~Wimp_Poll_NullMask; /* take null polls */
  /* fade main menu : 'Fetch', 'FetchThenSend', 'Send', 'Reload config' */
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_FETCH, 1 );
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_FETCHSEND, 1 );
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_SEND, 1 );
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_RELOADCONF, 1 );

   if ( frontend_openmenu == M_MAIN )
     frontend_createmenu( M_MAIN, frontend_mainmenu, 0, 0 ); /* refresh menu if open */
}


static void main_make_idle(bool chicon)
{
  if ( chicon )
  {
    if ( frontend_iconbar != -1 )
      wimpc_seticontext( -1, frontend_iconbar, "!popstar" );
    busy = false;
  }
  pollmask = pollmask | Wimp_Poll_NullMask; /* don't take null polls */
  /* unfade main menu : 'Fetch', 'FetchThenSend', 'Send', 'Reload config' */
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_FETCH, 0 );
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_FETCHSEND, 0 );
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_SEND, 0 );
  wimpc_menufadeitem( frontend_mainmenu, MAINMENU_RELOADCONF, 0 );

   if ( frontend_openmenu == M_MAIN )
     frontend_createmenu( M_MAIN, frontend_mainmenu, 0, 0 ); /* refresh menu if open */
}


int frontend_usersmenu( WimpMenu *menu )
{
  return wimpc_menuupdate( frontend_mainmenu, MAINMENU_FETCH, menu );
}


static bool check_env_int( const char *env )
{
  if (!env)
    return true;

  if ((env = getenv(env)) == 0)
    return false;

  switch (env[0])
  {
    case '0':
      return false;
    case 'N':
    case 'n':
      switch (env[1])
      {
        case 'o':
        case 'O':
          if (!env[2])
            return false;
          else
            return true;
        case 0:
          return false;
        default:
          return true;
      }
    default:
      return true;
  }

  return true;
}

static bool check_env( void )
{
  static bool last_state = false;
  bool state = check_env_int( config_lookup("Variable") );

  if ( last_state == true && state == false && config_lookup_bool("QuitWhenGoOffline:N") )
    exit( 0 );

  last_state = state;

  return state;
}


static void reschedule()
{
  unsigned int time_now;

  _swix(OS_ReadMonotonicTime, _OUT(0), &time_now);
  if (fetch_period && !fetch_pending)
  {
    next_fetch = time_now + fetch_period;
    fetch_pending = true;
    if ( !send_pending || next_fetch - last_poll < next_send - last_poll )
      /* no send so the next thingy must be a fetch, or send pending, but fetch is first */
      next_idle = next_fetch;
    else
      next_idle = next_send; /* send pending and is first */
  }
  if (send_period && !send_pending)
  {
    next_send = time_now + send_period;
    send_pending = true;
    if (!fetch_pending || next_send - last_poll < next_fetch - last_poll)
      next_idle = next_send; /* no fetch pending, or pending send is sooner */
    else
      next_idle = next_fetch; /* must be fetch pending which is sooner */
  }

  if (send_pending || fetch_pending)
  {
    unsigned int next_varcheck = time_now + checkvar_period;

    /* set next_idle to next fetch / send */
    if ( !send_pending || next_fetch - last_poll < next_send - last_poll )
      /* no send so the next thingy must be a fetch, or send pending, but fetch is first */
      next_idle = next_fetch;
    else
      next_idle = next_send; /* send pending and is first */

    /* set next_idle to next_varcheck, if it's sooner */
    if ( next_idle - next_varcheck < 0x80000000 ) /* looking for next_idle > next_varcheck */
      next_idle = next_varcheck;
    pollmask = pollmask & ~Wimp_Poll_NullMask; /* take null polls if fetch pending */
  }
  else
    pollmask = pollmask | Wimp_Poll_NullMask;
}



/* runs the fetchend action, if there is one.
 * The two parameters are the number of messages fetched and sent, or
 * -1 if a fetch or send did not occur this time.
 */

static void popstar_transferend(int fetched, int sent)
{
  const char *cmd;
  int handle;
  char command[MAX_PATH_LEN];

  /* Do 'TransferEnd', for backwards compatibility */
  cmd = config_lookup("TransferEnd");
  if (cmd != NULL && *cmd != '\0')
  {
    E_CHECK(wimp_start_task((char *) cmd, &handle));
  }
  
  /* Now do the FetchEnd and SendEnd */
  cmd = config_lookup("FetchEnd");
  if (fetched != -1 && cmd != NULL && *cmd != '\0')
  {
    snprintf(command, sizeof (command), "%s %d", cmd, fetched);
    E_CHECK(wimp_start_task(command, &handle));
  }

  cmd = config_lookup("SendEnd");
  if (sent != -1 && cmd != NULL && *cmd != '\0')
  {
    snprintf(command, sizeof (command), "%s %d", cmd, sent);
    E_CHECK(wimp_start_task(command, &handle));
  }
}


/* fetch mail for all users, send all mail */

static void fetch_all_users(int *fetched)
{
  int count, total = 0;
  int c;
  const user_info *user;
  
  for (c = 0; user = user_get(c), user; ++c)
    if ( ( user->autofetch == a_always ) ||
         ( user->autofetch == a_online && check_env() ) ||
         ( user->autofetch == a_var && check_env_int( user->var ) ) )
    {
      pop_fetch( c, &count );
      total += count;
      if ( stopquit_pending )
        break;
    }

  riscnet_mailfinish( total /* number of messages downloaded */ );
  voyager_newmail( total );
  *fetched = total;
}



static void popstar_fetchsendhandler(void)
{
  int fetched;
  int sent;

  if ( busy ) return;

  main_make_busy(true);
  if (config_lookup_bool("SendFirst:Y"))
    smtp_send(&sent);
  fetch_all_users(&fetched);
  if (!config_lookup_bool("SendFirst:Y"))
    smtp_send(&sent);
  stopquit_hide();
  main_make_idle(true);
  fetch_pending = false;
  send_pending = false;
  reschedule();

  popstar_transferend(fetched, sent);
  /* update online state to prevent another fetch if the state changed during the fetch */
  last_online_state = check_env();
}

void popstar_sendhandler( void )
{
  int sent;

  if ( busy ) return;

  main_make_busy(true);
  smtp_send(&sent);
  stopquit_hide();
  main_make_idle(true);
  send_pending = false;
  reschedule();
  popstar_transferend(-1, sent);
  /* update online state to prevent another fetch if the state changed during the fetch */
  last_online_state = check_env();
}

void popstar_fetchallhandler( void )
{
  int fetched;
  
  if ( busy ) return;

  main_make_busy(true);
  fetch_all_users(&fetched);
  stopquit_hide();
  main_make_idle(true);
  fetch_pending = false;
  reschedule();

  popstar_transferend(fetched, -1);
  /* update online state to prevent another fetch if the state changed during the fetch */
  last_online_state = check_env();
}


static void frontend_mouseclick( int window, int icon, int but, int x, int y )
{
  int handled = 1;

  y=y;

  if ( window == -2 )
  {
    /* iconbar */
    switch ( but )
    {
      case Wimp_MouseButtonSelect:
        status_openmanual();
        break;
      case Wimp_MouseButtonMenu:
        frontend_createmenu( M_MAIN, frontend_mainmenu, x-64, wimpc_menuheight(frontend_mainmenu) + 96 );
        break;
      case Wimp_MouseButtonAdjust:
        popstar_fetchsendhandler();
        break;
    }
  }
  else if ( window == frontend_infowin )
  {
    switch ( icon )
    {
      case INFOWIN_AUTHOR:
        url_launch( "mailto:ds@youmustbejoking.demon.co.uk" );
        break;
      case INFOWIN_WEBSITE:
        if (but == 1)
          url_launch( "http://www.heenan.me.uk/acorn/popstar.html" );
        else
          url_launch( "http://www.youmustbejoking.demon.co.uk/progs.apps.html#popstar" );
        break;
    }
  }
  else if ( window == frontend_oversizewin )
    oversize_mouseclick( icon, but );
  else if ( status_click( window, icon, but ) )
  {
    if (but == Wimp_MouseButtonMenu)
    {
      frontend_createmenu(M_MAIN, frontend_mainmenu, x - 64, y);
    }
  }
  else if ( stopquit_click( window, icon, but ) ) {}
  else
  {
    handled = 0;
  }
  if ( handled ) return;

  treport_click( window, icon, but );

}

static void frontend_wimpmessage_ack( WimpMessage * mess )
{
  /* NB. Don't forget to add messages to wimp_messages[] too */
  if ( mess->hdr.action_code == Wimp_MOpenUrl )
    url_bounce( mess );
}




/* lifted verbatim from main.c */

static bool prequit_handler(WimpMessage *msg, void *h)
{
  h=h;

  if (busy)
  {
    stopquit_show(msg->data.words[0] ? stopquit_Quit : stopquit_Shutdown,
    	msg->hdr.sender);
    msg->hdr.your_ref = msg->hdr.my_ref;
    E(wimp_send_message(Wimp_EUserMessageAcknowledge, msg, msg->hdr.sender,
    	0, 0));
  }
  return true;
}


#ifdef SLSWIHACK
  extern _kernel_oserror *sl_swix (int swi_no, unsigned int, ...);

  #define OffsetToBranch 5u
  #define ARM_op_mask 0xff000000u
  #define ARM_B_op 0xea000000u
  #define ARM_BL_op 0xeb000000u
  #define ARM_pipeline 8u

  static void hack_stub(unsigned int stub_addr, unsigned int new_addr)
  {
    *((unsigned int *) stub_addr) = ARM_B_op |
      ((new_addr - (stub_addr + ARM_pipeline)) >> 2) & ~ARM_op_mask;
  }
#endif

/* end of verbatim lift from main.c */




static void frontend_menuselection(WimpMenuSelections sel, int adjust)
{
  if ( frontend_openmenu == M_MAIN )
  {
    if ( adjust )
      /* must do this first, as it may be 'sometime' before we get out of this code block */
      frontend_createmenu( M_MAIN, frontend_mainmenu, 0, 0 ); /* redisplay menu */
    else
      frontend_openmenu = M_NONE;

    switch ( sel[0] )
    {
      case MAINMENU_INFO:
        break;

      case MAINMENU_FETCH:
        if ( sel[1] == -1 )
        {
          /* root of user menu selected - fetch all users */
          popstar_fetchallhandler();
        }
        else
        {
          int fetched;
          main_make_busy(true);
          /* fetch mail for user selected */
          pop_fetch(sel[1], &fetched);
          popstar_transferend(fetched, -1);
          stopquit_hide();
          main_make_idle(true);
          fetch_pending = false;
          reschedule();
        }
        break;

      case MAINMENU_FETCHSEND:
        popstar_fetchsendhandler();
        break;

      case MAINMENU_SEND:
        popstar_sendhandler();
        break;

      case MAINMENU_STATUS:
        status_openmanual();
        break;

      case MAINMENU_RELOADCONF:
        config_reload();
        fetch_period = (unsigned int) (config_lookup_float("FetchEvery") * 6000.0f);
        send_period = (unsigned int) (config_lookup_float("SendEvery") * 6000.0f);
        /* seconds, def. 10 seconds */
        checkvar_period = (unsigned int) (config_lookup_float("CheckOnlineVar:10") * 100.0f);
        user_reload();
        break;

      case MAINMENU_SHOWLOG:
      {
        static const char show_log_cmd[] = "Filer_Run <SysLog$LogDir>."TASK_LOGNAME;
        _swix(OS_CLI, _IN(0), show_log_cmd);
      } break;

      case MAINMENU_HELP:
      {
        static const char show_help_cmd[] = "Filer_Run "TASK_DIR".!Help";
        _swix(OS_CLI, _IN(0), show_help_cmd);
      } break;

      case MAINMENU_QUIT:
        if (busy)
          stopquit_show(stopquit_Quit, 0);
        else
          exit(0);
        break;
    }
    return;
  }
  if ( !adjust)
    frontend_openmenu = M_NONE;
}



_kernel_oserror *popstar_event_poll( int *event_code, WimpPollBlock *pollblock, int nextidle )
{
  int reason;

  if ( stopquit_pending )
  {
    xsyslog_logmessage(log_NAME, msgs_lookup("Stopped"), log_MiscInfo);
    stopquit_act( stopquit_pending );
  }

  if ( nextidle != -1 )
  {
    E_CHECK( wimp_pollidle(pollmask, pollblock, nextidle, socketwatch_pollword, &reason) );
  }
  else
  {
    E_CHECK( wimp_poll(pollmask, pollblock, socketwatch_pollword, &reason) );
  }

  if ( socketwatch_pollword )
    *socketwatch_pollword = 0;

  switch (reason)
  {
    case Wimp_ENull:
      break;

    case Wimp_ERedrawWindow:
      break;

    case Wimp_EOpenWindow:
      E_CHECK( wimp_open_window( (WimpOpenWindowBlock *) pollblock ) );
      break;

    case Wimp_ECloseWindow:
      status_close_handler( pollblock->close_window_request.window_handle );
      treport_close( pollblock->close_window_request.window_handle );
      E_CHECK( wimp_close_window( &pollblock->close_window_request.window_handle ) );
      break;

    case Wimp_EMouseClick:
      frontend_mouseclick( pollblock->mouse_click.window_handle, pollblock->mouse_click.icon_handle,
                           pollblock->mouse_click.buttons,       pollblock->mouse_click.mouse_x,
                           pollblock->mouse_click.mouse_y);
      break;

    case Wimp_EKeyPressed:
      if ( ! treport_keyhan( pollblock->key_pressed.key_code ) )
        wimp_process_key( pollblock->key_pressed.key_code );
      break;

    case Wimp_EMenuSelection:
    {
      WimpGetPointerInfoBlock pointer;
      wimp_get_pointer_info( &pointer );
      frontend_menuselection( pollblock->menu_selection, pointer.button_state & Wimp_MouseButtonAdjust );
    } break;

    case Wimp_EUserMessage:
    case Wimp_EUserMessageRecorded:
      /* NB. Don't forget to add messages to wimp_messages[] too */
      {
        int messno = pollblock->user_message.hdr.action_code;
        if (messno == Wimp_MQuit)
        {
          exit( 0 );
        }
        else if ( messno == Wimp_MPreQuit )
        {
          prequit_handler( &pollblock->user_message, 0 );
        }
        else if ( messno == Wimp_MMenusDeleted )
        {
          if ( frontend_menuopening )
            frontend_menuopening = 0;
          else
            frontend_openmenu = M_NONE;
        }
        else if ( messno == URI_MReturnResult )
          url_bounce( &pollblock->user_message );
        else if ( messno >= Riscnet_FirstMsg && messno <= Riscnet_LastMsg )
          riscnet_wimpmessage( &pollblock->user_message );
      } break;

    case Wimp_EUserMessageAcknowledge:
      /* NB. Don't forget to add messages to wimp_messages[] too */
      frontend_wimpmessage_ack( &pollblock->user_message );
      break;

  }

  *event_code = reason;


  return NULL;
}




static void set_proginfo_ver(void)
{
  char buffer[255];
  char *ptr, *date=__DATE__, *out = buffer;
  /* __DATE__ is MMM   DD   YYYY */
  strcpy( buffer, TASK_VERSION );
  strcat( buffer, " (" );
  out = buffer + strlen( buffer );
  ptr = strchr( date, ' ' );
  if ( ptr )
  {
    while ( *ptr == ' ' ) ptr++;
    while ( *ptr && *ptr != ' ' ) *out++ = *ptr++; /* copy day */
    *out++ = '-';
    while ( *ptr && *ptr == ' ' ) ptr++; /* move to start of year */
    while ( *date && *date != ' ' ) *out++ = *date++; /* copy month */
    *out++ = '-';
//  if ( *ptr && *(ptr+1) ) ptr+=2; /* skip over century */
    while ( *ptr ) *out++ = *ptr++; /* copy year */
    *out++ = ')';
    *out = 0;
    wimpc_seticontext( frontend_infowin, INFOWIN_VERSION, buffer );
  }
}

static void parse_cmdline( int argc, char *argv[] )
{
  int x;
  int voyager = 0;
  int flashled = 0;

  for ( x = 1; x < argc; x++ )
  {
    if ( !strcmp( argv[x], "-voyager" ) )
      voyager = 1;
    else if ( !strcmp( argv[x], "-flashled" ) )
      flashled = 1;
    else if ( !strcmp( argv[x], "-noicon" ) )
      frontend_noicon = 1;
    else if ( !strcmp( argv[x], "-quit" ) )
      frontend_quit = 1;
    else if ( !strcmp( argv[x], "-debug" ) )
      frontend_debug = 1;
  }

  /* pass settings to voyager object */
  voyager_init( voyager, flashled );
}

int main( int argc, char *argv[] )
{
  int statuswin = -1, reportwin = -1, quitwin = -1;
  time_t pending_quit = 0;

  xsyslogf(log_NAME,log_DebugInfo,TASK_NAME" starting...");

#ifdef MEMLEAKON
  {
    FILE *f = fopen(TASK_DIR".memleak","a");
    memleak_verbose(1);
    if (!f)
    {
      fprintf(stderr, "Cannot open memleak file\n");
      exit(1);
    }
    memleak_set_stream(f);
  }
#endif

#ifdef SLSWIHACK
  hack_stub((unsigned int) _swix, (unsigned int) sl_swix);
  sl_swix(0x6e, _INR(0,2), 1, _swix, _swix);  /* OS_SynchroniseCodeAreas */
#endif

#ifdef MemCheck_MEMCHECK
  MemCheck_Init();
  /*MemCheck_RegisterArgs(argc, argv);*/
  MemCheck_InterceptSCLStringFunctions();
#endif

  if ( wimpc_taskpresent( TASK_NAME ) ) exit(0);
  
  E_CHECK_ABORT(file_createdirs(TASK_MQUEUE_TEXT));
  E_CHECK_ABORT(file_createdirs(TASK_MQUEUE_WORK));
  E_CHECK_ABORT(file_createdirs(TASK_BADMARK_TEXT));
  E_CHECK_ABORT(file_createdirs(TASK_BADMARK_WORK));
  E_CHECK_ABORT(file_createdirs(TASK_MAIL_TEXT));

  /*memleak_verbose(1);*/

  _swix(OS_ReadMonotonicTime, _OUT(0), &last_poll);

  E_CHECK_ABORT( wimp_initialise( 310, TASK_NAME, (int *) wimp_messages,
                                  NULL /* wimp version*/, NULL /* task handle */ ) );

  E_CHECK_ABORT( wimp_open_template(TASK_DIR".Templates") );
  reportwin            = wimpc_loadtemplate( "report" );
  quitwin              = wimpc_loadtemplate( "quitquery" );
  frontend_oversizewin = wimpc_loadtemplate( "OverSize" );
  if (reportwin == -1 || quitwin == -1 || frontend_oversizewin == -1)
  {
    xsyslogf(log_NAME,log_DebugInfo, "Failed to load templates.");
    E_CHECK_ABORT( wimp_close_template() );
    exit(1);
  }

  frontend_infowin     = wimpc_loadtemplate( "info" );
  if (frontend_infowin == -1)
  {
    xsyslogf(log_NAME,log_DebugInfo, "Failed to load info template.");
    E_CHECK_ABORT( wimp_close_template() );
    exit(1);
  }
  
#ifndef FEATURE_NOSTATUS  
  statuswin            = wimpc_loadtemplate( "status" );
  if (statuswin == -1)
  {
    xsyslogf(log_NAME,log_DebugInfo, "Failed to load status template.");
    E_CHECK_ABORT( wimp_close_template() );
    exit(1);
  }
#endif
  E_CHECK_ABORT( wimp_close_template() );

  frontend_mainmenu = wimpc_createmenu( 0, ',', TASK_NAME, MAINMENU );
  if ( !frontend_mainmenu )
    E_ABORT("Out of memory, could not create main menu!\n");

  E_CHECKABORT( msgtrans_load( TASK_DIR ".Messages", &msgs_descriptor, NULL ) );

  err_set_taskname(msgs_lookup("_TaskName"));
#ifdef MemCheck_MEMCHECK
  msgs_prime_memcheck();
#endif
  pollmask = Wimp_Poll_NullMask |
             Wimp_Poll_PointerLeavingWindowMask |
             Wimp_Poll_PointerEnteringWindowMask |
             Wimp_Poll_LoseCaretMask |
             Wimp_Poll_GainCaretMask;

  status_init( statuswin );
  treport_init( reportwin );
  set_proginfo_ver();
  user_init();
  riscnet_init();
  parse_cmdline( argc, argv );

  stopquit_init( quitwin );
  oversize_init( frontend_oversizewin );

  config_init();

#ifndef FEATURE_NOICONBAR
  if ( config_lookup_bool("IconBar:Y") && !frontend_noicon )
  {
    /* Icon bar if user has asked for one in config file, and -noicon flag isn't present */
    frontend_iconbar = wimpc_createiconbar( "!"TASK_NAME );
    if ( frontend_iconbar < 0 )
      exit(1);
  }
#endif
  
  socketwatch_init();
  if ( socketwatch_pollword )
  {
    /* enable pollword */
    pollmask |= Wimp_Poll_PollWord;
    atexit( socketwatch_finalise ); /* free pollword on exit */
  }
  else
  {
    /* disable pollword non-zero events */
    pollmask |= Wimp_Poll_PollWordNonZeroMask;
  }

  xsyslogf(log_NAME,log_DebugInfo,TASK_NAME" initialised");
  if ( !riscnet_present )
  {
    bool transfer = check_env();
#ifdef FEATURE_AUTOFETCHQUIT
    const bool autoquit = 1;
    if (transfer || config_lookup_bool("SendOffline:N"))
      popstar_fetchsendhandler();
    else
      popstar_fetchallhandler();
#else
    bool autoquit = ( config_lookup_bool("AutoSend") || config_lookup_bool("AutoFetch") ) &&
  	config_lookup_bool("AutoQuit");
  	
    if (popstar_keyalt())
    {
      autoquit = 0;
      frontend_quit = 0;
    }

    if ( config_lookup_bool("AutoFetch") && config_lookup_bool("AutoSend") &&
         (transfer || config_lookup_bool("SendOffline:N")) )
        popstar_fetchsendhandler();
    else if ( frontend_quit )
    {
      /* -quit passed - force a fetch + send */
      popstar_fetchsendhandler();
    }
    else if ( config_lookup_bool("AutoFetch") )
      popstar_fetchallhandler();
    else if (config_lookup_bool("AutoSend") && (transfer || config_lookup_bool("SendOffline:N")) )
      popstar_sendhandler();
#endif
    
    if (autoquit || frontend_quit)
    {
      if ( treport_pending == 0 )
        exit(0); /* autoquit, unless there's an error box pending */
      pending_quit = time( NULL ) + 30; /* schedule quit for when user has acknowledged error, or timer has elapsed */
    }
  }

  fetch_period = (unsigned int) (config_lookup_float("FetchEvery") * 6000.0f);
  send_period = (unsigned int) (config_lookup_float("SendEvery") * 6000.0f);
  checkvar_period = (unsigned int) (config_lookup_float("CheckOnlineVar:600") * 100.0f); /* seconds, def. 10 mins */
  reschedule();

  for (;;)
  {
    WimpPollBlock event;
    int ecode;
    unsigned int time_now;
    bool online_state;
    
    last_online_state = check_env();

    if ( (fetch_pending || send_pending) && pending_quit == 0 )
    {
      enum { fetch_send, fetch, send, nothing } todo = nothing;
//      xsyslogf( log_NAME, 0, "Next send  (%d) = %x", send_pending, next_send );
//      xsyslogf( log_NAME, 0, "Next fetch (%d) = %x", fetch_pending, next_fetch );
//      xsyslogf( log_NAME, 0, "Next idle      = %x", next_idle );
      popstar_event_poll(&ecode, &event, next_idle ); /* give the wimp control until we next want to do something */
      _swix(OS_ReadMonotonicTime, _OUT(0), &time_now);
      last_poll = time_now;
      online_state = check_env();

      /* fetch if there's a fetch pending, or we're doing periodic fetches and the online state has changed */
      if ( (fetch_pending && time_now - next_fetch < 0x80000000) ||
           (fetch_period && online_state && !last_online_state) )
      {
        if ( riscnet_present )
          fetch_pending = 0; /* no auto fetches whilst riscnet is around */
        else
          todo = fetch;
      }

      /* send if there's a send pending, or we're doing periodic sends and the online state has changed */
      if ( (send_pending && time_now - next_send < 0x80000000) ||
           (send_period && online_state && !last_online_state) )
      {
        if ( (check_env() || config_lookup_bool("SendOffline:N")) && !riscnet_present )
          todo = todo == fetch ? fetch_send : send;
        else
          send_pending = 0;
      }
      last_online_state = online_state;

      switch ( todo )
      {
        case fetch_send: popstar_fetchsendhandler(); break;
        case fetch:      popstar_fetchallhandler(); break;
        case send:       popstar_sendhandler(); break;
        default:         reschedule();
      }
    }
    else if (pending_quit)
    {
      _swix(OS_ReadMonotonicTime, _OUT(0), &time_now);
      pollmask = pollmask & ~Wimp_Poll_NullMask; /* take null polls */
      popstar_event_poll(&ecode, &event, time_now + 100);

      if ( treport_pending == 0 )
        exit( 0 ); /* user has acknowledged error */

      /* check to see if timer has expired */
      if ( time(NULL) > pending_quit)
        exit( 0 );
    }
    else
      popstar_event_poll(&ecode, &event, -1); /* if there's not a fetch pending, we won't be taking null polls */
  }

  return 0;
}
