#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "kernel.h"
#include "server.h"
#include "oslib/os.h"
#include "oslib/osword.h"

#ifndef BUILD_MODULE
// build application (run it in a task window)

int main(int argc, char **argv) {

  if (server_start(5900, "")) {   // port and password
    exit(0);
  }
  server_set_flag(SERVER_FLAG_SUPPORT_MOUSE,    SERVER_FLAG_SET);
  server_set_flag(SERVER_FLAG_SUPPORT_KBD,      SERVER_FLAG_SET);
  server_set_flag(SERVER_FLAG_SWAP_MENU_ADJUST, SERVER_FLAG_SET);
  atexit(server_stop);
  while (1) {
    server_poll();
  }
}


#else
// build module

static char password[9]       = "";
static int port               = 5900;
static int timer_waiting      = 0;
static int callback_waiting   = 0;
static int nudge_delay        = 25; // centiseconds without callbacks before nudging mouse
static int last_callback_time = 0;  // time of last callback

static int frontend_start(byte *pw);
static int frontend_stop(byte *pw);

// veneers
int timer_handler(_kernel_swi_regs *r, byte *pw);
int callback_handler(_kernel_swi_regs *r, byte *pw);


_kernel_oserror err_bad_arguments_0 = { 1, "Syntax: *vncserv_start <port> <password>" };


// ---------------------------------------------------------------------
// module api


_kernel_oserror *module_init(char *tail, int base, byte *pw)
{
  return NULL;
}

_kernel_oserror *module_finalise(int fatal, int base, byte *pw)
{
  frontend_stop(pw);
  return NULL;
}


_kernel_oserror *command_handler(char *args, int argc, int cmdno, byte *pw)
{
  switch (cmdno) {
  case 0:           // *vncserv_start <port> <password>
    {
      int prt;
      char pwd[256];
      if (sscanf(args, "%d %s", &prt, pwd) == 2) {
        port = prt;
        strncpy(password, pwd, 8);
        password[8] = '\0';
        frontend_start(pw);
        return NULL;
      }
      else { 
        return &err_bad_arguments_0;
      }
    }
    break;
  case 1:           // *vncserv_stop
    frontend_stop(pw);
    break;
  case 2:           // *vncserv_mouse_kbd
    server_set_flag(SERVER_FLAG_SUPPORT_MOUSE, SERVER_FLAG_SET);
    server_set_flag(SERVER_FLAG_SUPPORT_KBD, SERVER_FLAG_SET);
    break;
  case 3:           // *vncserv_swap_adjust_menu 0 | 1
    if (atoi(args)) {
      server_set_flag(SERVER_FLAG_SWAP_MENU_ADJUST, SERVER_FLAG_SET);
    }    
    else {
      server_set_flag(SERVER_FLAG_SWAP_MENU_ADJUST, SERVER_FLAG_CLEAR);
    }
    break;
  case 4:           // *vncserv_dont_nudge_mouse [<centiseconds>]
    nudge_delay = 0x40000000;
    break;
  }
  return NULL;      // or pointer to error-block
}


// ---------------------------------------------------------------------
// handlers and stuff

int timer_handler(_kernel_swi_regs *r, byte *pw)
{
  if (clock() > last_callback_time + nudge_delay) {
    oswordpointer_position_block pos;
    // release mouse keys
    server_release_key(0x70);
    server_release_key(0x71);
    server_release_key(0x72);

    pos.op = oswordpointer_OP_SET_POSITION;
    pos.x = 2*mouse_x;
    pos.y = 2*(screensize_y - mouse_y + 1);
    xoswordpointer_set_position(&pos);
    pos.op = oswordpointer_OP_SET_POSITION;
    pos.x = 2*mouse_x;
    pos.y = 2*(screensize_y - mouse_y);
    xoswordpointer_set_position(&pos);
    last_callback_time = clock();
  }
  if (callback_waiting)  {
    return 1;
  }
  xos_add_call_back((void *)callback_handler, (byte *)pw);
  callback_waiting = 1;
  return 1;
}


int callback_handler(_kernel_swi_regs *r, byte *pw)
{
  last_callback_time = clock();
  server_poll();
  callback_waiting = 0;
  return 1;                   // always return a value != 0 !!!!!
}

// ---------------------------------------------------------------------
// frontend

int frontend_start(byte *pw)
{
  server_start(port, password);
  xos_call_every(1, (void *)timer_handler, pw);
  timer_waiting = 1;
  last_callback_time = clock();
  return 0;
}


int frontend_stop(byte *pw)
{
  if (timer_waiting) {
    xos_remove_ticker_event((void *)timer_handler, pw);
  }
  timer_waiting = 0;
  if (callback_waiting) {
    xos_remove_call_back((void *)callback_handler, pw);
  }
  callback_waiting = 0;
  server_stop();
  return 0;
}

#endif
