/*

    /osxptb/trunk/PsychSourceGL/Source/Common/Eyelink/PsychEyelink.c

    PROJECTS: Eyelink 
  
    AUTHORS:

        cburns@berkeley.edu             cdb
        E.Peters@ai.rug.nl              emp
        f.w.cornelissen@med.rug.nl      fwc
        mario.kleiner@tuebingen.mpg.de  mk
        li@sr-research.com              lj
 
    PLATFORMS:    All.

    HISTORY:

        11/22/05  cdb        Created.
        29/06/06  fwc        Fixed EyelinkSystemIsConnected to allow dummy mode connections
        15/03/09  mk         Added experimental support for eye camera image display.
        12/20/13  lj         Fixed PsychEyelinkParseToString to allow space between % ;
                             modified  getMouseState to limit mouse cursor inside of camera image.

*/

#include "PsychEyelink.h"
#include <math.h>

/////////////////////////////////////////////////////////////////////////
// Global variables used throughout eyelink C files

int giSystemInitialized = 0;

// Level of verbosity: Do only query via Verbosity() accessor function outside this file!
static int verbosity = 2;

// Callback string for eyelink display callback function:
static char eyelinkDisplayCallbackFunc[1024];

// Memory pointer to malloc()'ed image pixel buffer that holds the
// image data for a RGBA8 texture with the most recent eye camera image:
static byte* eyeimage = NULL;

// Width x Height of eye camera image in pixels:
static int eyewidth  = 0;
static int eyeheight = 0;

// Color remapping palette table:
static unsigned int palmap32[256];
#define ERR_BUFF_LEN 1000

/* Declaration of callback functions defined later in this file: */
static INT16 ELCALLBACK  PsychEyelink_setup_image_display(INT16 width, INT16 height);
static void ELCALLBACK   PsychEyelink_exit_image_display(void);
static void ELCALLBACK   PsychEyelink_set_image_palette(INT16 ncolors, byte r[130], byte g[130], byte b[130]);
static void ELCALLBACK   PsychEyelink_draw_image_line(INT16 width, INT16 line, INT16 totlines, byte *pixels);

static INT16  ELCALLBACK PsychEyelink_setup_cal_display(void);
static void ELCALLBACK   PsychEyelink_exit_cal_display(void);
static void ELCALLBACK   PsychEyelink_clear_display(void);
static void ELCALLBACK   PsychEyelink_draw_cal_target(INT16 x, INT16 y);
static void ELCALLBACK   PsychEyelink_erase_cal_target(void);
static void ELCALLBACK   PsychEyelink_image_title(INT16 threshold, char *title);
static INT16 ELCALLBACK  PsychEyelink_get_input_key(InputEvent *keyinput);
static void ELCALLBACK   PsychEyelink_alert_printf_hook(const char *msg);
static void ELCALLBACK   PsychEyelink_noop(void);

static void ELCALLBACK   PsychEyelink_cal_target_beep_hook(void);
static void ELCALLBACK   PsychEyelink_cal_done_beep_hook(INT16 error);
static void ELCALLBACK   PsychEyelink_dc_done_beep_hook(INT16 error);
static void ELCALLBACK   PsychEyelink_dc_target_beep_hook(void);

/////////////////////////////////////////////////////////////////////////
// Check if system is initialized
//
PsychError EyelinkSystemIsConnected(void)
{
    int iStatus=-9999;
    iStatus=eyelink_is_connected();
//    mexPrintf("EyelinkSystemIsConnected status %d ((iStatus==0)=%d)\n", iStatus, (iStatus==0) );
    if (iStatus==0) {
        PsychErrorExitMsg(PsychError_user, "Eyelink system is not connected!\n");
    }
/*    
    if (eyelink_is_connected()==0) {
        PsychErrorExitMsg(PsychError_user, "Eyelink system is not connected!\n");
    }
    */
    return(PsychError_none);
}

/////////////////////////////////////////////////////////////////////////
// Check is system is initialized
//
PsychError EyelinkSystemIsInitialized(void)
{
    if (giSystemInitialized != 1) {
        PsychErrorExitMsg(PsychError_user, "Eyelink system is not initialized!\n");
    }
    return(PsychError_none);
}

/* Eyelink('Verbosity') - Set level of verbosity.
 */
PsychError EyelinkVerbosity(void) 
{
     static char useString[] = "oldlevel = Eyelink('Verbosity' [,level]);";
    static char synopsisString[] = 
        "Set level of verbosity for error/warning/status messages. 'level' optional, new level "
        "of verbosity. 'oldlevel' is the old level of verbosity. The following levels are "
        "supported: 0 = Shut up. 1 = Print errors, 2 = Print also warnings, 3 = Print also some info, "
        "4 = Print more useful info (default), >5 = Be very verbose (mostly for debugging the driver itself). ";        
    static char seeAlsoString[] = " ";     

    int level= -1;

    // Setup online help: 
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none); };
    
    PsychErrorExit(PsychCapNumInputArgs(1));     // The maximum number of inputs
    PsychErrorExit(PsychRequireNumInputArgs(0)); // The required number of inputs    
    PsychErrorExit(PsychCapNumOutputArgs(1));     // The maximum number of outputs

    PsychCopyInIntegerArg(1, kPsychArgOptional, &level);
    if (level < -1) PsychErrorExitMsg(PsychError_user, "Invalid level of verbosity provided. Valid are levels of zero and greater.");
    
    // Return current/old level:
    PsychCopyOutDoubleArg(1, kPsychArgOptional, (double) verbosity);

    // Set new level, if one was provided:
    if (level > -1) verbosity = level;

    return(PsychError_none);
}

// Return level of verbosity:
int Verbosity(void) {
    return(verbosity);
}

// Parse printf() style format string and variable number of
// integer or string arguments into a printf() formatted
// string and return static pointer to the final string.
// Used, e.g., by Eyelink('Command') and Eyelink('Message'):
const char* PsychEyelinkParseToString(int startIdx)
{
    static char            strCommand[256];
    int                i            = 0, j=0;
    int                iNumInArgs        = 0;
    PsychArgFormatType            psychArgType            = PsychArgType_none;
    int                             iTempValue              = 0;
    char                *pstrTemp        = NULL;
    char                *pstrFormat        = NULL;
    char                            strFragment[256];
    char                            fSpec[256];
    int                             wIdx = 0;
    int                             argIdx;

    // Alloc and grab the input format string
    PsychAllocInCharArg(startIdx, TRUE, &pstrFormat);
    iNumInArgs = PsychGetNumInputArgs();   

    // Define start index of variable argument list:
    argIdx = startIdx + 1;

    // Clear strings
    memset(strCommand, 0, sizeof(strCommand));

    // Parse complete format string:
    while ((*pstrFormat != 0) && (wIdx < 255)) {
      // Special character % detected?
      if ((*pstrFormat != '%') || (*(pstrFormat+1) == '%')) {
        // Easy: Regular char or escaped %. Just copy into target command string:

        // Eat up the escape '%' character, if any:
        if (pstrFormat == strstr(pstrFormat, "%%")) pstrFormat++;

        // Copy escaped single % or regular character:
        strCommand[wIdx++] = *(pstrFormat++);

        // Next character...
        continue;
      }

      // Special % char detected, which is not escaped, therefore
      // a datatype format specifier follows immediately:

      // Is there an argument available to match the format string spec?
      if (iNumInArgs < argIdx) {
        PsychErrorExitMsg(PsychError_user, "Number of supplied arguments does not match number of arguments required by format string!");
      }

      // Find end of actual parameter spec:
      for (i = 0; (pstrFormat[i] > 0) && (pstrFormat[i] != '%'); i++) {};
      for (j = i+1; (pstrFormat[j] > 0) && (pstrFormat[j] != ' ') && (pstrFormat[j]!='%'); j++) {};

      // Copy format substring to fSpec:
      memset(fSpec, 0, sizeof(fSpec));
      strncpy(fSpec, pstrFormat, ((j-i) < 256) ? (j-i) : 255);
      
      // Prepare output substring for writing:
      memset(strFragment, 0, sizeof(strFragment));

      // Check if input argument type matches parameter spec string
      // and assign, if so, abort otherwise:
      psychArgType = PsychGetArgType(argIdx);
      switch(psychArgType) {
        case PsychArgType_double:
          if ((PsychGetArgM(argIdx) == 1) && (PsychGetArgN(argIdx) == 1)) {
        PsychCopyInIntegerArg(argIdx, TRUE, &iTempValue);
        
        // Got a int value. Was a int value expected?
        if (strstr(fSpec, "d") || strstr(fSpec, "i")) {
          // Yes: Print into output string fragment:
          snprintf(strFragment, 255, fSpec, iTempValue);
        } else {
          // No: This is a mismatch - Game over:
          PsychErrorExitMsg(PsychError_user, "Mismatch between provided scalar integer argument and expected argument!");
        }
          } else {
        PsychGiveHelp();
        PsychErrorExitMsg(PsychError_user, "");
          }
          break;

        case PsychArgType_char:
          PsychAllocInCharArg(argIdx, TRUE, &pstrTemp);
          // Got a string. Was a string expected?
          if (strstr(fSpec, "s")) {
        // Yes: Print into output string fragment:
        snprintf(strFragment, 255, fSpec, pstrTemp);
          } else {
        // No: This is a mismatch - Game over:
        PsychErrorExitMsg(PsychError_user, "Mismatch between provided character string and expected argument!");
          }
          break;

        default:
          PsychGiveHelp();
          PsychErrorExitMsg(PsychError_user, "");
          break;
      }
      
      // If we made it here, then the strFragment is ready for
      // joining:
      if ((strlen(strCommand) + strlen(strFragment)) < 256) {
        strcat(strCommand, strFragment);
      } else {
        // Break out of parser - Need to truncate:
        break;
      }

      // Advance parse positions:
      wIdx = strlen(strCommand);
      pstrFormat += j;
      argIdx++;

      // Next parse iteration.
    }

    // Sanity check:
    if (*pstrFormat != 0) printf("Eyelink-Warning:Final overall command truncated to '%s'!\nMaximum of 255 characters allowed.\n", strCommand);

    // Return pointer to internally statically allocated final character string:
    return(strCommand);
}

// Initialize all callback hook functions for use by Eyelink runtime, e.g.,
// all the callbacks for eye camera image display:
void PsychEyelink_init_core_graphics(const char* callback)
{
    HOOKFCNS fcns;
    
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_init_core_graphics()\n");
    
    memset(&fcns, 0, sizeof(fcns));
    
    // Setup cam image callbacks:
    fcns.setup_image_display_hook = PsychEyelink_setup_image_display;
    fcns.exit_image_display_hook  = PsychEyelink_exit_image_display;
    fcns.set_image_palette_hook   = PsychEyelink_set_image_palette;
    fcns.image_title_hook         = PsychEyelink_image_title;
    fcns.draw_image_line_hook     = PsychEyelink_draw_image_line;

    // Setup calibration callbacks:
    fcns.setup_cal_display_hook = PsychEyelink_setup_cal_display;
    fcns.exit_cal_display_hook  = PsychEyelink_exit_cal_display;
    fcns.clear_cal_display_hook = PsychEyelink_clear_display;
    fcns.draw_cal_target_hook   = PsychEyelink_draw_cal_target;
    fcns.erase_cal_target_hook  = PsychEyelink_erase_cal_target;

    // Setup keyboard and I/O callbacks:
    fcns.get_input_key_hook     = PsychEyelink_get_input_key;
    fcns.alert_printf_hook      = PsychEyelink_alert_printf_hook;

    // Set auditory feedback callbacks:
    fcns.cal_target_beep_hook   = PsychEyelink_cal_target_beep_hook;
    fcns.cal_done_beep_hook     = PsychEyelink_cal_done_beep_hook;
    fcns.dc_target_beep_hook    = PsychEyelink_dc_target_beep_hook;
    fcns.dc_done_beep_hook      = PsychEyelink_dc_done_beep_hook;

    // Just set this to make eyelink-core happy:
    fcns.record_abort_hide_hook = PsychEyelink_noop;

    // Assign runtime environment display callback function:
    memset(eyelinkDisplayCallbackFunc, 0, sizeof(eyelinkDisplayCallbackFunc));

    snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback);
//    #if PSYCH_SYSTEM != PSYCH_WINDOWS
//    snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback);
//    #else
//    _snprintf(eyelinkDisplayCallbackFunc, sizeof(eyelinkDisplayCallbackFunc) - 1, "%s", callback);
//    #endif
    
    // Assign hooks to Eyelink runtime:
    setup_graphic_hook_functions(&fcns);
    
    // Optionally dump the whole hookfunctions struct:
    if (Verbosity() > 5) PsychEyelink_dumpHookfunctions();
    
    return;
}

// Disable all hook functions at shutdown time:
void PsychEyelink_uninit_core_graphics(void)
{
    HOOKFCNS fcns;
    
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_uninit_core_graphics()\n");
    
    memset(&fcns, 0, sizeof(fcns));
    setup_graphic_hook_functions(&fcns);


    // Fake callback to cleanup runtime
    PsychEyelinkCallRuntime(-1, 0, 0, "");

    // Optionally dump the whole hookfunctions struct:
    if (Verbosity() > 5) PsychEyelink_dumpHookfunctions();

    return;
}

void PsychEyelink_dumpHookfunctions(void)
{
    HOOKFCNS* pfcns = get_all_hook_functions();
    int i;
    
    printf("PsychEyelink: Dump of current Eyelink HOOKFCNS struct as byte array:\n\n");
    for (i=0; i < sizeof(HOOKFCNS); i++) printf(" %02x", (int)(((unsigned char*) pfcns)[i]));
    printf("\nPsychEyelink: Dump done\n\n");

    return;
}

void PsychEyelink_TestEyeImage(void)
{
    int i, x, y;
    byte r[256], g[256], b[256];
    byte scanline[640];
    InputEvent keyinput;
    
    // Pseudo-Eyelink camera image test:
    
    // Setup pseudo eye-display of 640 x 480 pixels via setup callback:
    PsychEyelink_setup_image_display(640, 480);
    
    // Build pseudo color LUT:
    for (i=0; i < 256; i++) {
        r[i]=i;
        g[i]=255 - i;
        b[i]=i * 2;
    }
    PsychEyelink_set_image_palette(256, r, g, b);

    // Set image title:
    PsychEyelink_image_title(1, "Foobar-O-Matic:");

    // Calibration beep:
    PsychEyelink_cal_target_beep_hook();
    
    // Run pseudo-display loop for 600 frames:
    for (i = 0; i < 600; i++) {
        // Draw calibration target:
        PsychEyelink_draw_cal_target(i, 200);

        // Fill buffer with image pattern:
        for (y=1; y <= 480; y++) {
            // Build y'th scanline:
            for (x=0; x < 640; x++) scanline[x] = (byte) ((x + y + i) % 256);
            
            // Submit y'th scanline:
            PsychEyelink_draw_image_line(640, y, 480, (byte*) &scanline);
        }
        
        // Check keyboard:
        keyinput.key.key = 0;
        if (PsychEyelink_get_input_key(&keyinput) > 0) {
            PsychEyelink_alert_printf_hook("Eyelink: Key detected.\n");
            // Break out of loop on keycode 41 or 27 == ESCAPE on OS/X or Windows.
            if (keyinput.key.key == 41 || keyinput.key.key == 27) break;
            if (keyinput.key.key == TERMINATE_KEY) {
                printf("Eyelink: TestSuite: WARNING: Abort code detected. Master abort.\n");
                break;
            }
        }
    }

    // Calibration end beep hook:
    PsychEyelink_cal_done_beep_hook(keyinput.key.key);
    
    // Tear down pseudo display:
    PsychEyelink_exit_image_display();

    // Test calibration display:
    PsychEyelink_setup_cal_display();
    PsychEyelink_dc_target_beep_hook();
    PsychEyelink_clear_display();
    PsychEyelink_dc_done_beep_hook(keyinput.key.key);
    
    PsychEyelink_exit_cal_display();

    return;
}

int PsychEyelinkCallRuntime(int cmd, int x, int y, char* msg)
{
    PsychGenericScriptType    *inputs[2];
    PsychGenericScriptType    *outputs[1];
    double* callargs;
    double rc;
    int retc;

    // Callbacks forcefully disabled by error-handling? Return with error code if so:
    if (0 == eyelinkDisplayCallbackFunc[0]) return(0xdeadbeef);

    // Create a Matlab double matrix with 4 elements: 1st is command code 
    // others are available for use specific to each command
    outputs[0]  = NULL;
    inputs[0]   = mxCreateDoubleMatrix(1, 4, mxREAL);
    callargs    = mxGetPr(inputs[0]);
    
    callargs[0] = (double) cmd; // Command code.
    callargs[1] = (double) x;
    callargs[2] = (double) y;

    if (msg != NULL) {
        inputs[1] = mxCreateString(msg);
    }
    else {
        inputs[1] = NULL;
    }
    
    // Call the runtime environment:
    if ((retc = Psych_mexCallMATLAB((cmd == 2) ? 1 : 0, outputs, (inputs[1]) ? 2 : 1, inputs, eyelinkDisplayCallbackFunc)) > 0) {
        printf("EYELINK: WARNING! PsychEyelinkCallRuntime() Failed to call eyelink runtime callback function %s [rc = %i]!\n", eyelinkDisplayCallbackFunc, retc);
        printf("EYELINK: WARNING! Make sure that function is on your Matlab/Octave path and properly initialized.\n");
        printf("EYELINK: WARNING! May also be an error during execution of that function. Type ple at command prompt for error messages.\n");
        printf("EYELINK: WARNING! Auto-Disabling all callbacks to the runtime environment for safety reasons.\n");
        eyelinkDisplayCallbackFunc[0] = 0;
    }

    // Release our matrix again:
    mxDestroyArray(inputs[0]);
    if (msg != NULL) mxDestroyArray(inputs[1]);
    
    if (outputs[0]) {
        rc = mxGetScalar(outputs[0]);
        mxDestroyArray(outputs[0]);
    }
    else {
        rc = 0;
    }
    
    return((int) rc);
}

// Callback functions, called by Eyelink runtime at various occassions, e.g,
// during tracker setup, drift correction/calibration etc.:
// =========================================================================

static void ELCALLBACK PsychEyelink_noop(void)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_noop()\n");
    
    // Done.
    return;
}

// PsychEyelink_setup_image_display() tells the width and height of the camera
// image in pixels.
static INT16 ELCALLBACK PsychEyelink_setup_image_display(INT16 width, INT16 height)
{
    
    
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_setup_image_display()\n");
    // Release any stale image buffer:
    if (eyeimage != NULL) free(eyeimage);
    
    // Reset everything to startup default:
    eyeimage  = NULL;
    eyewidth  = 0;
    eyeheight = 0;
    
    if (width < 1 || height < 1) {
        printf("EYELINK: WARNING! Invalid image dimensions (smaller than 1 pixel!) received from eyelink: Aborting image setup.\n");
        return(-1);
    }

    // Allocate an internal memory buffer of sufficient size to hold an image
    // of size width x height pixels:
    eyeimage = (byte*) malloc(sizeof(unsigned char) * 4 * width * height);
    if (eyeimage != NULL) {
        eyewidth  = width;
        eyeheight = height;
    }
    else {
        // Failed:
        return(-1);
    }
    
    // Tell callback about image dimensions fwiw:
    if (0xdeadbeef == PsychEyelinkCallRuntime(8, eyewidth, eyeheight, NULL)) {
        // Error condition. Return error to eyelink runtime:
        return(-1);
    }
    
    if (Verbosity() > 5) printf("Eyelink: Leaving PsychEyelink_setup_image_display()\n");

    // Done.
    return(0);
}

// PsychEyelink_exit_image_display() shuts down any camera image display:
static void ELCALLBACK PsychEyelink_exit_image_display(void)
{
    
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_exit_image_display()\n");

    // Release any allocated image buffer:
    if (eyeimage != NULL) free(eyeimage);
    
    // Reset everything to startup default:
    eyeimage  = NULL;
    eyewidth  = 0;
    eyeheight = 0;
    
    // Tell runtime to exit display: Command code 9.
    PsychEyelinkCallRuntime(9, 0, 0, NULL);
    
    // Done.
    return;
}
// added by NJ @ SR Research Sept 2010
#define UPSIDE 0
#define LEFTSIDE 1
#define RIGHTSIDE 2
#define DOWNSIDE 3
void drawSemiCircle(CrossHairInfo *chi, int left, int top, int dia, int side, int cindex)
{
    
    

    unsigned char r =0;
    unsigned char g =0;
    unsigned char b =0;
    int radius = dia/2;
    int x = left - 1;
    int y = top -1;
    unsigned int *v0;
    int x0,y0, ddF_x =1, ddF_y,f;
    

    if (eyeimage == NULL){
        return;
    }

    switch(cindex)
    {
        case CR_HAIR_COLOR:          r=g=b =255; break;//255,255,255
        case PUPIL_HAIR_COLOR:       r=g=b =255; break;//255,255,255
        case PUPIL_BOX_COLOR:             g =255; break;//0,255,0
        case SEARCH_LIMIT_BOX_COLOR: 
        case MOUSE_CURSOR_COLOR:        r = 255; break;//255,0,0
    }
    
    v0 = (unsigned int*) (eyeimage);
    
    // implement manual clipping to mimic behavior on host pc
    
    if(side == UPSIDE)
    {
        
        x0 = left;
        y0 = top;
        radius = dia / 2;    
        y= radius;
        f = 1 - radius;
        ddF_y = -2 * radius;
        x =  0;
        y = radius;
        x0 = x0 + dia/2;
        y0 = y0 + dia/2;

        
        if (y0 < eyeheight && y0 > 0 && y0-radius < eyeheight && y0-radius > 0 && y0-radius < eyeheight &&  x0+radius < eyewidth && x0-radius >0 && x0+radius > 0 && x0-radius < eyewidth){
            v0[(eyewidth*eyeheight) - (y0-radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        }
        
        while( x < y ){     
            
            if(f >= 0){
                y--;
                ddF_y += 2;
                f += ddF_y;
                
            }
            x++;
            ddF_x += 2;
            f += ddF_x;
            

            if (y0+y >0 && y0+y < y0 && y0+y < eyeheight) {
                
                if(x0+x < eyewidth)
                v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if (x0-x > 0 )
                v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (y0-y >0 && y0-y < y0 && y0-y < eyeheight){//if (y0-y < y0 && y0-y < eyeheight){
                
                if (x0+x < eyewidth)
                v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if (x0-x > 0 )
                v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (y0+x >0 && y0+x < y0 && y0+x < eyeheight){//if (y0+x < y0 && y0+x < eyeheight){
                
                if (x0+y < eyewidth)
                v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if((x0-y) >0)
                v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (y0-x > 0 && y0-x < y0 && y0-x < eyeheight){//if (y0-x < y0 && y0-x < eyeheight){
            
                if(x0+y < eyewidth)
                v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(x0-y > 0)
                v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
        }
    }else if (side == DOWNSIDE){
        
        radius = dia / 2;
        y= radius;
        f = 1 - radius;
        ddF_y = -2 * radius;
        ddF_x =1;
        x =  0;
        y = radius;
        
        x0 = left;
        y0 = top;
        x0 = x0 + dia/2;
        
        if (y0+radius < eyeheight && y0+radius > 0 && x0 > 0 && x0 < eyewidth)
            v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        if (y0 < eyeheight && y0 > 0 && x0+radius > 0 && x0+radius < eyewidth)
            v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        if (y0 < eyeheight && y0 > 0 && x0-radius > 0 && x0-radius < eyewidth)
            v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        
        
        while( x < y ){     
            
            if(f >= 0){
                y--;
                ddF_y += 2;
                f += ddF_y;
                
            }
            x++;
            ddF_x += 2;
            f += ddF_x;
            
            if (y0+y >0 && y0+y > y0 && y0+y < eyeheight) {
                if ( x0+x < eyewidth)
                    v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if ( x0-x > 0 )
                    v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (y0-y > 0 && y0-y > y0 && y0-y < eyeheight){
                if ( x0+x < eyewidth)
                    v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if ( x0-x > 0 )    
                    v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (y0+x >0 && y0+x > y0 && y0+x < eyeheight){
                if ( x0+y < eyewidth )
                    v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if ( x0-y > 0)
                    v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (y0-x  >0 && y0-x > y0 && y0-x < eyeheight){
                if ( x0+y < eyewidth)
                    v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if (x0-y > 0)
                    v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
        }
        
        
    }else if (side == RIGHTSIDE){
        
        radius = dia / 2;
        y= radius;
        f = 1 - radius;
        ddF_y = -2 * radius;
        ddF_x =1;
        x =  0;
        y = radius;
        
        x0 = left;
        y0 = top;
        y0 = y0 + dia/2;
                
        
        if (x0 + radius < eyewidth && x0+radius > 0){
            v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        }
        
        while( x < y ){     
            
            if(f >= 0){
                y--;
                ddF_y += 2;
                f += ddF_y;
                
            }
            x++;
            ddF_x += 2;
            f += ddF_x;
            
            if (x0+x  >0 && x0+x > x0 && x0+x < eyewidth) {
                if(y0+x < eyeheight)
                    v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(y0-x > 0)
                    v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                
            }
            if (x0-x > 0 && x0-x > x0 && x0-x < eyewidth){
                if(y0+y < eyeheight)
                    v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(y0-y > 0)
                    v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (x0+y > 0 && x0+y > x0 && x0+y < eyewidth){
                if(y0+x < eyeheight)
                    v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(y0-x > 0)
                    v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);            
            }
            if (x0-y > 0 && x0-y > x0 && x0-y < eyewidth){
                if(y0+x < eyeheight)
                    v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(y0-x > 0)
                    v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
        }
        
    }else if (side == LEFTSIDE){
        
        radius = dia / 2;
        y= radius;
        f = 1 - radius;
        ddF_y = -2 * radius;
        ddF_x =1;
        x =  0;
        y = radius;
        
        x0 = left;
        y0 = top;
        y0 = y0 + dia/2;
        x0 = x0 + dia/2; 

        if (x0 - radius > 0 && x0-radius < eyewidth){
        v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        }
        
        while( x < y ){     
            
            if(f >= 0){
                y--;
                ddF_y += 2;
                f += ddF_y;
                
            }
            x++;
            ddF_x += 2;
            f += ddF_x;
            
            if (x0+x > 0 && x0+x < x0 && x0+x > 0) {
                v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);                
            }
            if (x0-x > 0 && x0-x < x0 && x0-x > 0){
                if(y0+x < eyeheight)
                    v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(y0-x > 0)    
                    v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            if (x0+y > 0 && x0+y < x0 && x0+y > 0){
                if(y0+x < eyeheight)
                    v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(y0-x > 0)
                    v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);            
            }
            if (x0-y > 0 && x0-y < x0 && x0-y > 0){
                if(y0+x < eyeheight)
                    v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if(y0-x > 0)
                    v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
        }
        
    }
    return;
}

// lack of graphics functions use primitive algorithm to draw circle by coloring in rgb in image memory 
void drawCircle(CrossHairInfo *chi, int x0, int y0, int width, int height, int cindex)
{
    
    

    unsigned char r =0;
    unsigned char g =0;
    unsigned char b =0;
    int x = 0, y, f;
    unsigned int *v0;
    int radius, ddF_x =1, ddF_y;
    
    if (eyeimage == NULL) return;

    switch(cindex){
        case CR_HAIR_COLOR:          r=g=b = 255; break;//255,255,255
        case PUPIL_HAIR_COLOR:       r=g=b = 255; break;//255,255,255
        case PUPIL_BOX_COLOR:             g = 255; break;//0,255,0
        case SEARCH_LIMIT_BOX_COLOR: 
        case MOUSE_CURSOR_COLOR:         r = 255; break;//255,0,0
    }

    
    v0 = (unsigned int*) (eyeimage);
    
    radius = width / 2;    
    y= radius;
    f = 1 - radius;
    
    ddF_y = -2 * radius;
    x =  0;
    y = radius;
    x0 = x0 + width/2;
    y0 = y0 + width/2;

    if (x0 - radius > 0 && x0+radius < eyewidth && y0+radius < eyeheight && y0-radius > 0){
        v0[(eyewidth*eyeheight) - (y0+radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        v0[(eyewidth*eyeheight) - (y0-radius)*eyewidth+x0] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        v0[(eyewidth*eyeheight) - y0*eyewidth+(x0+radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        v0[(eyewidth*eyeheight) - y0*eyewidth+(x0-radius)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
    }
    
    while( x < y ){     
        
        if(f >= 0){
            y--;
            ddF_y += 2;
            f += ddF_y;            
        }
        x++;
        ddF_x += 2;
        f += ddF_x;        
        if (y0+y < eyeheight && y0+y > 0){
            if(x0+x < eyewidth)
                v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            if(x0-x  > 0)
                v0[(eyewidth*eyeheight) - (y0+y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        }
        if (y0-y < eyeheight && y0-y > 0){
            if(x0+x < eyewidth)    
                v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0+x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            if(x0-x  > 0)
                v0[(eyewidth*eyeheight) - (y0-y)*eyewidth+(x0-x)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        }
        if(y0+x < eyeheight && y0+x > 0){
            if(x0+y < eyewidth)
                v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            if(x0-y >0)
                v0[(eyewidth*eyeheight) - (y0+x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
        }
        if(y0-x < eyeheight && y0-x > 0){
            if(x0+y < eyewidth)
                v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0+y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            if(x0-y >0)
                v0[(eyewidth*eyeheight) - (y0-x)*eyewidth+(x0-y)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);        
        }
    }
    return;
}
// added by NJ @ SR Research Sept 2010
void drawLozenge(CrossHairInfo *chi, int x0, int y0, int width, int height, int cindex)
{
    
    unsigned char r =0;
    unsigned char g =0;
    unsigned char b =0;
    int x = 0, y;
    int y2, y1;
    unsigned int *v0;
    
    if (eyeimage == NULL) return;
    // clip to prevent memory issues and wrap around
        
    switch(cindex)    {
    
        case CR_HAIR_COLOR:          r=g=b = 255; break;//255,255,255
        case PUPIL_HAIR_COLOR:       r=g=b = 255; break;//255,255,255
        case PUPIL_BOX_COLOR:             g = 255; break;//0,255,0
        case SEARCH_LIMIT_BOX_COLOR: 
        case MOUSE_CURSOR_COLOR:         r = 255; break;//255,0,0
    }
    
if(eyeimage != NULL) {
    
    // Retrieve v0 as pointer to pixel row in output buffer:
    v0 = (unsigned int*) (eyeimage);
    
    // is it a circle?
    if(abs(width - height) < 4) 
    {
        drawCircle(chi, x0, y0, width, height, cindex);
            
    }else { // non. ligne
        
        int minwidth = width * (width < height) + height * (width >= height); //min(width,height);
        if (width == minwidth) // width was smaller
        {            
            y1 = y0+width/2;
            x = x0;
            y2 = y1+(height-width) + 1;
            // only draw vertical lines
            for (y=y1 ;y< y2;y++) {
                // be careful to clip or memory error occurs
                if (y < eyeheight && y > 0 && x > 0 && x < eyewidth)
                    v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                if (y < eyeheight && y > 0 && (x+ width) > 0 && (x+width) < eyewidth)
                    v0[(eyewidth*eyeheight) - y*eyewidth+(x+width)] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
                
            }    // Now draw the semi circles
            drawSemiCircle(chi,x0,y0,width,UPSIDE,cindex);
            drawSemiCircle(chi,x0,(y0+width/2+(height-width)),width,DOWNSIDE,cindex);
            
            
        }else{ // height smaller
            
            int x1 = x0+height/2;
            int x2 = x1+(width-height);
            y=y0;             
            //horizontal            
            for (x=x1 ;x<= x2;x++) {
                if ( x < eyewidth && x > 0 && y > 0 && y < eyeheight)
                    v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
            
            y = y0+height;
            for (x=x1 ;x<= x2;x++) {
                if ( x < eyewidth && x > 0 && y < eyeheight && y > 0)
                    v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
                        
            drawSemiCircle(chi,(x0+height/2+(width-height)),y0,height,RIGHTSIDE,cindex);
            drawSemiCircle(chi,x0,y0,height,LEFTSIDE,cindex);
            
        }    
    }
}
    return;
}

// added by NJ @ SR Research LTD
void drawLine(CrossHairInfo *chi, int x1, int y1, int x2, int y2, int cindex)
{
    
    unsigned char r =0;
    unsigned char g =0;
    unsigned char b =0;
    int dx, dy;
    int x, y, ch;
    INT16 xc[4],yc[4], enabled;
    unsigned int *v0;
    int xx1, xx2, yy1, yy2;
    
    if (eyeimage == NULL) return;
    // get camera channel. 2 = head.
    ch = get_image_xhair_data(xc, yc, &enabled);
    
    // clip if fail 
    if (ch == 2){
                
        if (x1<0) x1=0;
        if (x2<0) x2=0;
        if (y1<0) y1=0;
        if (y2<0) y2=0;
        
        if (x1>eyewidth-1) x1=eyewidth-1;
        if (x2>eyewidth-1) x2=eyewidth-1;
        if (y1>eyeheight-1) y1=eyeheight-1;
        if (y2>eyeheight-1) y2=eyeheight-1;
    }
    
    
    switch(cindex)
    {
        case CR_HAIR_COLOR:          r=g=b = 255; break;//255,255,255
        case PUPIL_HAIR_COLOR:       r=g=b = 255; break;//255,255,255
        case PUPIL_BOX_COLOR:             g = 255; break;//0,255,0
        case SEARCH_LIMIT_BOX_COLOR: 
        case MOUSE_CURSOR_COLOR:         r = 255; break;//255,0,0
    }
    // Memory pointer to malloc()'ed image pixel buffer that holds the
    // image data for a RGBA8 texture with the most recent eye camera image:
    // eyeimage is a global variable
    if(eyeimage != NULL) {
        
        // Retrieve v0 as pointer to pixel row in output buffer: image is upside down
        v0 = (unsigned int*) ( eyeimage);                
        
        dx = x2 - x1;
        dy = y2 - y1;            
        
        
        if(ch != 2){
            // never diagonal here y1 is always < y2
            for (y=y1 ;y< y2;y++) {
                x = x1 + (dx) * (y - y1)/(dy);    
                if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0) 
                    v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }        
        }
        
        //account for diagonal lines in binocular mode ( x1 and y1 may be > x2 and y2)
        
        // vertical 
        if (dx == 0 ){            
            if(y1>y2){
                yy1 = y2;
                yy2 = y1;
            }else {
                yy1 = y1;
                yy2 = y2;
            }
                
            for (y=yy1 ;y< yy2;y++) {
                x = x1 + (dx) * (y - y1)/(dy);            
                if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0)
                    v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }        
        }
        else  {    
            if(x1>x2){        
                xx1 = x2;
                xx2 = x1;
            }else {
                xx1 = x1;
                xx2 = x2;
            }
    
            for (x=xx1 ;x< xx2;x++) {
                y = y1 + (dy) * (x - x1)/(dx);
                if (y < eyeheight && x < eyewidth && y >0 && x >0 && (eyewidth*eyeheight) - y*eyewidth+x > 0)
                    v0[(eyewidth*eyeheight) - y*eyewidth+x] = 0xFF000000 | ((unsigned int) b << 16) | ((unsigned int) g << 8) | ((unsigned int) r);
            }
        }
    }    
    return;
}


typedef void  (*GET_MOUSE_LOC)(CrossHairInfo *dt, int *x, int *y, int *state);
GET_MOUSE_LOC mouseLoc = NULL;

void  ELCALLTYPE  set_mouse_loc_callback(GET_MOUSE_LOC get_mouse_loc)
{
    mouseLoc = get_mouse_loc;
}


void getMouseState(CrossHairInfo *chi, int *rx, int *ry, int *rstate)
{
    float x =0;
    float y =0;
    PsychGenericScriptType            *inputs[1];
    PsychGenericScriptType            *outputs[1];
    double* callargs;
    double* outputargs;
    float ar[7];
    float w,h;
    int i;
    
    inputs[0]   = mxCreateDoubleMatrix(1, 4, mxREAL);
    callargs    = mxGetPr(inputs[0]);
    
    callargs[0] = 16; // 16 == Command code for mouse button event    
    
    Psych_mexCallMATLAB(1, outputs, 1, inputs, eyelinkDisplayCallbackFunc);
    
    outputargs = mxGetData(outputs[0]);
    for (i=0;i<7;i++){
        ar[i] = (int) outputargs[i];
    }
    
    // Release our matrix again:
    mxDestroyArray(inputs[0]);
    mxDestroyArray(outputs[0]);
    
    w = ar[0];
    h = ar[1];
    x = floor((ar[2] - ((w/2) - ar[4]/2)) * ((float)eyewidth/ar[4]));
    y = floor((ar[3] - ((h/2) - ar[5]/2)) * ((float)eyeheight/ar[5]));

    if(x>0 && y >0 && x <= eyewidth && y <= eyeheight)
    {
        *rx = (int)x;
        *ry = (int)y;
        *rstate =  (int)ar[6];
    }else
    {
        if(x<=0 && y<=0)
        {
            
            *rx = 1;
            *ry = 1;
        }
        else if(x<0 && y>eyeheight)
        {
            
            *rx = 1;
            *ry = eyeheight;
        }
        else if(x>eyewidth && y>eyeheight)
        {
            
            *rx = eyewidth;
            *ry = eyeheight;
        }
        else if(x>eyewidth && y<0)
         {
            
            *rx = eyewidth;
            *ry = 1;
        }
        else if(x>eyewidth && y>0 && y<eyeheight)
        {
            
            *rx = eyewidth;
            *ry = y;
        }
        else if(x<0 && y>0 && y<=eyeheight)
        {
            
            *rx = 1;
            *ry = y;
        }
        else if(y<0 && x>0 && x<=eyewidth)
        {
            
            *rx = x;
            *ry = 1;
        }
        else if(y>eyeheight && x>0 && x<=eyewidth)
        {
            
            *rx = x;
            *ry = eyeheight;
        }
    }

    return;
}


// PsychEyelink_draw_image_line() retrieves exactly one scanline worth of eye camera
// image data. Once a full image has been received, it has to trigger the actual image
// display:
static void ELCALLBACK PsychEyelink_draw_image_line(INT16 width, INT16 line, INT16 totlines, byte *pixels)
{
    PsychGenericScriptType            *inputs[1];
    PsychGenericScriptType            *outputs[1];
    double* callargs;
    double teximage;
    static INT16 lastline = -1;
    static int wrapcount = 0;
    static double tlastwrap = 0.0;
    double tnow;
    int rc;
    byte* p;
    unsigned int *v0;
    short i;
    CrossHairInfo crossHairInfo;
    
    if (Verbosity() > 8) printf("Eyelink: Entering PsychEyelink_draw_image_line()\n");

    // Callbacks forcefully disabled by error-handling? Simply return with no-op, if so:
    if (0 == eyelinkDisplayCallbackFunc[0]) return;

    // width, line, totlines within valid range?
    if (width < 1 || width > eyewidth || line < 1 || line > eyeheight || totlines < 1 || totlines > eyeheight) {
        printf("EYELINK: WARNING! Eye camera image with invalid parameters received! (width = %i, line = %i, totlines = %i out of sane range %i x %i)!\n",
                width, line, totlines, eyewidth, eyeheight);
        printf("EYELINK: WARNING! Will try to clamp to valid values, but results may be junk.\n");
        width = eyewidth;
        line = (line < 1) ? 1 : line;
        line = (line > eyeheight) ? line : eyeheight;
        totlines = (totlines < 1) ? 1 : totlines;
        totlines = (totlines > eyeheight) ? totlines : eyeheight;
    }

    
    

    // Data structures properly initialized?
    if(eyeimage != NULL) {
        // Retrieve p as pointer to input pixel index color buffer:
        p = pixels;
        
        // Retrieve v0 as pointer to pixel row in output buffer:
        v0 = (unsigned int*) (( eyeimage + ( (totlines - line) * width * 4 ) ));
        
        // Copy one row of pixels from input- to output buffer:
        // This is a bit optimized, but we could do more if we're really bored with life ;-)
        for(i=0; i < width; i++) {
            // Decode pixel index value in *p via palette color lookup table and store to output buffer:
            *(v0++) = palmap32[*p++];
        }

        if (Verbosity() > 8) printf("Eyelink: PsychEyelink_draw_image_line(): Scanline %i received.\n", (int) line);

        // Premature wraparound?
        if (line < lastline) {
            // Premature wraparound due to too slow processing. Increase wrapcounter:
            wrapcount++;            
        }

        // More than some threshold?
        if (wrapcount > 10) {
            // Spill a warning?
            PsychGetAdjustedPrecisionTimerSeconds(&tnow);
            if (tnow - tlastwrap > 2.0) {
                // Last invocation longer than 2 seconds away:
                // Output some warning to console...
                if (Verbosity() > 1) {
                    printf("Eyelink: Warning: Skipped videoframes from eye camera detected within last seconds (count=%i)\n", wrapcount);
                    printf("Eyelink: Warning: Timing problems on your machine or network problems on tracker connection?!?\n\n");
                }

                // Update / Reset detector:
                tlastwrap = tnow;
                wrapcount = 0;
            }                
        }
        
        // Update skip detector:
        lastline = line;

        // Complete new eye image received?
        if (line == totlines) {
            // Yes. Our eyeimage buffer contains a new image.
            
            // Reset skip detector:
            lastline  = -1;
        
            crossHairInfo.w = eyewidth;
            crossHairInfo.h = eyeheight;
            crossHairInfo.drawLozenge = drawLozenge;
            crossHairInfo.drawLine = drawLine;
            crossHairInfo.getMouseState = mouseLoc?mouseLoc:getMouseState;
            crossHairInfo.userdata = eyeimage;
            
            eyelink_draw_cross_hair(&crossHairInfo);
            
            
            // Compute double-encoded Matlab/Octave compatible memory pointer to image buffer:
            teximage = PsychPtrToDouble((void*) eyeimage);

            
            
            
            // Ok, teximage is a memory pointer to our image buffer, encoded as a double.
            // Now we need to call our Matlab callback function which actually converts
            // the data in our internal image buffer into a PTB texture, then draws that
            // texture etc. to display the new eye camera image.
            if (Verbosity() > 6) printf("Eyelink: PsychEyelink_draw_image_line(): All %i Scanlines received. Calling Runtime!\n", (int) line);
            
            // Create a Matlab double matrix with 4 elements: 1st is command code '1'
            // 2nd is the double pointer, 3r//d is image width, 4th is image height:
            outputs[0]  = NULL;
            inputs[0]   = mxCreateDoubleMatrix(1, 4, mxREAL);
            callargs    = mxGetPr(inputs[0]);

            callargs[0] = 1; // 1 == Command code for "Show eye image".
            callargs[1] = teximage;
            callargs[2] = eyewidth;
            callargs[3] = eyeheight;

            rc = Psych_mexCallMATLAB(0, outputs, 1, inputs, eyelinkDisplayCallbackFunc);
            if(rc) {
                printf("EYELINK: WARNING! Failed to call eyelink camera image display callback function %s [rc=%i]!\n", eyelinkDisplayCallbackFunc, rc);
                printf("EYELINK: WARNING! Make sure that function is on your Matlab/Octave path and properly initialized.\n");
                printf("EYELINK: WARNING! May also be an error during execution of that function. Type ple at command prompt for error messages.\n");
                printf("EYELINK: WARNING! Auto-Disabling all callbacks to the runtime environment for safety reasons.\n");
                eyelinkDisplayCallbackFunc[0] = 0;
            }

            // Release our matrix again:
            mxDestroyArray(inputs[0]);
        }
    }
    
    
    
    
    // Done.
    return;
}

// PsychEyelink_set_image_palette() sets the color palette for decoding 1-byte color index
// values in an eyelink camera image into RGB8 1-byte-per-color-component color values:
static void ELCALLBACK PsychEyelink_set_image_palette(INT16 ncolors, byte r[], byte g[], byte b[])
{
    short i;
    
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_set_image_palette()\n");

    if (ncolors > 256) {
        printf("EYELINK: WARNING! Invalid color palette size %i (> 256 colors!) received from eyelink: Clamping to 256 colors.\n", (int) ncolors);
        ncolors = 256;
    }

    // Copy given r,g,b color arrays into internal remapping table palmap32:
    for(i=0; i < ncolors; i++) {
        // Format is ABGR
        palmap32[i] = 0xFF000000 | ((unsigned int) b[i] << 16) | ((unsigned int) g[i] << 8) | ((unsigned int) r[i]);
    }

    return;
}

static INT16  ELCALLBACK PsychEyelink_setup_cal_display(void)
{
    //nj added "hack" to disable flashing instructions in drift correction and to enable sending cal and val results
    int mode = -1;
    
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_setup_cal_display()\n");

    mode = eyelink_tracker_mode();
    
    if (mode == 1 || mode ==9 ) //EL_DRIFT_CORR_MODE)
        if (0xdeadbeef == PsychEyelinkCallRuntime(17, 0, 0, NULL)) {
            // Error condition. Return error to eyelink runtime:
            return(-1);
        }
    
    // Tell runtime to setup calibration display: Command code 7.
    if (0xdeadbeef == PsychEyelinkCallRuntime(7, 0, 0, NULL)) {
        // Error condition. Return error to eyelink runtime:
        return(-1);
    }

    // Return success:
    return(0);
}

static void ELCALLBACK   PsychEyelink_exit_cal_display(void)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_exit_cal_display()\n");

    // Tell runtime to exit calibration display: Command code 11.
    PsychEyelinkCallRuntime(11, 0, 0, NULL);
    return;
}

static void ELCALLBACK   PsychEyelink_clear_display(void)
{
    //NJ modified to add msg to call back 6 with cal and val result
    char strMessage[256];
    int result =-1;
    // Clear strings
    memset(strMessage, 0, sizeof(strMessage));
    
    
    result = eyelink_cal_message(strMessage);
    
    
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_clear_display()\n");

    // Tell runtime to clear display: Command code 6.
    PsychEyelinkCallRuntime(6, 0, 0, strMessage);//NULL);

        
    return;
}

static void ELCALLBACK   PsychEyelink_draw_cal_target(INT16 x, INT16 y)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_draw_cal_target(): x=%i y=%i.\n", (int) x, (int) y);

    // Tell runtime about where to draw calibration target: Command code 5.
    PsychEyelinkCallRuntime(5, (int) x, (int) y, NULL);

    return;
}

static void ELCALLBACK   PsychEyelink_erase_cal_target(void)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_erase_cal_target():\n");

    // Tell runtime about clear calibration target: Command code 10.
    PsychEyelinkCallRuntime(10, 0, 0, NULL);

    return;
}

static void ELCALLBACK   PsychEyelink_image_title(INT16 threshold, char *title)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_image_title(): threshold = %i : Title = %s\n", (int) threshold, title);

    //mexPrintf("C code: %s ... %d\n", title, threshold);
    //fflush(stdout);
    
    // Tell runtime about image title: Command code 4.
    PsychEyelinkCallRuntime(4, (int) threshold, 0, title);
    
    return;
}

#ifndef ELKEY_DOWN
#define ELKEY_DOWN 1 //temporary while we wait for sr-research's lib to get updated with this
#endif

static INT16 ELCALLBACK  PsychEyelink_get_input_key(InputEvent *keyinput)
{
    int ky = 0;
    double tnow;
    static double tlastquery = 0;
    const  double tmininterval = 0.1;    // Allow one query every 0.1 seconds.
    InputEvent *key_input = keyinput;

    // Throttling routine:
    // We don't want key queries to call out to the runtime too often, as this
    // creates a quite significant overhead, e.g., approx. 1 msec for a KbCheck
    // for a fast 2009'ish machine on OS/X!
    PsychGetAdjustedPrecisionTimerSeconds(&tnow);
    if (tnow - tlastquery < tmininterval) {
        // Last invocation less than tmininterval seconds away. Throttle this,
        // we just return "no key pressed".
        if (Verbosity() > 9) printf("Eyelink: In PsychEyelink_get_input_key(): Throttling...\n");
        return(0);
    }
    else {
        // Last invocation longer than tmininterval seconds away. Accept this
        // query and update timestamp:
        tlastquery = tnow;
    }
    
    if (Verbosity() > 7) printf("Eyelink: Entering PsychEyelink_get_input_key()\n");

    // Call runtime for keycode of pressed key (command code 2):
    if ((ky = PsychEyelinkCallRuntime(2, 0, 0, NULL)) == 0xdeadbeef) {
        // Error condition in runtime callback! Can't progress. We try to
        // shutdown the current eyelink runtime operation by sending a fake
        // keycode corresponding to the terminate key:
        if (Verbosity() > 0) printf("Eyelink: In PsychEyelink_get_input_key(): Error condition detected: Trying to send TERMINATE_KEY abort keycode!\n");
        ky = TERMINATE_KEY;
    }
    
    if (ky > 0) {
        // Fill Eyelinks InputEvent struct:
        memset(key_input, 0, sizeof(InputEvent));
        key_input->key.key = ky;
        key_input->key.state = ELKEY_DOWN; 
        key_input->key.type = KEYINPUT_EVENT;
        key_input->key.modifier = 0; //event.key.keysym.mod;
        key_input->key.unicode = 0; //event.key.keysym.unicode;
        
        // One key pressed:
        return(1);
    }
    else {
        // No key pressed:
        return(0);
    }
}

static void ELCALLBACK   PsychEyelink_alert_printf_hook(const char *msg)
{
    // Print error message to runtime console if error output is allowed:
    if (Verbosity() > 3) printf("Eyelink: Alert! Eyelink says: %s.\n\n", msg);
    
    // Tell runtime about alert condition: Command code 3.
    PsychEyelinkCallRuntime(3, 0, 0, (char*) msg);

    return;
}

static void ELCALLBACK   PsychEyelink_cal_target_beep_hook(void)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_cal_target_beep_hook():\n");

    PsychEyelinkCallRuntime(12, 0, 0, NULL);
    return;
}

static void ELCALLBACK   PsychEyelink_dc_target_beep_hook(void)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_dc_target_beep_hook():\n");

    PsychEyelinkCallRuntime(13, 0, 0, NULL);
    return;
}

static void ELCALLBACK      PsychEyelink_cal_done_beep_hook(INT16 error)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_cal_done_beep_hook():\n");

    PsychEyelinkCallRuntime(14, (int) error, 0, NULL);
    return;
}

static void ELCALLBACK      PsychEyelink_dc_done_beep_hook(INT16 error)
{
    if (Verbosity() > 5) printf("Eyelink: Entering PsychEyelink_dc_done_beep_hook():\n");

    PsychEyelinkCallRuntime(15, (int) error, 0, NULL);
    return;
}

