/* Include files for X 11 */
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include <termio.h>
#include <stdio.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <string.h>
#include <unistd.h>

#include "digitizer.h"

/* ---------------------------------------------------------------------- */


#define DIGI_MSG_LEN 13         /* Laenge der Digitizer Message */
#define MAX_MSG 5               /* Anzahl der MSG die ueberlesen werden */
#define DIGI_INIT_STRING "@cG\"" /* Stream-Mode / Grid-Mode */
#define TRUE (0==0)
#define FALSE (1==0)
/* #define DEBUG Um Debugging einzuschalten */

/* ---------------------------------------------------------------------- */

/* Modulglobale Variable */
static int device;
static int win_x,
          win_y,
          win_w,
          win_h;
static int display_height;
static int win_pos_unknown = TRUE;

/* ---------------------------------------------------------------------- */

/*
 Position und Groesse des LaTeXDraw-Fensters speichern
*/
void save_win_pos(x, y, w, h)
int x, y, w, h;
{
   if (win_pos_unknown)
   {
           /* Erster Aufruf ==> alles merken */
       win_x = x;
       win_y = y;
       win_w = w;
       win_h = h;
       win_pos_unknown = FALSE;
#ifdef DEBUG
       fprintf(stderr, "Alles neu : (%d|%d|%d|%d)\n",
               win_x, win_y, win_w, win_h);
#endif
   }
   else {
       if ((win_w == w) && (win_h == h))
       {
               /* Position des Fensters hat sich veraendert */
           win_x = x;
           win_y = y;
#ifdef DEBUG
           fprintf(stderr, "Neue Position : (%d|%d)\n", win_x, win_y);
#endif
       }
       else {
               /* Groesse des Fensters hat sich veraendert */
           win_w = w;
           win_h = h;
#ifdef DEBUG
           fprintf(stderr, "Neue Groesse : (%d|%d)\n", win_w, win_h);
#endif
       }
   }
}

/* ---------------------------------------------------------------------- */

/*
 Verbindung zum Digitizer herstellen und Digitizer initialisieren
*/
int init_digitizer(display)
Display *display;
{
   char config[32];
   int i;
   struct termio termbuf;
   XWindowAttributes attribs;


   device = open("/dev/tty01", O_RDWR);

   if (-1 == device)
   {
       printf("\r\nDevice konnte nicht geoeffnet werden");
       return(-1);
   }

   if (-1 == ioctl(device,TCGETA,&termbuf))
   {
       printf("\r\nFehler beim lesen der KONFIG, Fehler %d",errno);
       return(-1);
   }


   termbuf.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | BRKINT);
   termbuf.c_oflag &= ~OPOST;
   termbuf.c_lflag &= ~(ICANON | ISIG | ECHO);
   termbuf.c_cc[VMIN] = 0;
   termbuf.c_cc[VTIME] = 0;

   termbuf.c_cflag &= ~CBAUD;
   termbuf.c_cflag |= B9600;
   termbuf.c_cflag &= ~CSIZE;
   termbuf.c_cflag |= CS8;
   termbuf.c_cflag &= ~CSTOPB;
   termbuf.c_cflag &= ~PARENB;

   if (-1 == ioctl(device,TCSETA,&termbuf))
   {
       printf("\r\nFehler device %d",errno);
       return(-1);
   }
           /* presence stream mode */
   strcpy(config, DIGI_INIT_STRING);

   for (i=0; i<strlen(config); i++)
       write(device, &config[i], 1);

       /* Groesse des Displays holen und Digitizer skalieren */
   XGetWindowAttributes(display, DefaultRootWindow(display), &attribs);
   config[0] = 'r';
   config[1] = attribs.width & 0xff;
   config[2] = (attribs.width & 0xff00) >> 8;
   config[3] = attribs.height & 0xff;
   config[4] = (attribs.height & 0xff00) >> 8;
   write(device, config, 5);

   display_height = attribs.height;

   return(1);
}

/* ---------------------------------------------------------------------- */

/*
 Aus der seriellen Schnittstelle ankommende Zeichen lesen
 Insgesamt werden MAX_MSG Nachrichten eingelesen, von denen die letzte
 in *x,*y,*button zurueckgegeben wird
*/
static int readit(x,y,button)
int *x,
   *y,
   *button;
{
       /* Zwischenspeicher fuer eingegangene Meldungen */
   static char zwsp[(MAX_MSG+1)*DIGI_MSG_LEN];
   static int count = 0;

       /* Kommandos fuer Digitizer Mode */
   char cmd1[] = "G!";         /* Wert nur nach Bewegung */
   char cmd2[] = "G ";         /* Immer Werte senden */

   char buffer[MAX_MSG*DIGI_MSG_LEN];
   int  i,n;
   int erg;


       /* Im Zweifelsfall kein Ergebniss vorhanden */
   erg = 0;

       /* Maximal eine Nachricht einlesen */
   n = read(device, buffer, 4*DIGI_MSG_LEN);

       /* Gelesene Zeichen in den Puffer kopieren */
   if (0 != n)
   {
       for (i=0; i<n; i++)
           zwsp[count++] = buffer[i];
   }

       /* Eine komplette Nachricht da ? */
   while (count >= DIGI_MSG_LEN)
   {
           /* Ist der String korrekt aufgebaut */
       if (zwsp[12] == 10)
       {
               /* Korrekter String */
           *x = *y = *button = 0;
           for (i=0;i<4;i++)
           {
               *x = *x * 10 + (zwsp[i] - '0');
               *y = *y * 10 + (zwsp[5+i] - '0');
           }
           *button = zwsp[10] - '0';

           count -= DIGI_MSG_LEN;
           for (i=0; i<count ; i++)
               zwsp[i] = zwsp[DIGI_MSG_LEN+i];

           if (*button != 0)
               write(device, cmd2, strlen(cmd2));
           else
               write(device, cmd1, strlen(cmd1));

           erg = 1;
       }
       else
       {
               /* Nachricht war Muell also neuen Beginn suchen */
           buffer[0] = 0;
           while (10 != buffer[0])
               read(device, buffer, 1);
           /* Puffer als geloescht markieren */
           count = 0;
       }
           /* fprintf(stderr,"(%d|%d | %d)\n", *x, *y, *button); */
   }


   return (erg);
}

/* ---------------------------------------------------------------------- */

/*
 Digitizer Koordinaten so umwandeln, dass aus ihnen LaTeXDraw-Koordinaten
 werden
*/
static void translate_digi_to_ltd(x_in, y_in, x_out, y_out)
int x_in,
   y_in;
int *x_out,
   *y_out;
{
   *x_out = x_in;
   *y_out = win_h - y_in;
#ifdef DEBUG
   fprintf(stderr, "(%d|%d) ==> (%d|%d)\n",
           x_in,y_in, *x_out, *y_out);
#endif
}

/* ---------------------------------------------------------------------- */

/*
 Koordianten eines Digitizer-Events in LTD-Koordinaten umrechnen
*/
void translate_x_to_ltd(x_in, y_in, x_out, y_out)
int x_in,
   y_in;
double *x_out,
      *y_out;
{
   *x_out = (double)x_in;
       /* *y_out = (double)(y_in - win_h); */
   *y_out = (double)(win_h - y_in);
#ifdef DEBUG
   fprintf(stderr, "x->digi (%d|%d) ==> (%f|%f)\n",
           x_in,y_in, *x_out, *y_out);
#endif
}

/* ---------------------------------------------------------------------- */

/*
 Digitizer-Nachrichten erzeugen
*/
int process_digitizer(display, window)
Display *display;
Window window;
{
   static int old_x = -1;
   static int old_y = -1;
   static int old_b = -1;

   int x,y,
       this_x,
       this_y,
       this_b;
   int stateField[] = {0,
                        Button2Mask, Button1Mask,
                        Button4Mask, Button3Mask};
   int buttonField[] =  {0, 2, 1, 4, 3};
   long motionEventField[] = {0,
                              Button2MotionMask, Button1MotionMask,
                              Button4MotionMask, Button3MotionMask};
   XEvent myEvent;
   int event_b;
   long event_mask;



       /* Wert vom Digitizer holen */
   if (0 == readit(&x, &y, &this_b))
       return(FALSE);

       /* Koordinaten fuer LaTeXDraw umsetzen */
   translate_digi_to_ltd(x,y, &this_x, &this_y);

   if ((this_x != old_x) || (this_y != old_y))
   {
           /* Bewegung des Cursors ausfuehren */
       XWarpPointer(display,
                    None, window, /*DefaultRootWindow(display),*/
                    0, 0, 0, 0,
                    this_x,
                    this_y);
       old_x = this_x;
       old_y = this_y;

       if ((this_b != 0) && (this_b <= 4))
       {
           myEvent.type = MotionNotify;
           myEvent.xany.display = display;
           myEvent.xmotion.window = window;
           myEvent.xmotion.x = this_x;
           myEvent.xmotion.y = this_y;
           myEvent.xmotion.state = stateField[this_b];
           event_mask = motionEventField[this_b];
           XSendEvent(display, PointerWindow, 0, event_mask, &myEvent);
       }
   }


       /* Button - Event */
   if ((this_b != abs(old_b)) && (this_b <= 4))
   {
           /* Button press oder release */
       if (old_b < 0)
       {
           event_b = -old_b;
           old_b = 0;
           myEvent.type = ButtonRelease;
           event_mask = ButtonReleaseMask;
       }
       else
       {
           event_b = this_b;
           old_b = -this_b;
           myEvent.type = ButtonPress;
           event_mask = ButtonPressMask;
       }

       myEvent.xany.display = display;
       myEvent.xbutton.window = window;
       myEvent.xbutton.x = this_x;
       myEvent.xbutton.y = this_y;
       myEvent.xbutton.state = stateField[event_b];
       myEvent.xbutton.button = buttonField[event_b];

       XSendEvent(display, PointerWindow, 0, event_mask, &myEvent);
   }

       /* XEvent rausschreiben */
   XSync(display, 0);
   return(TRUE);
}