#include "kernel.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "oslib/os.h"
#include "sockets.h"
#include "Desk.Error.h"

#include "def.h"
#include "defstruc.h"
#include "connstrc.h"
#include "eventstrc.h"

#include "event.h"
#include "master.h"
#include "telnet.h"
#include "syslogif.h"

static MasterConnection master;
int masterPort = 2200;

OSERROR errorBlock;
char errorString[STRING_BUFFER_SIZE];

void master_kill(void)
{
  xsyslogf_irq(SYSLOG_FILE,LOG_ERROR,"master_kill: tidying up as best we can\n");
  /* Tidy up as much as possible -
   * something went wrong so we can't continue */
   if (master.masterStatus != SOCKET_READY)
   {
     socket_close(master.masterSocket);
   }
   master.masterStatus = SOCKET_READY;

}

int master_handler(SOCKET socket, socket_event_type reason)
{
  switch (reason)
  {
    /* we've got an incoming connection - now let's set up a socket to it */
    case socket_ASYNC_EVENT:  telnet_open_connection(socket);
            break;
    case socket_BROKEN_EVENT: /* can we break a connection to the master socket? */
            break;
    default:
            xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_HIGH,"master_handler: unknown reason %d, socket %d\n",reason,socket);
            break;
  }

  return 0;
}


OSERROR *master_initialise(void)
{
  int true=-1;

    SYSLOG_ENTRY("master_initialise");
    master.masterStatus = SOCKET_READY;

    if ( (master.masterSocket = socket(AF_INET, SOCK_STREAM, 0)) == EOF)
    {
      xsyslogf(SYSLOG_FILE,LOG_ERROR,"master_initialise: Can't create master socket");
      Desk_Error_Report(0,"Can't create master socket - exiting.  (If SSHProxy was running before, wait a while and try again)");
      memcpy(&errorBlock,&_inet_error,sizeof(OSERROR));
      master_kill();
      return &errorBlock;
    }

    master.masterStatus = SOCKET_CREATED;

    if (socket_ioctl(master.masterSocket,FIOASYNC,&true) == EOF)
    {
      xsyslogf(SYSLOG_FILE,LOG_ERROR,"master_initialise: Failed to mark master socket for events");
      memcpy(&errorBlock,&_inet_error,sizeof(OSERROR));
      master_kill();
      return &errorBlock;
    }

    master.masterAddress.sin_family      = AF_INET;
    master.masterAddress.sin_addr.s_addr = htonl(INADDR_ANY);
    master.masterAddress.sin_port        = htons(masterPort);
    bzero((void *) &(master.masterAddress.sin_zero),8);

    if (bind(master.masterSocket, (struct sockaddr *) &master.masterAddress, sizeof(master.masterAddress)) == EOF)
    {
      xsyslogf(SYSLOG_FILE,LOG_ERROR,"master_initialise: Can't bind local address");
      Desk_Error_Report(0,"Can't bind master socket to port %d - exiting.  (If SSHProxy was running before, wait a while and try again)",masterPort);
      memcpy(&errorBlock,&_inet_error,sizeof(OSERROR));
      master_kill();
      return &errorBlock;
    }

    master.masterStatus = SOCKET_BOUND;

    if (listen(master.masterSocket, MAX_CONNECTIONS) == EOF )
    {
      xsyslogf(SYSLOG_FILE,LOG_ERROR,"master_initialise: Can't listen on master socket");
      Desk_Error_Report(0,"Can't listen on port %d - exiting.  (If SSHProxy was running before, wait a while and try again)",masterPort);
      memcpy(&errorBlock,&_inet_error,sizeof(OSERROR));
      master_kill();
      return &errorBlock;
    }

    master.masterStatus = SOCKET_LISTENING;

    if (socket_ioctl(master.masterSocket,FIONBIO,&true) == EOF)
    {
      xsyslogf(SYSLOG_FILE,LOG_ERROR,"master_initialise: Can't set master socket as non-blocking");
      memcpy(&errorBlock,&_inet_error,sizeof(OSERROR));
      master_kill();
      return &errorBlock;
    }

    event_register_handler(master.masterSocket, master_handler);

    SYSLOG_EXIT("master_initialise");
    return NO_OSERROR;

}

OSERROR *master_finalise(void)
{
   event_deregister_handler(master.masterSocket, master_handler);

   if (master.masterStatus != SOCKET_READY)
   {
     socket_close(master.masterSocket);
     master.masterStatus = SOCKET_READY;
   }

   return NO_OSERROR;
}
