/*
** DEMO.C                     Demonstrate GrIP API usage
*/



#include "grip.h"

#include "player.h"
#include "draw.h"
#include "video.h"
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>



/*
** ScanKey()                  Check if a key is down
**
** Returns -10 if <ESC>ape is pressed, -1 through -10 if <F1> through <F10>
** are pressed, or 1 through 4 for the number keys.  If none of these keys
** are pressed, then returns 0.
*/
int ScanKey(void)
  {
  int c;

  // Check if a key is down
  if (!kbhit())
    return 0;
  if ((c = getch()) == 0)
    c = - getch();

  // Check for <ESC>ape
  if (c == 27)
    return -10;

  // Check for a function key F1 to F10
  if (c <= -59 && c >= -68)
    return c + 58;

  // Check for a number key from 1 to 4
  if (c >= '1' && c <= '4')
    return c - '0';

  return 0;
  }




/*
** SelectSlot(play)           Lets the operator select a slot for a player
**
** Waits for the operator to press a numeric key to specify the slot to
** use.  Aborts if <ESC> or a function key is pressed.
*/
int SelectSlot(int play)
  {
  PLAYER* p = &player[play];
  int i;

  // Start flashing the field
  p->state = STATE_SELECT;

  // Wait for a numeric slot selection
  for (;;)
    {
    // Update the display during the vertical retrace
    WaitVertBlank();
    RefreshDisplay();

    // Refresh the controllers
    GrRefresh(0);

    // Watch for keypresses
    i = ScanKey();

    // Exit on ESC or a function key
    if (i < 0)
      break;

    // If a number was pressed, then set the new slot number
    if (i > 0)
      {
      p->slot = i;
      InitPlayer(play, i);
      break;
      }
    }

  // Restore the field state
  p->state = STATE_NORMAL;

  return i;
  }




/*
** SelectButton(play, lr)     Lets the operator select a button
**
** Waits for the operator to press a button on the controller, and then
** assigns that button to the player.  Aborts if <ESC> or a function key
** is pressed.
*/
int SelectButton(int play, int lr)
  {
  PLAYER* p = &player[play];
  GRIP_BITFIELD buttons;
  GRIP_INDEX index;
  unsigned short state;
  int i;

  // Not allowed to select a button for an invalid slot
  if ( p->slot == 0 || (!(GrGetSlotMap() & (1 << p->slot))) )
    return 0;

  // Not allowed to select a button for a slot with no buttons
  if (! (GrGetClassMap(p->slot) & (1uL << GRIP_CLASS_BUTTON)) )
    return 0;

  // Start flashing the field
  if (lr)
    {
    state = p->right.state;
    p->right.state = STATE_SELECT;
    }
  else
    {
    state = p->left.state;
    p->left.state = STATE_SELECT;
    }

  // Wait for a numeric slot selection
  for (;;)
    {
    // Update the display during the vertical retrace
    WaitVertBlank();
    RefreshDisplay();

    // Refresh the controllers
    GrRefresh(0);

    // Watch for keypresses
    i = ScanKey();

    // Exit on ESC or a function key
    if (i < 0)
      break;

    // If this slot has exactly one button being pressed, then
    // that is the button to use
    buttons = GrGetPackedValues(p->slot, GRIP_CLASS_BUTTON, 0, 31);
    if ( buttons != 0 && (buttons & (buttons-1)) == 0 )
      {
      for (index = 0; ( buttons & (1 << index) ) == 0; index++)
        ;
      if (lr)
        {
        p->right.index = index;
        p->right.mask = buttons;
        p->right.value = 1;
        GrGetControlName(p->slot, GRIP_CLASS_BUTTON, index, p->right.name);
        }
      else
        {
        p->left.index = index;
        p->left.mask = buttons;
        p->left.value = 1;
        GrGetControlName(p->slot, GRIP_CLASS_BUTTON, index, p->left.name);
        }
      state = STATE_NORMAL;
      break;
      }
    }

  // Restore the field state
  if (lr)
    p->right.state = state;
  else
    p->left.state = state;

  return i;
  }




/*
** SelectAxis(play)           Lets the operator select an axis
**
** Waits for the operator to move an axis on the controller, and then
** assigns that axis to the player.  Aborts if <ESC> or a function key
** is pressed.
*/
int SelectAxis(int play)
  {
  PLAYER* p = &player[play];
  GRIP_VALUE value;
  GRIP_VALUE* min;
  GRIP_VALUE* max;
  GRIP_VALUE thresh;
  GRIP_INDEX count;
  GRIP_INDEX index;
  unsigned short state;
  int i;

  // Not allowed to select an axis for an invalid slot
  if ( p->slot == 0 || (!(GrGetSlotMap() & (1 << p->slot))) )
    return 0;

  // Not allowed to select an axis for a slot with no axes
  if (! (GrGetClassMap(p->slot) & (1uL << GRIP_CLASS_AXIS)) )
    return 0;

  // Count the number of axes and allocate space for them
  count = GrGetMaxIndex(p->slot, GRIP_CLASS_AXIS) + 1;
  if ( (min = (GRIP_VALUE*)malloc(count*sizeof(GRIP_VALUE)) ) == 0)
    {
    return 0;
    }
  if ( (max = (GRIP_VALUE*)malloc(count*sizeof(GRIP_VALUE)) ) == 0)
    {
    free(min);
    return 0;
    }

  // An axis shall be considered chosen if it has travelled at least
  // one half of its maximum distance, with a minimum distance of 2,
  // to reduce jitter from GamePad-type controllers.
  thresh = GrGetMaxValue(p->slot, GRIP_CLASS_AXIS) / 2;
  if (thresh < 2)
    thresh = 2;

  // Setup the initial conditions for the axes
  for (index = 0; index < count; index++)
    {
    // Get the current position
    min[index] = max[index] = GrGetValue(p->slot, GRIP_CLASS_AXIS, index);
    }

  // Start flashing the field
  state = p->axis.state;
  p->axis.state = STATE_SELECT;

  // Wait for a numeric slot selection
  while (p->axis.state == STATE_SELECT)
    {
    // Update the display during the vertical retrace
    WaitVertBlank();
    RefreshDisplay();

    // Refresh the controllers
    GrRefresh(0);

    // Watch for keypresses
    i = ScanKey();

    // Exit on ESC or a function key
    if (i < 0)
      break;

    // Scan through all the axes and check whether they have travelled
    // far enough to reach the threshold
    for (index = 0; index < count; index++)
      {
      // Get the current position
      value = GrGetValue(p->slot, GRIP_CLASS_AXIS, index);
      if (value < min[index])
        min[index] = value;
      else if (value > max[index])
        max[index] = value;
      // Check against the selection threshold
      if (max[index] - min[index] >= thresh)
        {
        p->axis.state = state = STATE_NORMAL;
        p->axis.index = index;
        p->axis.value = value;
        p->axis.max = GrGetMaxValue(p->slot, GRIP_CLASS_AXIS);
        GrGetControlName(p->slot, GRIP_CLASS_AXIS, index, p->axis.name);
        }
      }
    }

  // Restore the field state
  p->axis.state = state;

  // Free the memory
  free(min);
  free(max);

  return i;
  }




/*
** SetupPlayer(play, key)     Select a controller option for a player
**
** Configures a player's input devices by monitoring which ones the user
** is manipulating.
**
** Returns -10 if <ESC>ape or <F10> is pressed during the setup process.
*/
int SetupPlayer(int play, int key)
  {
  switch (key)
    {
    case 0:                   // Specify slot
      return SelectSlot(play);

    case 1:                   // Specify left button
      return SelectButton(play, 0);

    case 2:                   // Specify axis
      return SelectAxis(play);

    case 3:                   // Specify right button
      return SelectButton(play, 1);
    }

  return 0;
  }





/*
** Banner
*/
char Banner[] =
  "GrIP API Demo Program                            Version 1.00\r\n"
  "Copyright (c) 1995 by Advanced Gravis Computer Technology Ltd\r\n"
  "All Rights Reserved.\r\n";



/*
** main()                     Initialization and main input loop
*/
void main()
  {
  char lib[128];              // Full path for GrIP library
  FILE* flib;                 // File handle for library
  long size;                  // Size of library file (bytes)
  void* image;                // Memory image of library file
  int i;

  // Search for the GrIP library: GRIP.GLL
  //
  // First search in the current directory, then search in any directories
  // specified by the "SET GRIP=" environment variable, then search in any
  // directories in the path, and finally search in "C:\GRIP\".
  //
  // Note that the filename for the library ("GRIP.GLL"), the environment
  // variable name ("GRIP") and the default install directory ("C:\GRIP\")
  // are defined in the module GRIP.C as GrLibName, GrLibEnv, and
  // GrLibDir respectively.
  _searchenv(GrLibName, GrLibEnv, lib);
  if (lib[0] == 0)
    _searchenv(GrLibName, "PATH", lib);
  if (lib[0] == 0)
    {
    strcpy(lib, GrLibDir);
    strcat(lib, GrLibName);
    }

  // Open the loadable library file and determine its size
  if ( (flib = fopen(lib, "rb")) == 0 )
    {
    cprintf(Banner);
    cprintf("\r\nERROR: Unable to load library file \"%s\"\r\n", lib);
    return;
    }
  fseek(flib, 0, SEEK_END);
  size = ftell(flib);

  // Allocate memory for the library and read it all into memory
  //
  // (If the size is greater than 32k, then this is definitely not
  // the correct file, so signal an error.)
  if (size > 32*1024L)
    {
    cprintf(Banner);
    cprintf("\r\nERROR: Invalid library file \"%s\" (File too large)\r\n", lib);
    return;
    }
  image = (void*)malloc(size);
  if (image == 0)
    {
    cprintf(Banner);
    cprintf("\r\nERROR: Unable to allocate sufficient memory for library\r\n", lib);
    return;
    }
  fseek(flib, 0, SEEK_SET);
  fread(image, 1, size, flib);
  fclose(flib);

  // Link to the library (and free up the temporary memory)
  if (!GrLink(image, size))
    {
    cprintf(Banner);
    cprintf("\r\nERROR: Unable to initialize loadable library!\r\n");
    return;
    }
  free(image);

  // Initialize the GrIP system
  if (!GrInitialize())
    {
    cprintf(Banner);
    cprintf("\r\nERROR: Unable to initialize GrIP!\r\n");

    // Disconnect from the GrIP library
    GrUnlink();

    return;
    }

  // Hide the cursor
  CursorOff();

  // Initialize the players
  for (i = 0; i < PLAYER_MAX; i++)
    InitPlayer(i,0);

  // Update the display
  for (;;)
    {
    // Update the display during the vertical retrace
    WaitVertBlank();
    RefreshDisplay();

    // Refresh the controllers
    GrRefresh(0);

    // Update the states of the players
    for (i = 0; i < PLAYER_MAX; i++)
      UpdatePlayer(i);

    // Watch for keypresses
    i = ScanKey();

    // Exit on ESC or F10
    if (i == -10)
      break;

    // If F1 through F8 was pressed, then go into "Setup mode"
    while (i <= -1 && i >= -8)
      i = SetupPlayer( (-i-1) / 4, (-i-1) & 3 );
    }

  // Restore the cursor
  CursorOn();

  // Clean up the display
  ClearScreen();
  cprintf(Banner);
  cprintf("\r\n");

  // Shutdown the GrIP system
  GrShutdown();

  // Disconnect from the GrIP library
  GrUnlink();

  return;
  }

