/**
   @file

   Server/User-Side Application with GUI to control the robot.
*/

#include <time.h>
#include <math.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <glib.h>
#include <unistd.h>
#include <signal.h>

#include "../lib/libcomm.h"
#include "guicommon.h"

// Pointer to the Main-Application-Object. Is necessary if a function
// of the GUI is called from a not-gui-object.
GtkWidget *gs_application;

// Stores the absolut position of the robot. 
int robot_x = 50, robot_y = 0;
// ... fill it into a structure to be able to transfer it
struct ROBOT_POSITION robot_position;

// Stores the portnumber on which the server operates. serverport:
// Datalink (remote/connect); serverport + 1: Linemonitor
// (local/bind).
int serverport;

// filediscriptor which pointes to the socket-stream which is
// connected to the robot's side and used fot the data-transfer.
int sock;

// Configuration for LineMonitor. Stores the Soft- and Hard-Timeout
// and wait-times.
int soft_msec;
int hard_msec;
int wait_msec;

// File discriptor to the linemonitor() connection. Used for Emergercy
// Stop command
int ems_fd;



/**

    @param widget (GtkWidget *) pointer to affected object (for
    example: window).

    @param event (GdkEventM *) pointer to structure which
    describes the event.

    @param data (gpointer) pointer to additional data from gtk.

*/
static gbooleandelete_event(GtkWidget *widget,
			    GdkEvent  *event,
			    gpointer   data) {

  linemonitor_emergencystop(ems_fd); // ??
  fprintf(stderr, "Shut down...\n");
  gtk_main_quit();
  gdk_window_process_all_updates();
  return FALSE;
}


/** This function is called if a signal (line C-c, terminate)
    occurs. See in signal() -calls in main() for detals.

    @param sig (int) number of orrured signal
*/
static void finish(int sig) {
  gs_delete_event(gs_application, NULL);
}


/** This function will be called be gtk when the window gets a closing
    command, it makes sure that the interface is down and the program
    is finished.

    @param widget (GtkWidget *) pointer to affected object (for
    example: window).

    @param data (gpointer) pointer to additional data from gtk.
 */
void gs_delete_event(GtkWidget *widget, gpointer data) {
  linemonitor_emergencystop(ems_fd);
  fprintf(stderr, "Shut down...\n");
  gtk_main_quit();
  gdk_window_process_all_updates();
}


/** This function is called by gtk if the drawing area (da) needs to
    be redrawn. For example if it becomes visible.

    @param widget (GtkWidget *) pointer to affected object (in this
    case the drawing area).

    @param data (gpointer) pointer to additional data from gtk.
*/
void gs_da_expose_event(GtkWidget *widget, gpointer data) {
  gui_common_drawrobot(widget, &robot_x, &robot_y, -1, -1, 0);
}


/** This function is called if an event occurs (mose motion or mouse
    button press) over the drawing area (da).

    @param widget (GtkWidget *) pointer to affected object (in this
    case the drawing area).

    @param event (GdkEventMotion *) pointer to structure which
    describes the event.
*/
static gint
motion_notify_event(GtkWidget *widget, GdkEventMotion *event) {
  // variables for the position of the mouse
  int x, y;

  GdkModifierType state;

  // member-variable to store if a moving action takes place or not
  static int move = 0;

  // get positoin of the mouse-pointer
  if (event->is_hint) {
    gdk_window_get_pointer (event->window, &x, &y, &state);
  } else {
    x = event->x;
    y = event->y;
    state = event->state;
  }

  // if left mouse button is pressed ...
  if (state & GDK_BUTTON1_MASK) {
    // ... re-draw robot and dertermine new position of the robot if
    // the click was on the robot
    gui_common_drawrobot(widget, &robot_x, &robot_y, x, y, 1);
    move = 1;
    // if the connection to the robot is established...
    if (sock != 0) {
      // ... and the position of it was changed ...
      if ((robot_position.x != robot_x) ||
	  (robot_position.y != robot_y)) {
	robot_position.x = robot_x;
	robot_position.y = robot_y;

	// ... send the new coordinates to the robot
	block_send(sock, 1, (char *) &robot_position,
		   sizeof(struct ROBOT_POSITION));
      }
    }
  } else {
    // moving event is finished, drwa robot.
    if (move) {
      gui_common_drawrobot(widget, &robot_x, &robot_y, x, y, 2);
      move = 0;
    }
  }
  
  return TRUE;
}



/** This function is called if a button is pressed

    @param widget (GtkWidget *) pointer to affected object (button).

    @param event (GdkEventMotion *) pointer to structure which
    describes the event.
*/

void gs_button_press_event(GtkWidget *widget, gpointer data) {
  if(strcmp("Emergency Stop", (char *)data) == 0) {
    linemonitor_emergencystop(ems_fd);
    gs_delete_event(gs_application, NULL);
    g_print("Emergency Stop Button pressed\n");
  }
}



/** Exception function for linemonitor_server(), print exception
    code and meaning on the screen and initiate appropriate
    actions if necessary. See linemonitor_server() API. */
linemonitor_exception(char *server, int port, int type) {
  fprintf(stderr, "linemonitor_exception(%s, %d, %d): ",
	  server, port, type);
  switch (type) {
  case 0:
    fprintf(stderr, "Connecion Fault\n");
    gs_delete_event(gs_application, NULL);
    break;
  case 1:
    fprintf(stderr, "Soft Real Time Exception\n");
    break;
  case 2:
    fprintf(stderr, "HARD Real Time Exception\n");
    gs_delete_event(gs_application, NULL);
    break;
  } /* switch() */
}


/**
   Start the linemonitor_server() -- waiting for a connection in a
   background-thread. This has to be a thread because otherwise the
   system would block.  It would wait for a connection while it is
   supposed to connect itself.
*/
thread_wait_for_linemonitor() {
  ems_fd = linemonitor_server(serverport + 1,
			      soft_msec, hard_msec, wait_msec,
			      (void *) linemonitor_exception);
  pthread_exit(NULL);
}



int main(int argc, char *argv[]) {
  // GTK-Objecte. This is necessary to create the buttons on the
  // screen and connect them to some actions
  GtkWidget *button1, *button2, *button3, *button4;
  // Sorting into tables.
  GtkWidget *table, *table2;
  // pointer to the DrawingArea-Object
  GtkWidget *da;

  // Init the gtk (GUI toolkit)
  gtk_init(&argc, &argv);

  // Connect the finish() -function to some signals which can cause a
  // system shut down.
  signal(SIGHUP, finish);  // 01  /  hangup     - close Window
  signal(SIGINT, finish);  // 02  /  Interrupt  - ^C - C-c
  signal(SIGQUIT, finish); // 03  /  Quit
  signal(SIGTERM, finish); // 15  /  Terminierung -- kill
  signal(SIGALRM, finish); // 14  /  Alarm

  // load and examine parameters
  if (argc != 6) {
    sock = 0;
    fprintf(stderr, "%s robot-address port soft_msec hard_msec wait_msec\n", argv[0]);
    //    exit(0);
  } else {
    // ID and atributes for the threads
    pthread_t        thrd_2;
    pthread_attr_t   thrd_2_attr;

    serverport = atoi(argv[2]);
    soft_msec = atoi(argv[3]);
    hard_msec = atoi(argv[4]);
    wait_msec = atoi(argv[5]);

    // launch thread_wait_for_linemonitor(), see
    // thread_wait_for_linemonitor().
    pthread_attr_init(&thrd_2_attr);
    pthread_create(&thrd_2,
		   &thrd_2_attr,
		   (void *) thread_wait_for_linemonitor,
		   NULL);

    // make sure, that the linemonitor_server() is ready before
    // connect to the robot.
    sleep(1);

    // connect to the robot.
    if ((sock = socket_connect(argv[1], serverport)) <= 0) {
      perror("connect()");
      sock = 0;
    }
  }

  // create button(s)
  button1  = gtk_button_new_with_label("Emergency Stop");
  //  button2  = gtk_button_new_with_label("Button 2");
  //  button3  = gtk_button_new_with_label("Button 3");
  //  button4  = gtk_button_new_with_label("Button 4");


  // connect the buttons to the function gr_button_press_event(). If
  // the botton is pressed, this function will be colled.
  gtk_signal_connect(GTK_OBJECT(button1), "clicked",
		     GTK_SIGNAL_FUNC(gs_button_press_event),
		     "Emergency Stop" );
  /*  gtk_signal_connect(GTK_OBJECT(button2), "clicked",
		     GTK_SIGNAL_FUNC(gs_button_press_event),
		     "Button 2");
  gtk_signal_connect(GTK_OBJECT(button3), "clicked",
		     GTK_SIGNAL_FUNC(gs_button_press_event),
		     "Button 3" );
  gtk_signal_connect(GTK_OBJECT(button4), "clicked",
		     GTK_SIGNAL_FUNC(gs_button_press_event),
		     "Button 4");*/

  // create tables to sort buttons and drawindarea in it
  table = gtk_table_new(2,2,FALSE);
  table2 = gtk_table_new(2,2,FALSE);

  // create new application (wiindow)
  gs_application = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  // set window title
  gtk_window_set_title(GTK_WINDOW (gs_application),
		       "Server - User Interface");

  // connect the function gr_delete_event() with the event of a
  // window-close.
  g_signal_connect(G_OBJECT (gs_application), "delete_event",
		   G_CALLBACK (gs_delete_event), NULL);

  // display window
  gtk_widget_show(gs_application);

  // create drawing area
  da = gtk_drawing_area_new();

  // set which events in the drawing area causing a expose-event
  gtk_widget_set_events (da, GDK_EXPOSURE_MASK
			 | GDK_LEAVE_NOTIFY_MASK
			 | GDK_BUTTON_PRESS_MASK
			 | GDK_POINTER_MOTION_MASK
			 | GDK_POINTER_MOTION_HINT_MASK);
  
  // set a minimum size of da
  gtk_widget_set_size_request (da, 100, 100);
  
  // connect the function gr_delete_event() with the event of a
  // window-close.
  gtk_signal_connect(GTK_OBJECT(gs_application), "delete_event",
		     GTK_SIGNAL_FUNC(gs_delete_event), NULL);

  // connect the function gr_da_expose_event() with the expose-event
  // of the drawing area.
  gtk_signal_connect(GTK_OBJECT(da), "expose_event",
		     GTK_SIGNAL_FUNC(gs_da_expose_event), NULL);

  // connect the function motion_notify_event() with the
  // mouse-motion-event of the drawing area.
  gtk_signal_connect(da, "motion_notify_event",
		     GTK_SIGNAL_FUNC(motion_notify_event), NULL);

  // fill the buttons in the table
  gtk_table_attach_defaults(GTK_TABLE(table2), button1, 0,1, 0,1);
  /*  gtk_table_attach_defaults(GTK_TABLE(table2), button2, 0,1, 1,2);
  gtk_table_attach_defaults(GTK_TABLE(table2), button3, 1,2, 0,1);
  gtk_table_attach_defaults(GTK_TABLE(table2), button4, 1,2, 1,2); */

  // fill the drawing area and the table into another table
  gtk_table_attach_defaults(GTK_TABLE(table), da, 0,1, 0,1);
  gtk_table_attach_defaults(GTK_TABLE(table), table2, 0,1, 1,2);

  // fill this table into the windos
  gtk_container_add(GTK_CONTAINER(gs_application),table);

  // display it  
  gtk_widget_show_all(gs_application);

  // call gtk-main-loop
  gtk_main();

  return 0;
}


