/*
 * control.c
 * MACT library controller handling
 *
 * Derived from MACT386.LIB disassembly by Jonathon Fowler
 *
 */

#include "compat.h"
#include "types.h"
#include "keyboard.h"
#include "mouse.h"
#include "control.h"
#include "_control.h"
#include "util_lib.h"

#include "baselayer.h"
#include "osd.h"
#include "pragmas.h"


boolean CONTROL_JoyPresent = false;
boolean CONTROL_JoystickEnabled = false;
boolean CONTROL_MousePresent = false;
boolean CONTROL_MouseEnabled = false;
uint32  CONTROL_ButtonState1 = 0;
uint32  CONTROL_ButtonHeldState1 = 0;
uint32  CONTROL_ButtonState2 = 0;
uint32  CONTROL_ButtonHeldState2 = 0;

// static int32 CONTROL_UserInputDelay = -1;
static int32 CONTROL_MouseSensitivity = DEFAULTMOUSESENSITIVITY;
static int32 CONTROL_NumMouseButtons = 0;
static int32 CONTROL_NumMouseAxes = 0;
static int32 CONTROL_NumJoyButtons = 0;
static int32 CONTROL_NumJoyAxes = 0;
static controlflags       CONTROL_Flags[CONTROL_NUM_FLAGS];
static controlbuttontype  CONTROL_MouseButtonMapping[MAXMOUSEBUTTONS],
CONTROL_JoyButtonMapping[MAXJOYBUTTONS];
static controlkeymaptype  CONTROL_KeyMapping[CONTROL_NUM_FLAGS];
static controlaxismaptype CONTROL_MouseAxesMap[MAXMOUSEAXES],	// maps physical axes onto virtual ones
CONTROL_JoyAxesMap[MAXJOYAXES];
static controlaxistype    CONTROL_MouseAxes[MAXMOUSEAXES],	// physical axes
CONTROL_JoyAxes[MAXJOYAXES];
static controlaxistype    CONTROL_LastMouseAxes[MAXMOUSEAXES],
CONTROL_LastJoyAxes[MAXJOYAXES];
static int32   CONTROL_MouseAxesScale[MAXMOUSEAXES],             CONTROL_JoyAxesScale[MAXJOYAXES];
static int32   CONTROL_MouseButtonState[MAXMOUSEBUTTONS],        CONTROL_JoyButtonState[MAXJOYBUTTONS];
static int32   CONTROL_MouseButtonClickedTime[MAXMOUSEBUTTONS],  CONTROL_JoyButtonClickedTime[MAXJOYBUTTONS];
static boolean CONTROL_MouseButtonClickedState[MAXMOUSEBUTTONS], CONTROL_JoyButtonClickedState[MAXJOYBUTTONS];
static boolean CONTROL_MouseButtonClicked[MAXMOUSEBUTTONS],      CONTROL_JoyButtonClicked[MAXJOYBUTTONS];
static byte    CONTROL_MouseButtonClickedCount[MAXMOUSEBUTTONS], CONTROL_JoyButtonClickedCount[MAXJOYBUTTONS];
static boolean CONTROL_UserInputCleared[3];
static int32(*GetTime)(void);
static boolean CONTROL_Started = false;
static int32 ticrate;
static int32 CONTROL_DoubleClickSpeed;

int extinput[CONTROL_NUM_FLAGS];
keybind boundkeys[MAXBOUNDKEYS], mousebind[MAXMOUSEBUTTONS];
int bindsenabled = 0;

void CONTROL_GetMouseDelta(void)
{
    int32 x,y;

    MOUSE_GetDelta(&x, &y);

    /* What in the name of all things sacred is this?
    if (labs(*x) > labs(*y)) {
    	*x /= 3;
    } else {
    	*y /= 3;
    }
    *y = *y * 96;
    *x = (*x * 32 * CONTROL_MouseSensitivity) >> 15;
    */

    CONTROL_MouseAxes[0].analog = (x * (CONTROL_MouseSensitivity<<1));
    CONTROL_MouseAxes[1].analog = (y * (CONTROL_MouseSensitivity<<1))<<1;
}

int32 CONTROL_GetMouseSensitivity(void)
{
    return (CONTROL_MouseSensitivity);
}

void CONTROL_SetMouseSensitivity(int32 newsensitivity)
{
    CONTROL_MouseSensitivity = newsensitivity;
}

boolean CONTROL_StartMouse(void)
{
    CONTROL_NumMouseButtons = MAXMOUSEBUTTONS;
    return MOUSE_Init();
}

void CONTROL_GetJoyAbs(void)
{
}

void CONTROL_FilterJoyDelta(void)
{
}

void CONTROL_GetJoyDelta(void)
{
    int32 i;

    for (i=0; i<joynumaxes; i++)
        CONTROL_JoyAxes[i].analog = joyaxis[i] >> 5;
}

boolean CONTROL_StartJoy(int32 joy)
{
    UNREFERENCED_PARAMETER(joy);
    return (inputdevices & 4) == 4;
}

void CONTROL_ShutJoy(int32 joy)
{
    UNREFERENCED_PARAMETER(joy);
    CONTROL_JoyPresent = false;
}

int32 CONTROL_GetTime(void)
{
    static int32 t = 0;
    t += 5;
    return t;
}

static inline boolean CONTROL_CheckRange(int32 which)
{
    if ((uint32)which >= (uint32)CONTROL_NUM_FLAGS) return true;
    //Error("CONTROL_CheckRange: Index %d out of valid range for %d control flags.",
    //	which, CONTROL_NUM_FLAGS);
    return false;
}

void CONTROL_SetFlag(int32 which, boolean active)
{
    if (CONTROL_CheckRange(which)) return;

    if (CONTROL_Flags[which].toggle == INSTANT_ONOFF)
    {
        CONTROL_Flags[which].active = active;
        return;
    }
    if (active)
    {
        CONTROL_Flags[which].buttonheld = false;
    }
    else if (CONTROL_Flags[which].buttonheld == false)
    {
        CONTROL_Flags[which].buttonheld = true;
        CONTROL_Flags[which].active = (CONTROL_Flags[which].active ? false : true);
    }
}

boolean CONTROL_KeyboardFunctionPressed(int32 which)
{
    boolean key1 = 0, key2 = 0;

    if (CONTROL_CheckRange(which)) return false;

    if (!CONTROL_Flags[which].used) return false;

    if (CONTROL_KeyMapping[which].key1 != KEYUNDEFINED && !boundkeys[CONTROL_KeyMapping[which].key1].cmd[0])
        key1 = KB_KeyDown[ CONTROL_KeyMapping[which].key1 ] ? true : false;

    if (CONTROL_KeyMapping[which].key2 != KEYUNDEFINED && !boundkeys[CONTROL_KeyMapping[which].key1].cmd[0])
        key2 = KB_KeyDown[ CONTROL_KeyMapping[which].key2 ] ? true : false;

    return (key1 | key2);
}

void CONTROL_ClearKeyboardFunction(int32 which)
{
    if (CONTROL_CheckRange(which)) return;

    if (!CONTROL_Flags[which].used) return;

    if (CONTROL_KeyMapping[which].key1 != KEYUNDEFINED)
        KB_KeyDown[ CONTROL_KeyMapping[which].key1 ] = 0;

    if (CONTROL_KeyMapping[which].key2 != KEYUNDEFINED)
        KB_KeyDown[ CONTROL_KeyMapping[which].key2 ] = 0;
}

void CONTROL_DefineFlag(int32 which, boolean toggle)
{
    if (CONTROL_CheckRange(which)) return;

    CONTROL_Flags[which].active     = false;
    CONTROL_Flags[which].used       = true;
    CONTROL_Flags[which].toggle     = toggle;
    CONTROL_Flags[which].buttonheld = false;
    CONTROL_Flags[which].cleared    = 0;
}

boolean CONTROL_FlagActive(int32 which)
{
    if (CONTROL_CheckRange(which)) return false;

    return CONTROL_Flags[which].used;
}

void CONTROL_MapKey(int32 which, kb_scancode key1, kb_scancode key2)
{
    if (CONTROL_CheckRange(which)) return;

    CONTROL_KeyMapping[which].key1 = key1 ? key1 : KEYUNDEFINED;
    CONTROL_KeyMapping[which].key2 = key2 ? key2 : KEYUNDEFINED;
}

void CONTROL_PrintKeyMap(void)
{
    int32 i;

    for (i=0;i<CONTROL_NUM_FLAGS;i++)
    {
        initprintf("function %2ld key1=%3x key2=%3x\n",
                   i, CONTROL_KeyMapping[i].key1, CONTROL_KeyMapping[i].key2);
    }
}

void CONTROL_PrintControlFlag(int32 which)
{
    initprintf("function %2ld active=%d used=%d toggle=%d buttonheld=%d cleared=%d\n",
               which, CONTROL_Flags[which].active, CONTROL_Flags[which].used,
               CONTROL_Flags[which].toggle, CONTROL_Flags[which].buttonheld,
               CONTROL_Flags[which].cleared);
}

void CONTROL_PrintAxes(void)
{
    int32 i;

    initprintf("nummouseaxes=%d\n", CONTROL_NumMouseAxes);
    for (i=0;i<CONTROL_NumMouseAxes;i++)
    {
        initprintf("axis=%d analog=%d digital1=%d digital2=%d\n",
                   i, CONTROL_MouseAxesMap[i].analogmap,
                   CONTROL_MouseAxesMap[i].minmap, CONTROL_MouseAxesMap[i].maxmap);
    }

    initprintf("numjoyaxes=%d\n", CONTROL_NumJoyAxes);
    for (i=0;i<CONTROL_NumJoyAxes;i++)
    {
        initprintf("axis=%d analog=%d digital1=%d digital2=%d\n",
                   i, CONTROL_JoyAxesMap[i].analogmap,
                   CONTROL_JoyAxesMap[i].minmap, CONTROL_JoyAxesMap[i].maxmap);
    }
}

void CONTROL_MapButton(int32 whichfunction, int32 whichbutton, boolean doubleclicked, controldevice device)
{
    controlbuttontype *set;

    if (CONTROL_CheckRange(whichfunction)) whichfunction = BUTTONUNDEFINED;

    switch (device)
    {
    case controldevice_mouse:
        if ((uint32)whichbutton >= (uint32)MAXMOUSEBUTTONS)
        {
            //Error("CONTROL_MapButton: button %d out of valid range for %d mouse buttons.",
            //		whichbutton, CONTROL_NumMouseButtons);
            return;
        }
        set = CONTROL_MouseButtonMapping;
        break;

    case controldevice_joystick:
        if ((uint32)whichbutton >= (uint32)MAXJOYBUTTONS)
        {
            //Error("CONTROL_MapButton: button %d out of valid range for %d joystick buttons.",
            //		whichbutton, CONTROL_NumJoyButtons);
            return;
        }
        set = CONTROL_JoyButtonMapping;
        break;

    default:
        //Error("CONTROL_MapButton: invalid controller device type");
        return;
    }

    if (doubleclicked)
        set[whichbutton].doubleclicked = whichfunction;
    else
        set[whichbutton].singleclicked = whichfunction;
}

void CONTROL_MapAnalogAxis(int32 whichaxis, int32 whichanalog, controldevice device)
{
    controlaxismaptype *set;

    if ((uint32)whichanalog >= (uint32)analog_maxtype)
    {
        //Error("CONTROL_MapAnalogAxis: analog function %d out of valid range for %d analog functions.",
        //		whichanalog, analog_maxtype);
        return;
    }

    switch (device)
    {
    case controldevice_mouse:
        if ((uint32)whichaxis >= (uint32)MAXMOUSEAXES)
        {
            //Error("CONTROL_MapAnalogAxis: axis %d out of valid range for %d mouse axes.",
            //		whichaxis, MAXMOUSEAXES);
            return;
        }

        set = CONTROL_MouseAxesMap;
        break;

    case controldevice_joystick:
        if ((uint32)whichaxis >= (uint32)MAXJOYAXES)
        {
            //Error("CONTROL_MapAnalogAxis: axis %d out of valid range for %d joystick axes.",
            //		whichaxis, MAXJOYAXES);
            return;
        }

        set = CONTROL_JoyAxesMap;
        break;

    default:
        //Error("CONTROL_MapAnalogAxis: invalid controller device type");
        return;
    }

    set[whichaxis].analogmap = whichanalog;
}

void CONTROL_SetAnalogAxisScale(int32 whichaxis, int32 axisscale, controldevice device)
{
    int32 *set;

    switch (device)
    {
    case controldevice_mouse:
        if ((uint32)whichaxis >= (uint32)MAXMOUSEAXES)
        {
            //Error("CONTROL_SetAnalogAxisScale: axis %d out of valid range for %d mouse axes.",
            //		whichaxis, MAXMOUSEAXES);
            return;
        }

        set = CONTROL_MouseAxesScale;
        break;

    case controldevice_joystick:
        if ((uint32)whichaxis >= (uint32)MAXJOYAXES)
        {
            //Error("CONTROL_SetAnalogAxisScale: axis %d out of valid range for %d joystick axes.",
            //		whichaxis, MAXJOYAXES);
            return;
        }

        set = CONTROL_JoyAxesScale;
        break;

    default:
        //Error("CONTROL_SetAnalogAxisScale: invalid controller device type");
        return;
    }

    set[whichaxis] = axisscale;
}

void CONTROL_MapDigitalAxis(int32 whichaxis, int32 whichfunction, int32 direction, controldevice device)
{
    controlaxismaptype *set;

    if (CONTROL_CheckRange(whichfunction)) whichfunction = AXISUNDEFINED;

    switch (device)
    {
    case controldevice_mouse:
        if ((uint32)whichaxis >= (uint32)MAXMOUSEAXES)
        {
            //Error("CONTROL_MapDigitalAxis: axis %d out of valid range for %d mouse axes.",
            //		whichaxis, MAXMOUSEAXES);
            return;
        }

        set = CONTROL_MouseAxesMap;
        break;

    case controldevice_joystick:
        if ((uint32)whichaxis >= (uint32)MAXJOYAXES)
        {
            //Error("CONTROL_MapDigitalAxis: axis %d out of valid range for %d joystick axes.",
            //		whichaxis, MAXJOYAXES);
            return;
        }

        set = CONTROL_JoyAxesMap;
        break;

    default:
        //Error("CONTROL_MapDigitalAxis: invalid controller device type");
        return;
    }

    switch (direction)  	// JBF: this is all very much a guess. The ASM puzzles me.
    {
    case axis_up:
    case axis_left:
        set[whichaxis].minmap = whichfunction;
        break;
    case axis_down:
    case axis_right:
        set[whichaxis].maxmap = whichfunction;
        break;
    default:
        break;
    }
}

void CONTROL_ClearAssignments(void)
{
    int32 i;

    memset(CONTROL_MouseButtonMapping,  BUTTONUNDEFINED, sizeof(CONTROL_MouseButtonMapping));
    memset(CONTROL_JoyButtonMapping,    BUTTONUNDEFINED, sizeof(CONTROL_JoyButtonMapping));
    memset(CONTROL_KeyMapping,          KEYUNDEFINED,    sizeof(CONTROL_KeyMapping));
    memset(CONTROL_MouseAxesMap,        AXISUNDEFINED,   sizeof(CONTROL_MouseAxesMap));
    memset(CONTROL_JoyAxesMap,          AXISUNDEFINED,   sizeof(CONTROL_JoyAxesMap));
    memset(CONTROL_MouseAxes,           0,               sizeof(CONTROL_MouseAxes));
    memset(CONTROL_JoyAxes,             0,               sizeof(CONTROL_JoyAxes));
    memset(CONTROL_LastMouseAxes,       0,               sizeof(CONTROL_LastMouseAxes));
    memset(CONTROL_LastJoyAxes,         0,               sizeof(CONTROL_LastJoyAxes));
    for (i=0;i<MAXMOUSEAXES;i++)
        CONTROL_MouseAxesScale[i] = NORMALAXISSCALE;
    for (i=0;i<MAXJOYAXES;i++)
        CONTROL_JoyAxesScale[i] = NORMALAXISSCALE;
}

static void DoGetDeviceButtons(
    int32 buttons, int32 tm,
    int32 NumButtons,
    int32 *DeviceButtonState,
    int32 *ButtonClickedTime,
    boolean *ButtonClickedState,
    boolean *ButtonClicked,
    byte  *ButtonClickedCount
)
{
    int32 i=NumButtons-1, bs;

    for (;i>=0;i--)
    {
        bs = (buttons >> i) & 1;

        DeviceButtonState[i] = bs;
        ButtonClickedState[i] = false;

        if (bs)
        {
            if (ButtonClicked[i] == false)
            {
                ButtonClicked[i] = true;

                if (ButtonClickedCount[i] == 0 || tm > ButtonClickedTime[i])
                {
                    ButtonClickedTime[i] = tm + CONTROL_DoubleClickSpeed;
                    ButtonClickedCount[i] = 1;
                }
                else if (tm < ButtonClickedTime[i])
                {
                    ButtonClickedState[i] = true;
                    ButtonClickedTime[i]  = 0;
                    ButtonClickedCount[i] = 2;
                }
            }
            else if (ButtonClickedCount[i] == 2)
            {
                ButtonClickedState[i] = true;
            }
            continue;
        }
        if (ButtonClickedCount[i] == 2)
            ButtonClickedCount[i] = 0;

        ButtonClicked[i] = false;
    }
}

void CONTROL_GetDeviceButtons(void)
{
    int32 t;

    t = GetTime();

    if (CONTROL_MouseEnabled)
    {
        DoGetDeviceButtons(
            MOUSE_GetButtons(), t,
            CONTROL_NumMouseButtons,
            CONTROL_MouseButtonState,
            CONTROL_MouseButtonClickedTime,
            CONTROL_MouseButtonClickedState,
            CONTROL_MouseButtonClicked,
            CONTROL_MouseButtonClickedCount
        );
    }

    if (CONTROL_JoystickEnabled)
    {
        int32 buttons = joyb;
        if (joynumhats > 0 && joyhat[0] != -1)
        {
            static int32 hatstate[] = { 1, 1|2, 2, 2|4, 4, 4|8, 8, 8|1 };
            int val;

            // thanks SDL for this much more sensible method
            val = ((joyhat[0] + 4500 / 2) % 36000) / 4500;
            if (val < 8) buttons |= hatstate[val] << min(MAXJOYBUTTONS,joynumbuttons);
        }

        DoGetDeviceButtons(
            buttons, t,
            CONTROL_NumJoyButtons,
            CONTROL_JoyButtonState,
            CONTROL_JoyButtonClickedTime,
            CONTROL_JoyButtonClickedState,
            CONTROL_JoyButtonClicked,
            CONTROL_JoyButtonClickedCount
        );
    }
}

void CONTROL_DigitizeAxis(int32 axis, controldevice device)
{
    controlaxistype *set, *lastset;

    switch (device)
    {
    case controldevice_mouse:
        set = CONTROL_MouseAxes;
        lastset = CONTROL_LastMouseAxes;
        break;

    case controldevice_joystick:
        set = CONTROL_JoyAxes;
        lastset = CONTROL_LastJoyAxes;
        break;

    default: return;
    }

    if (set[axis].analog > 0)
    {
        if (set[axis].analog > THRESHOLD || (set[axis].analog > MINTHRESHOLD && lastset[axis].digital == 1))
            set[axis].digital = 1;
    }
    else
    {
        if (set[axis].analog < -THRESHOLD || (set[axis].analog < -MINTHRESHOLD && lastset[axis].digital == -1))
            set[axis].digital = -1;
    }
}

void CONTROL_ScaleAxis(int32 axis, controldevice device)
{
    controlaxistype *set;
    int32 *scale;

    switch (device)
    {
    case controldevice_mouse:
        set = CONTROL_MouseAxes;
        scale = CONTROL_MouseAxesScale;
        break;

    case controldevice_joystick:
        set = CONTROL_JoyAxes;
        scale = CONTROL_JoyAxesScale;
        break;

    default: return;
    }

    set[axis].analog = mulscale16(set[axis].analog, scale[axis]);
}

void CONTROL_ApplyAxis(int32 axis, ControlInfo *info, controldevice device)
{
    controlaxistype *set;
    controlaxismaptype *map;

    switch (device)
    {
    case controldevice_mouse:
        set = CONTROL_MouseAxes;
        map = CONTROL_MouseAxesMap;
        break;

    case controldevice_joystick:
        set = CONTROL_JoyAxes;
        map = CONTROL_JoyAxesMap;
        break;

    default: return;
    }

    switch (map[axis].analogmap)
    {
    case analog_turning:          info->dyaw   += set[axis].analog; break;
    case analog_strafing:         info->dx     += set[axis].analog; break;
    case analog_lookingupanddown: info->dpitch += set[axis].analog; break;
    case analog_elevation:        info->dy     += set[axis].analog; break;
    case analog_rolling:          info->droll  += set[axis].analog; break;
    case analog_moving:           info->dz     += set[axis].analog; break;
    default: break;
    }
}

void CONTROL_PollDevices(ControlInfo *info)
{
    memcpy(CONTROL_LastMouseAxes, CONTROL_MouseAxes, sizeof(CONTROL_MouseAxes));
    memcpy(CONTROL_LastJoyAxes,   CONTROL_JoyAxes,   sizeof(CONTROL_JoyAxes));

    memset(CONTROL_MouseAxes, 0, sizeof(CONTROL_MouseAxes));
    memset(CONTROL_JoyAxes,   0, sizeof(CONTROL_JoyAxes));
    memset(info, 0, sizeof(ControlInfo));

    if (CONTROL_MouseEnabled)
    {
        int32 i = MAXMOUSEAXES-1;

        CONTROL_GetMouseDelta();
        do
        {
            CONTROL_DigitizeAxis(i, controldevice_mouse);
            CONTROL_ScaleAxis(i, controldevice_mouse);
            LIMITCONTROL(&CONTROL_MouseAxes[i].analog);
            CONTROL_ApplyAxis(i, info, controldevice_mouse);
        }
        while (--i);

        CONTROL_DigitizeAxis(0, controldevice_mouse);
        CONTROL_ScaleAxis(0, controldevice_mouse);
        LIMITCONTROL(&CONTROL_MouseAxes[0].analog);
        CONTROL_ApplyAxis(0, info, controldevice_mouse);
    }

    if (CONTROL_JoystickEnabled)
    {
        int32 i = MAXJOYAXES-1;

        CONTROL_GetJoyDelta();
        do
        {
            CONTROL_DigitizeAxis(i, controldevice_joystick);
            CONTROL_ScaleAxis(i, controldevice_joystick);
            LIMITCONTROL(&CONTROL_JoyAxes[i].analog);
            CONTROL_ApplyAxis(i, info, controldevice_joystick);
        }
        while (--i);

        CONTROL_DigitizeAxis(0, controldevice_joystick);
        CONTROL_ScaleAxis(0, controldevice_joystick);
        LIMITCONTROL(&CONTROL_JoyAxes[0].analog);
        CONTROL_ApplyAxis(0, info, controldevice_joystick);
    }

    CONTROL_GetDeviceButtons();
}

void CONTROL_AxisFunctionState(int32 *p1)
{
    if (CONTROL_NumMouseAxes)
    {
        int32 j, i = CONTROL_NumMouseAxes-1;

        do
        {
            if (!CONTROL_MouseAxes[i].digital) continue;

            if (CONTROL_MouseAxes[i].digital < 0)
                j = CONTROL_MouseAxesMap[i].minmap;
            else
                j = CONTROL_MouseAxesMap[i].maxmap;

            if (j != AXISUNDEFINED)
                p1[j] = 1;
        }
        while (i--);
    }

    if (CONTROL_NumJoyAxes)
    {
        int32 j, i = CONTROL_NumJoyAxes-1;

        do
        {
            if (!CONTROL_JoyAxes[i].digital) continue;

            if (CONTROL_JoyAxes[i].digital < 0)
                j = CONTROL_JoyAxesMap[i].minmap;
            else
                j = CONTROL_JoyAxesMap[i].maxmap;

            if (j != AXISUNDEFINED)
                p1[j] = 1;
        }
        while (i--);
    }
}

void CONTROL_ButtonFunctionState(int32 *p1)
{
    if (CONTROL_NumMouseButtons)
    {
        int32 i = CONTROL_NumMouseButtons-1, j;

        do
        {
            if (!mousebind[i].cmd[0])
            {
                j = CONTROL_MouseButtonMapping[i].doubleclicked;
                if (j != KEYUNDEFINED)
                    p1[j] |= CONTROL_MouseButtonClickedState[i];

                j = CONTROL_MouseButtonMapping[i].singleclicked;
                if (j != KEYUNDEFINED)
                    p1[j] |= CONTROL_MouseButtonState[i];
            }

            if (!bindsenabled)
                continue;

            if (mousebind[i].cmd[0] && CONTROL_MouseButtonState[i])
            {
                if (mousebind[i].repeat || (mousebind[i].laststate == 0))
                    OSD_Dispatch(mousebind[i].cmd);
            }
            mousebind[i].laststate = CONTROL_MouseButtonState[i];
        }
        while (i--);
    }

    if (CONTROL_NumJoyButtons)
    {
        int32 i=CONTROL_NumJoyButtons-1, j;

        do
        {
            j = CONTROL_JoyButtonMapping[i].doubleclicked;
            if (j != KEYUNDEFINED)
                p1[j] |= CONTROL_JoyButtonClickedState[i];

            j = CONTROL_JoyButtonMapping[i].singleclicked;
            if (j != KEYUNDEFINED)
                p1[j] |= CONTROL_JoyButtonState[i];
        }
        while (i--);
    }
}
/*
void CONTROL_GetUserInput( UserInput *info )
{
	ControlInfo ci;

	CONTROL_PollDevices( &ci );

	info->dir = dir_None;

	// checks if CONTROL_UserInputDelay is too far in the future due to clock skew?
	if (GetTime() + ((ticrate * USERINPUTDELAY) / 1000) < CONTROL_UserInputDelay)
		CONTROL_UserInputDelay = -1;

	if (GetTime() >= CONTROL_UserInputDelay) {
		if (CONTROL_MouseAxes[1].digital == -1)
			info->dir = dir_North;
		else if (CONTROL_MouseAxes[1].digital == 1)
			info->dir = dir_South;
		else if (CONTROL_MouseAxes[0].digital == -1)
			info->dir = dir_West;
		else if (CONTROL_MouseAxes[0].digital == 1)
			info->dir = dir_East;

		if (CONTROL_JoyAxes[1].digital == -1)
			info->dir = dir_North;
		else if (CONTROL_JoyAxes[1].digital == 1)
			info->dir = dir_South;
		else if (CONTROL_JoyAxes[0].digital == -1)
			info->dir = dir_West;
		else if (CONTROL_JoyAxes[0].digital == 1)
			info->dir = dir_East;
	}

	info->button0 = CONTROL_MouseButtonState[0] | CONTROL_JoyButtonState[0];
	info->button1 = CONTROL_MouseButtonState[1] | CONTROL_JoyButtonState[1];

	if (KB_KeyDown[sc_kpad_8] || KB_KeyDown[sc_UpArrow])
		info->dir = dir_North;
	else if (KB_KeyDown[sc_kpad_2] || KB_KeyDown[sc_DownArrow])
		info->dir = dir_South;
	else if (KB_KeyDown[sc_kpad_4] || KB_KeyDown[sc_LeftArrow])
		info->dir = dir_West;
	else if (KB_KeyDown[sc_kpad_6] || KB_KeyDown[sc_RightArrow])
		info->dir = dir_East;

	if (KB_KeyDown[BUTTON0_SCAN_1] || KB_KeyDown[BUTTON0_SCAN_2] || KB_KeyDown[BUTTON0_SCAN_3])
		info->button0 = 1;
	if (KB_KeyDown[BUTTON1_SCAN])
		info->button1 = 1;

	if (CONTROL_UserInputCleared[1]) {
		if (!info->button0)
			CONTROL_UserInputCleared[1] = false;
		else
			info->button0 = false;
	}
	if (CONTROL_UserInputCleared[2]) {
		if (!info->button1)
			CONTROL_UserInputCleared[2] = false;
		else
			info->button1 = false;
	}
}

void CONTROL_ClearUserInput( UserInput *info )
{
	switch (info->dir) {
		case dir_North:
		case dir_South:
		case dir_East:
		case dir_West:
			CONTROL_UserInputCleared[0] = true;
			CONTROL_UserInputDelay = GetTime() + ((ticrate * USERINPUTDELAY) / 1000);
			switch (info->dir) {
				case dir_North: KB_KeyDown[sc_UpArrow]    = KB_KeyDown[sc_kpad_8] = 0; break;
				case dir_South: KB_KeyDown[sc_DownArrow]  = KB_KeyDown[sc_kpad_2] = 0; break;
				case dir_East:  KB_KeyDown[sc_LeftArrow]  = KB_KeyDown[sc_kpad_4] = 0; break;
				case dir_West:  KB_KeyDown[sc_RightArrow] = KB_KeyDown[sc_kpad_6] = 0; break;
				default: break;
			}
			break;
		default: break;
	}
	if (info->button0) CONTROL_UserInputCleared[1] = true;
	if (info->button1) CONTROL_UserInputCleared[2] = true;
}
*/
void CONTROL_ClearButton(int32 whichbutton)
{
    if (CONTROL_CheckRange(whichbutton)) return;
    BUTTONCLEAR(whichbutton);
    CONTROL_Flags[whichbutton].cleared = true;
}

void CONTROL_ProcessBinds(void)
{
    if (!bindsenabled) return;

    {
        int i=MAXBOUNDKEYS-1;

        do
        {
            if (boundkeys[i].cmd[0] && KB_KeyPressed(i))
            {
                if (boundkeys[i].repeat || (boundkeys[i].laststate == 0))
                    OSD_Dispatch(boundkeys[i].cmd);
            }
            boundkeys[i].laststate = KB_KeyPressed(i);
        }
        while (--i);
    }

    if (boundkeys[0].cmd[0] && KB_KeyPressed(0))
    {
        if (boundkeys[0].repeat || (boundkeys[0].laststate == 0))
            OSD_Dispatch(boundkeys[0].cmd);
    }
    boundkeys[0].laststate = KB_KeyPressed(0);
}


void CONTROL_GetInput(ControlInfo *info)
{
    int32 periphs[CONTROL_NUM_FLAGS];

    CONTROL_PollDevices(info);

    memset(periphs, 0, sizeof(periphs));
    CONTROL_ButtonFunctionState(periphs);
    CONTROL_AxisFunctionState(periphs);

    CONTROL_ButtonHeldState1 = CONTROL_ButtonState1;
    CONTROL_ButtonHeldState2 = CONTROL_ButtonState2;
    CONTROL_ButtonState1 = CONTROL_ButtonState2 = 0;

    CONTROL_ProcessBinds();

    {
        int32 i = CONTROL_NUM_FLAGS-1;

        do
        {
            CONTROL_SetFlag(i, CONTROL_KeyboardFunctionPressed(i) | periphs[i] | extinput[i]);

            if (CONTROL_Flags[i].cleared == false) BUTTONSET(i, CONTROL_Flags[i].active);
            else if (CONTROL_Flags[i].active == false) CONTROL_Flags[i].cleared = 0;
        }
        while (--i);

        CONTROL_SetFlag(0, CONTROL_KeyboardFunctionPressed(0) | periphs[0] | extinput[0]);
        if (CONTROL_Flags[0].cleared == false) BUTTONSET(0, CONTROL_Flags[0].active);
        else if (CONTROL_Flags[0].active == false) CONTROL_Flags[0].cleared = 0;
    }

    memset(extinput, 0, sizeof(extinput));
}

void CONTROL_WaitRelease(void)
{
}

void CONTROL_Ack(void)
{
}

boolean CONTROL_Startup(controltype which, int32(*TimeFunction)(void), int32 ticspersecond)
{
    int32 i;

    UNREFERENCED_PARAMETER(which);

    if (CONTROL_Started) return false;

    if (TimeFunction) GetTime = TimeFunction;
    else GetTime = CONTROL_GetTime;

    ticrate = ticspersecond;

    CONTROL_DoubleClickSpeed = (ticspersecond*57)/100;
    if (CONTROL_DoubleClickSpeed <= 0)
        CONTROL_DoubleClickSpeed = 1;

    if (initinput()) return true;

    CONTROL_MousePresent = CONTROL_MouseEnabled    = false;
    CONTROL_JoyPresent   = CONTROL_JoystickEnabled = false;
    CONTROL_NumMouseButtons = CONTROL_NumJoyButtons = 0;
    CONTROL_NumMouseAxes    = CONTROL_NumJoyAxes    = 0;
    KB_Startup();

    //switch (which) {
    //	case controltype_keyboard:
    //		break;

    //	case controltype_keyboardandmouse:
    CONTROL_NumMouseAxes      = MAXMOUSEAXES;
    CONTROL_NumMouseButtons   = MAXMOUSEBUTTONS;
    CONTROL_MousePresent      = MOUSE_Init();
    CONTROL_MouseEnabled      = CONTROL_MousePresent;
    //		break;

    //	case controltype_keyboardandjoystick:
    CONTROL_NumJoyAxes    = min(MAXJOYAXES,joynumaxes);
    CONTROL_NumJoyButtons = min(MAXJOYBUTTONS,joynumbuttons + 4*(joynumhats>0));
    CONTROL_JoyPresent    = CONTROL_StartJoy(0);;
    CONTROL_JoystickEnabled = CONTROL_JoyPresent;
    //		break;
    //}

    /*
    if (CONTROL_MousePresent)
        initprintf("CONTROL_Startup: Mouse Present\n");
    if (CONTROL_JoyPresent)
        initprintf("CONTROL_Startup: Joystick Present\n");
    */

    CONTROL_ButtonState1     = 0;
    CONTROL_ButtonState2     = 0;
    CONTROL_ButtonHeldState1 = 0;
    CONTROL_ButtonHeldState2 = 0;

    memset(CONTROL_UserInputCleared, 0, sizeof(CONTROL_UserInputCleared));

    for (i=0; i<CONTROL_NUM_FLAGS; i++)
        CONTROL_Flags[i].used = false;

    CONTROL_Started = true;

    return false;
}

void CONTROL_Shutdown(void)
{
    if (!CONTROL_Started) return;

    CONTROL_JoyPresent = false;

    MOUSE_Shutdown();
    uninitinput();

    CONTROL_Started = false;
}

