/* 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);
}