/*--------------------------------------------------*
 *                                                  *
 * Module:    GetTouch.c                            *
 * Purpose:   To provide an intelligent interface   *
 *            to the Elographics ELODEV TSR from an *
 *            application that must distinguish     *
 *            between button and curve type touches.*
 * Author:    W. Harvey Gray                        *
 * Compiler:  Microsoft 5.0                         *
 *                                                  *
 * External   GetTouch                              *
 * Functions: InitializeTouch                       *
 *            ReplayTouch                           *
 *            SetButtonRadius                       *
 *            TouchInfo                             *
 *                                                  *
 * Variables: There are no external variables used  *
 *            or defined by this module.            *
 *                                                  *
 * Copyright 1992, W. Harvey Gray.  May be used     *
 *   freely, if authorship and publication are      *
 *   acknowledged.                                  *
 *                                                  *
 *--------------------------------------------------*/

#include <math.h>
#include <stdio.h>

#include "dvrfunc.h"
#include "GetTouch.h"

/*--------------------------------------------------*
 *                module defines                    *
 *--------------------------------------------------*/

#define TOUCH_BUF_NUM 50   /* Internal buffer size. */

/*--------------------------------------------------*
 *                static variables                  *
 *--------------------------------------------------*/

static boolean is_button;       /* Is a button flag */
static boolean from_tbuff;  /* Touch in buffer flag */
static boolean overflow;   /* Buff storage exceeded */ 

static int num_returned;      /* Total touch points */
static int num_touch;      /* Count of touch points */
static int ut;          /* Touch point untouch flag */
static int xt;          /* Touch point x coordinate */
static int yt;          /* Touch point y coordinate */

static long radius2;     /* Square of button radius */
static long dist2;             /* Distance variable */
static long xcen;            /* X centroid of touch */
static long xsum;          /* X sum of touch points */
static long ycen;            /* Y centroid of touch */
static long ysum;          /* Y sum of touch points */

/*--------------------------------------------------*
 *                touch buffer and pointers         *
 *--------------------------------------------------*/

static struct touchit touch_information;

static struct touchit *pti = &touch_information;

static struct touchxy touch[TOUCH_BUF_NUM];
static struct touchxy tub;

static struct touchxy *ptouch = &touch[0];
static struct touchxy *ptub = &tub;

/*--------------------------------------------------*
 *                static prototypes                 *
 *--------------------------------------------------*/

static struct touchxy *buffered( boolean * );
static struct touchxy *unbuffered( boolean * );

static void buffer_touch( void );
static void store_touch( struct touchxy * );

/*--------------------------------------------------*
 *     extern function   GetTouch()                 *
 *--------------------------------------------------*/

struct touchxy *GetTouch( boolean *button ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Function GetTouch doesn't accept any inputs    *
 * from the caller and returns two values.  If      *
 * there is a touch in progress, then GetTouch      *
 * returns a pointer to a struct that contains the  *
 * next touch point coordinate pair.  If no touch   *
 * is in progress, then GetTouch returns a NULL     *
 * pointer to a touchxy struct.  Also returned via  *
 * a pointer to the boolean button variable is the  *
 * logical value of the button flag.  If TRUE, then *
 * the touch should be interpreted like the user    *
 * touched a button.  If FALSE, then the touch      *
 * should be interpreted like the user is drawing   *
 * a curve.                                         *
 *                                                  *
 *--------------------------------------------------*/
    
    if ( from_tbuff )
        return buffered( button );

    /*----------------------------------------------*
     *   If there is no touch, then return immedi-  *
     * ately pointing to a null touch point.        *
     *----------------------------------------------*/

    if ( ! gettouch( &xt, &yt, &ut ) )
        return (struct touchxy *) NULL;

    /*----------------------------------------------*
     *   If the space in the touch buffer has been  *
     * exceeded, then simply return the touch point.*
     *----------------------------------------------*/

    if ( overflow )
        return unbuffered( button );

    /*----------------------------------------------*
     *   The last special case occurs when an       *
     * untouch occurs on the first touch point.  If *
     * this happens, simply return the untouch pt.  *
     *----------------------------------------------*/

    else if ( ut ) {

        ptub->xt = xt;
        ptub->yt = yt;
        ptub->ut = ut;
        *button = TRUE;

        return ptub;
    }

    /*----------------------------------------------*
     *   If here, then the touch point is the first *
     * touch point of a new touch.  Begin by buffer-*
     * ing (at most) TOUCH_BUF_NUM touch points.     *
     *----------------------------------------------*/

    is_button = TRUE;      /* Assume it's a button. */
    num_touch = 1;  /* Init. number of touch points */

    ptouch = &touch[0];  /* Init. touch buffer ptr. */
    ptouch->xt = xt;           /* Store touch point */
    ptouch->yt = yt;
    ptouch->ut = ut;

    xcen = xsum = (long) xt; /* Init. touch pt cen. */
    ycen = ysum = (long) yt;

    buffer_touch();        /* Fill the touch buffer */
    
    /*----------------------------------------------*
     *   Setup so that the touch can be extracted   *
     * from its internal buffer and return the      *
     * first touch point.                           *
     *----------------------------------------------*/

    from_tbuff = TRUE;
    num_returned = 1;

    ptouch = &touch[0];
    *button = is_button;

    return ptouch;
}

/*--------------------------------------------------*
 *     extern function   InitializeTouch()          *
 *--------------------------------------------------*/

void InitializeTouch( void ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Initialize the internal variables of the       *
 * GetTouch module.                                 *
 *                                                  *
 *--------------------------------------------------*/

    is_button = FALSE;
    from_tbuff = FALSE;
    overflow = FALSE;

    num_returned = 0;
    num_touch = 0;
    ut = FALSE;
    xt = 0;
    yt = 0;

    radius2 = 0;
    dist2 = 0;
    xcen = 0;
    xsum = 0;
    ycen = 0;
    ysum = 0;

    return;
}

/*--------------------------------------------------*
 *     extern function   ReplayTouch()              *
 *--------------------------------------------------*/

void ReplayTouch( boolean set_button ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Replays a touch, if there is one in the buffer.*
 *                                                  *
 *--------------------------------------------------*/
    
    if ( from_tbuff ) {      /* Lose, if no buffer. */

        num_returned = 0;
        ptouch = &touch[-1];   /* It will be ++'ed. */
        is_button = set_button;
    }
    return;
}

/*--------------------------------------------------*
 *     extern function   SetButtonRadius()          *
 *--------------------------------------------------*/

void SetButtonRadius( int radius ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Sends the button radius to this module.        *
 *                                                  *
 *--------------------------------------------------*/

    radius2 = (long) radius * radius;
}

/*--------------------------------------------------*
 *     extern function   TouchInfo()                *
 *--------------------------------------------------*/

struct touchit *TouchInfo( void ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Returns a pointer to a struct that contains    *
 * touch information.                               *
 *                                                  *
 *--------------------------------------------------*/

    pti->xcen = (int) ( xsum / (long) num_touch );
    pti->ycen = (int) ( ysum / (long) num_touch );
    pti->n = num_touch;
    pti->button = is_button;

    return pti;
}

/*--------------------------------------------------*
 *     static function   buffer_touch()             *
 *--------------------------------------------------*/

static void buffer_touch( void ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Fill up the touch buffer by reading and storing*
 * touch points.  Several conditions stop filing    *
 * the touch buffer: if there are too many touch    *
 * points for the buffer, stop buffering; if the    *
 * touch is determined to not be a button, then     *
 * there is no need to continue buffering; or if    *
 * an untouch is encountered, then quit buffering.  *
 *                                                  *
 *--------------------------------------------------*/

    while ( num_touch < TOUCH_BUF_NUM ) {

        if ( gettouch( &xt, &yt, &ut ) ) {

            num_touch++;
            store_touch( ++ptouch );

            if ( dist2 > radius2 ) {

                /* If here, then not a button push. */
                                 /* Stop buffering. */
                is_button = FALSE;
                break;
            }
            if ( ut ) {
                                 /* Stop buffering. */
                overflow = FALSE;
                break;
            }
        }
    }
    return;
}

/*--------------------------------------------------*
 *     static function   buffered()                 *
 *--------------------------------------------------*/

static struct touchxy *buffered( boolean *button ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Returns a buffered touch checking for the end  *
 * of the internal touch buffer.  If the end of the *
 * buffer is encountered, then several conditional  *
 * flags are set.                                   *
 *                                                  *
 *--------------------------------------------------*/

    num_returned++;
    ptouch++;
    *button = is_button;
                               /* Exhausted buffer? */

    if ( num_returned == num_touch ||
         num_returned > TOUCH_BUF_NUM ) {

        from_tbuff = FALSE;
        overflow = TRUE;
    }
                              /* Untouch buffered? */
    if ( ptouch->ut ) {

        from_tbuff = FALSE;
        overflow = FALSE;
    }
    return ptouch;
}

/*--------------------------------------------------*
 *     static function   store_touch()              *
 *--------------------------------------------------*/

static void store_touch( struct touchxy *pt ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Stores touch point into a touch point struct   *
 * and calculates the instantaneous average touch   *
 * coordinates and the distance between this touch  *
 * point's coordinate and the touches average coor- *
 * dinate.                                          *
 *                                                  *
 *--------------------------------------------------*/

    pt->xt = xt;
    pt->yt = yt;
    pt->ut = ut;

    xsum += xt;
    ysum += yt;
    xcen = xsum / (long) num_touch;
    ycen = ysum / (long) num_touch;

    dist2 = (xt - xcen) * (xt - xcen) +
            (yt - ycen) * (yt - ycen);
    return;
}

/*--------------------------------------------------*
 *     static function   unbuffered()               *
 *--------------------------------------------------*/

static struct touchxy *unbuffered( boolean *button ) {

/*--------------------------------------------------*
 * Purpose:                                         *
 *                                                  *
 *   Returns an unbuffered touch point.             *
 * Additionally, it sets the button                 *
 * flag if it is determined that the touch's        *
 * distance exceeds the button's radius.            *
 *                                                  *
 *--------------------------------------------------*/

    num_touch++;
    store_touch( ptub );

    if ( dist2 > radius2 ) {

        is_button = FALSE;   /* Touch is a button. */
    }
    *button = is_button;

    if ( ut )       /* If untouch, reset function. */
        overflow = FALSE;

    return ptub;
}                                  /* End of file. */
