/* control.h

   This module handles the players' controls.

   IMPORTANT: All memory allocated MUST be locked!!

   Usage example:
     int left = control_key(player->control, CONTROL_KEY_LEFT);

     if (left)
       do_stuff();
*/

#ifndef INCLUDED_CONTROL_H
#define INCLUDED_CONTROL_H

#define CONTROL_KEY_FORWARD  0
#define CONTROL_KEY_BACKWARD 1
#define CONTROL_KEY_LEFT     2
#define CONTROL_KEY_RIGHT    3
#define CONTROL_KEY_FIRE     4
#define CONTROL_KEY_POWERUP  5

#define CONTROL_NUM_KEYS     6

#define CONTROL_FIFO_SIZE    48


#define CONTROL_TYPE_UNKNOWN  0
#define CONTROL_TYPE_OFF      1
#define CONTROL_TYPE_KEYBOARD 2
#define CONTROL_TYPE_JOYSTICK 3
#define CONTROL_TYPE_ZIG_AI   4

#define N_CONTROL_TYPES       5


#define MAX_DEVICES 8 /* Change this if necessary. */



/* This holds settings and status for a keyboard controller. */
typedef struct C_KEYBOARD {

 /* These are KEY_* constants representing the player's chosen keys. They may
    be anything except 0, KEY_ESC or KEY_PAUSE.
 */
 unsigned char key_id[CONTROL_NUM_KEYS];

} C_KEYBOARD;


/* This holds settings and status for a joystick controller. */
typedef struct C_JOYSTICK {

 int joy_num[CONTROL_NUM_KEYS];   /* Joystick number */
 int stick[CONTROL_NUM_KEYS],    /* Device controls */
     axis[CONTROL_NUM_KEYS],     /* Axis number is 0-n for d1, -1 to (-n-1) for d2 */
     button[CONTROL_NUM_KEYS];   /* Button is -1 if unused */

} C_JOYSTICK;


/* This is a universal controller struct. Each player has one of these while
   a game is in progress. type is set to one of the CONTROL_TYPE_* constants.
   dev.joy will be used for joysticks, while the keyboard will use dev.kb.
   Zig AI will not use dev at all. All devices will simulate key-presses, and
   the key scancodes will be irrelevant. You should not access this struct
   directly; use the functions in this module instead.
*/
typedef struct CONTROL {

 int type;

 /* 'key_time' constitutes a FIFO queue. An IRQ callback will be
	installed and update this buffer with the game time (-1 for empty)
	Key presses AND releases are stored (releases are marked with the top bit set.
	This is to save space in the queue. control_key(...) will still act the same,
	returning only whether the current key is being pressed or not.

 Time at which the last change occurred for each key. */
 unsigned int key_time[CONTROL_NUM_KEYS][CONTROL_FIFO_SIZE];

 int fifo_start[CONTROL_NUM_KEYS];
 volatile int fifo_end[CONTROL_NUM_KEYS];

 union {
  C_KEYBOARD keyboard;
  C_JOYSTICK joystick;
 } dev;

} CONTROL;


/* Dynamic list of pointers to the control structures. Whenever a control
   structure is created, its pointer is stored here. This is used so that the
   functions in this module can activate the controls - see below.
*/
extern CONTROL **ControlList;
extern int NumControls;


/* update_keyboard(): during the game, this function is installed in
   Allegro's keyboard_lowlevel_callback variable. That means it is called
   every time a key is pressed or released. Bits 0 to 6 of the parameter are
   a KEY_* constant, while bit 7 is set for a release or clear for a press.

   This function will cycle through the ControlList array. For each keyboard
   controller, it will check whether the key pressed or released has been
   chosen as one of the player's keys. If so, it will store a press or
   release in the FIFO. It must be registered at time 'true_game_time',
   defined in timeloop.h. Releases are marked by setting the top bit.

   THIS FUNCTION AND ALL YOUR MEMORY IT USES ARE MUST BE LOCKED !!!
*/
void update_keyboard(int k);


/* Unlike the keyboard, the joystick is not interrupt-driven. It must be
   regularly updated by calls to poll_joystick(). Hence there is little to be
   gained from recording the state of the joystick in an interrupt function.

   Instead, we use an interrupt function, set_joystick_flag(), to, duh, set
   joystick_flag. This function does nothing but set the variable
   'joystick_flag' to 1. It is installed as a timer interrupt to be called at
   a rate of 40 Hz.

   joystick_flag and set_joystick_flag() must both be locked in memory!

   update_joystick() is then responsible for reading the joystick. It need
   not be locked. It must be called as often as possible within reason. To
   that end, it will be called at several points during the draw_game()
   function in game.h. It will also be called once at the beginning of
   update_game() in game.h, to make sure the joysticks are up to date before
   we try to read their FIFOs.

   If joystick_flag is 0, this function returns without doing anything. That
   way we do not call poll_joystick() more often than necessary, since it is
   slow on some operating systems, coughwindowscough.

   Otherwise, update_joystick() first checks the ControlList array to see if
   any joysticks are in use.

   If joysticks are found to be in use, this function calls poll_joystick(),
   after which it iterates through the ControlList array for a second time.
   If any simulated key has changed, it stores a record in the FIFO, using
   'true_game_time' as the time at which it occurred.

   Finally it clears joystick_flag and returns.
*/
extern int joystick_flag; /* LOCK THIS! */
void set_joystick_flag(void); /* LOCK THIS! */

void update_joystick(); /* This need not be locked. */


/* control_key(): this will return nonzero if the corresponding key is held
   down, or zero otherwise. Note that this function can be used with
   joysticks and Zig AI; these controllers emulate the keyboard. The 'k'
   parameter is one of the CONTROL_KEY_* constants at the top of this file.

   You should call this just once per key per controller per physics frame,
   for reasons of efficiency. If you need its return value more than once,
   store it in a variable.

   This function uses the global game_time variable in timeloop.h. It does
   *NOT* use true_game_time.
*/
int control_key(CONTROL *ctrl, int k);


/* control_ack(): DESCRIPTION NOT ACCURATE

   Will pass through all control objects and shift the FIFO queue.
   Returns non-zero if there are still keys to be processed for
   this frame. Will remove the oldest keys (that have been processed).
   Returns 0 if there's nothing else to do.

   Usage:

     do {

       for (i = 0; i < num_players; i++)
          left = control_key(player[i]->control, CONTROL_LEFT);

     } while (control_ack());
 */
int control_ack(CONTROL *ctrl);

/* Constructor/Destructor */
CONTROL *create_control(int type);
void destroy_control(CONTROL *ctrl);

/* Some useful stuff for menus */
//won't need this: int control_get_type(CONTROL *ctrl);

/* Returns the string name of the key or stick */
AL_CONST char *control_get_type_name(CONTROL *ctrl);
AL_CONST char *control_get_key_name(CONTROL *ctrl, int k, char *buf, int size);


/* Returns "Left", "Right", "Fire", and so on. */
AL_CONST char *control_get_key_binding_name(int k);

/* Config save/load - will use defaults if necessary */
CONTROL *load_control_config(const char *section, int type);
void save_control_config(const char *section, CONTROL *ctrl);


/* For AI mainly - so they can set keys. Info will be stored in the
   keyboard binding.
*/
void control_press_key(CONTROL *ctrl, int k, int time);
void control_release_key(CONTROL *ctrl, int k, int time);


/* m_ctrl.h uses the following functions and variables to set up the
   controllers.

   Before you try to detect a key, call initiate_choose_key(), passing the
   control device and the CONTROL_KEY_* constant of your choice. You may pass
   either a keyboard or a joystick controller.

   After calling this function, you must ensure that choose_update_joystick()
   is called as often as possible, i.e. at least once in the loop, and
   several times during the drawing code. You need not make this a special
   case for joystick; just call the function anyway, since it will not do
   anything if the controller is a keyboard.

   At some point in your loop, you should call control_choose_key(). If this
   function returns zero, no key has yet been detected. If this function
   returns positive, a key has been detected and accepted. If it returns
   negative, the key detection was cancelled (Esc was pressed).

   When this function returns nonzero, you may consider the key detection
   resolved. The key has been stored in the CONTROL struct, and the key
   detection code has been shut down. The keyboard buffer will have been
   cleared, so you can proceed as normal.
*/


/* choose_update_keyboard() and choose_update_joystick() work in much the
   same way as update_keyboard() and update_joystick(). However, when a key
   press or release is detected (simulated or otherwise), their behaviour is
   different.

   If a key is released, the corresponding entry in choose_key[] should be
   cleared. If a key is pressed, the key should be accepted only if the
   corresponding entry in the choose_key[] array is clear. This guards
   against detecting keys that were already held down, provided choose_key[]
   is initialised to match the key[] array.

   Note that choose_update_keyboard() will be installed for both keyboard and
   joystick detection, so that it can detect Esc. Therefore,
   choose_update_keyboard() should check that choose_control is a keyboard
   controller before responding to any key except Esc.

   To accept a key, first remove choose_update_keyboard() from Allegro's
   keyboard_lowlevel_callback variable. Then, for a key on the keyboard,
   set key_chosen to the scancode of the key detected and leave it at that.
   For a simulated key on the joystick, write the data on which key was
   pressed directly into choose_control, set choose_control to null, and
   clear the keyboard buffer. Don't clear the keyboard buffer from within
   choose_update_keyboard!

   When accepting a key from the joystick, you should also remove
   set_joystick_flag(), which was installed as a timer interrupt, and set
   'joystick_flag' to 0 ready for the next time. Do this first.

   The choose_update_keyboard() function should respond to Esc for both
   keyboard and joystick controllers. Go through the same procedure as when
   responding to any other key on the keyboard. Store the key, which will be
   KEY_ESC, in key_chosen. If the controller is a joystick, remove
   set_joystick_flag().

   IMPORTANT: don't accept KEY_PAUSE!

   Before doing anything else, choose_update_joystick() should check that
   'joystick_flag' has been set. If this is true, then choose_control is
   guaranteed to point to a joystick CONTROL struct. At this point, call
   poll_joystick(), and use the joy[] array to determine whether a simulated
   key has been pressed.
*/
void choose_update_keyboard(int k); /* LOCK THIS! */
void choose_update_joystick(); /* This need not be locked. */

/* initialise_choose_key(). This function will:

   - Initialise choose_key[] so it reflects the current status of key[].
   - Set choose_control to point to the control struct passed.
   - Set choose_k equal to k.
   - Set key_chosen to 0.
   - Install choose_update_keyboard() as Allegro's keyboard_lowlevel_callback
     function.

   If the controller is a joystick, it will then install set_joystick_flag()
   as a timer handler to be called at a rate of 40 Hz.
*/
void initiate_choose_key(CONTROL *control, int k);

/* control_choose_key(). This function applies the following tests in order:

   If choose_control is null, a key on the joystick was accepted. Nothing to
   do but return 1.

   If key_chosen is zero, no key has yet been detected. Return 0.

   If key_chosen is KEY_ESC, the detection was cancelled. Clear the keyboard
   buffer and return -1.

   Otherwise, a key on the keyboard was accepted. Write the value of
   key_chosen into the choose_control struct for key 'choose_k'. Set
   choose_control to null. Then clear the keyboard buffer, and return 1.
*/
int control_choose_key();


/* These will be set up by initialise_control(). Devices will be autodetected
   and names will be filled in. Entry 0 is guaranteed to be "Off".
*/
extern char *ControlDeviceName[MAX_DEVICES];
extern int ControlDeviceType[MAX_DEVICES];
extern int ControlDeviceID[MAX_DEVICES];
extern int NumControlDevices;

/* human_device is set to the keyboard device index, and zig_ai_device is set
   to the Zig AI device's index. These are used if previous controller
   options could not be loaded, or all were set to "Off".
*/
extern int human_device;
extern int zig_ai_device;


/* control_identify_device(): this takes the string and compares it against
   all the entries in ControlDeviceName. If a match is found, it returns the
   device number. If no match is found, it returns 0, the index corresponding
   to "Off".
*/
int control_identify_device(int type, int id);


/* Called before and after the game. */
void activate_control();
void deactivate_control();

void control_identify_devices();

/* initialise_control(): this locks in memory all functions and variables in
   this module that will be used in an interrupt context. It also autodetects
   the control devices.

   NumControlDevices is the number of control devices detected (including
   "Off" and "Zig AI").

   ControlDeviceName[] is initialised with the names of the controllers, e.g.
   "Keyboard", "Joystick 2", "Zig AI".

   ControlDeviceType[] is initialised with CONTROL_TYPE_* constants.

   ControlDeviceID[] is initialised with a unique identifier where two
   controllers have the same type (e.g. two joysticks).
   It is called by the main module.


	Joystick and Keyboard must be installed before calling this function.
*/
void initialise_control();
void initialise_control_2();


/* Shuts down the control module. All CONTROL stuctres will be freed and non-usable. */
void shut_down_control();


#endif /* INCLUDED_CONTROL_H */
