////////////////////////////////////////////////////////////////////////////////
// Joystick.cpp -- this file is part of the Personal C64 Emulator
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm
//
// Reads Windows joysticks

#include <EDK.h>


// timer requires a class
class Joystick : public Object {

  UINT uJoyID;   // Windows joystick ID 1..16
  UINT uUp;      // thresholds
  UINT uDown;
  UINT uLeft;
  UINT uRight;
  int iRefresh;  // system clocks between two reads
  Timer Read;    // next read
  void OnRead();

public:

  // constructor
  Joystick() {
    uJoyID = 0;
    uUp = 0;
    uDown = 0;
    uLeft = 0;
    uRight = 0;
    iRefresh = 0;
  }

public:

  // interface reflects directions and button
  Port State;

  // initialisation
  void Init(int iNumber, Clock* pNewClock);
};


// initialisation
void Joystick::Init(int iNumber, Clock* pNewClock) {

  // set ID for Windows
  uJoyID = JOYSTICKID1 + iNumber;

  // read thresholds
  char ac[80];
  wsprintf(ac, "%d Threshold Up (1..32767)", 1 + iNumber);
  uUp = 32768 - gconf.GetInt("Joystick", ac, 15000);
  wsprintf(ac, "%d Threshold Down (1..32767)", 1 + iNumber);
  uDown = 32768 + gconf.GetInt("Joystick", ac, 15000);
  wsprintf(ac, "%d Threshold Left (1..32767)", 1 + iNumber);
  uLeft = 32768 - gconf.GetInt("Joystick", ac, 15000);
  wsprintf(ac, "%d Threshold Right (1..32767)", 1 + iNumber);
  uRight = 32768 + gconf.GetInt("Joystick", ac, 15000);

  // read refresh time
  wsprintf(ac, "%d Refresh (10..500 ms)", 1 + iNumber);
  iRefresh = pNewClock->ClocksFromSeconds(gconf.GetInt("Joystick", ac, 50) * 1e-3);

  // initialize subcomponents
  State.Init("State", this);
  Read.Init("Read", this);
  Read.SetClock(*pNewClock);
  Read.SetOnFire((pfn)OnRead);
  Read.StartCounter(iRefresh);
}


// read the joystick
void Joystick::OnRead() {

  // nothing pressed
  byte bState = 0;

  // read position and buttons
  JOYINFO ji;
  if (joyGetPos(uJoyID, &ji) == JOYERR_NOERROR) {

    // compare directions
    if (ji.wYpos <= uUp) {
      bState |= 0x01;
    } else if (ji.wYpos >= uDown) {
      bState |= 0x02;
    }
    if (ji.wXpos <= uLeft) {
      bState |= 0x04;
    } else if (ji.wXpos >= uRight) {
      bState |= 0x08;
    }

    // set fire
    if ((ji.wButtons & 0x03) != 0) {
      bState |= 0x10;
    }

  }

  // return the result to the emulator, invert because low active
  State.SetOutput(~bState);

  // call me back sometimes
  Read.StartCounter(iRefresh);
}


// pointers to joysticks
static Joystick* gapJoystick[16];


// create and connect the joysticks
void InitJoysticks(Port& Primary, Port& Secondary, Clock* pNewClock) {

  // Windows provides 16 joysticks
  for (int i = 0; i < 16; i++) {
    char ac[80];

    // check if joystick is enabled
    JOYINFO ji;
    MMRESULT mm = joyGetPos(JOYSTICKID1 + i, &ji);
    wsprintf(ac, "Use Joystick %d (1 or 0)", 1 + i);
    if (mm == JOYERR_NOERROR && gconf.GetInt("Joystick", ac, 1) != 0) {

      // create the joystick
      gapJoystick[i] = new Joystick();
      gapJoystick[i]->Init(i, pNewClock);

      // connect it to the primary or secondary port
      wsprintf(ac, "%d Same C64 Port as Numpad Keys (1 or 0)", 1 + i);
      flag fSamePort = gconf.GetInt("Joystick", ac, i & 1);
      gapJoystick[i]->State.ConnectTo(fSamePort ? Primary : Secondary);

    }
  }
}


// destroy the LPT joysticks
void ExitJoysticks() {
  for (int i = 0; i < 16; i++) {
    if (gapJoystick[i] != NULL) {
      delete gapJoystick[i];
      gapJoystick[i] = NULL;
    }
  }
}
