/* Copyright (C) 2019 Mr Goldberg
   This file is part of the Goldberg Emulator

   The Goldberg Emulator is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   The Goldberg Emulator is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the Goldberg Emulator; if not, see
   <http://www.gnu.org/licenses/>.  */

#include "base.h"
#ifndef CONTROLLER_SUPPORT
inline void GamepadInit(void) {}
inline void GamepadShutdown(void) {}
inline void GamepadUpdate(void) {}
inline GAMEPAD_BOOL GamepadIsConnected(GAMEPAD_DEVICE device) { return GAMEPAD_FALSE; }
inline GAMEPAD_BOOL GamepadButtonDown(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) { return GAMEPAD_FALSE; }
inline float GamepadTriggerLength(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) { return 0.0; }
inline GAMEPAD_STICKDIR GamepadStickDir(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) { return STICKDIR_CENTER; }
inline void GamepadStickNormXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, float* outX, float* outY) {}
inline float GamepadStickLength(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) { return 0.0; }
inline void GamepadSetRumble(GAMEPAD_DEVICE device, float left, float right,  unsigned int rumble_length_ms) {}
#endif

struct Controller_Map {
    std::map<ControllerDigitalActionHandle_t, std::set<int>> active_digital;
    std::map<ControllerAnalogActionHandle_t, std::pair<std::set<int>, enum EInputSourceMode>> active_analog;
};

struct Controller_Action {
    ControllerHandle_t controller_handle;
    struct Controller_Map active_map;
    ControllerDigitalActionHandle_t active_set;

    Controller_Action(ControllerHandle_t controller_handle) {
        this->controller_handle = controller_handle;
    }

    void activate_action_set(ControllerDigitalActionHandle_t active_set, std::map<ControllerActionSetHandle_t, struct Controller_Map> &controller_maps) {
        auto map = controller_maps.find(active_set);
        if (map == controller_maps.end()) return;
        this->active_set = active_set;
        this->active_map = map->second;
    }

    std::set<int> button_id(ControllerDigitalActionHandle_t handle) {
        auto a = active_map.active_digital.find(handle);
        if (a == active_map.active_digital.end()) return {};
        return a->second;
    }

    std::pair<std::set<int>, enum EInputSourceMode> analog_id(ControllerAnalogActionHandle_t handle) {
        auto a = active_map.active_analog.find(handle);
        if (a == active_map.active_analog.end()) return std::pair<std::set<int>, enum EInputSourceMode>({}, k_EInputSourceMode_None);
        return a->second;
    }
};

enum EXTRA_GAMEPAD_BUTTONS {
    BUTTON_LTRIGGER = BUTTON_COUNT + 1,
    BUTTON_RTRIGGER = BUTTON_COUNT + 2,
    BUTTON_STICK_LEFT_UP = BUTTON_COUNT + 3,
    BUTTON_STICK_LEFT_DOWN = BUTTON_COUNT + 4,
    BUTTON_STICK_LEFT_LEFT = BUTTON_COUNT + 5,
    BUTTON_STICK_LEFT_RIGHT = BUTTON_COUNT + 6,
    BUTTON_STICK_RIGHT_UP = BUTTON_COUNT + 7,
    BUTTON_STICK_RIGHT_DOWN = BUTTON_COUNT + 8,
    BUTTON_STICK_RIGHT_LEFT = BUTTON_COUNT + 9,
    BUTTON_STICK_RIGHT_RIGHT = BUTTON_COUNT + 10,
};

#define JOY_ID_START 10

class Steam_Controller :
public ISteamController001,
public ISteamController003,
public ISteamController004,
public ISteamController005,
public ISteamController006,
public ISteamController007,
public ISteamController,
public ISteamInput001,
public ISteamInput
{
    class Settings *settings;
    class SteamCallResults *callback_results;
    class SteamCallBacks *callbacks;
    class RunEveryRunCB *run_every_runcb;

    std::map<std::string, int> button_strings = {
        {"DUP", BUTTON_DPAD_UP},
        {"DDOWN", BUTTON_DPAD_DOWN},
        {"DLEFT", BUTTON_DPAD_LEFT},
        {"DRIGHT", BUTTON_DPAD_RIGHT},
        {"START", BUTTON_START},
        {"BACK", BUTTON_BACK},
        {"LSTICK", BUTTON_LEFT_THUMB},
        {"RSTICK", BUTTON_RIGHT_THUMB},
        {"LBUMPER", BUTTON_LEFT_SHOULDER},
        {"RBUMPER", BUTTON_RIGHT_SHOULDER},
        {"A", BUTTON_A},
        {"B", BUTTON_B},
        {"X", BUTTON_X},
        {"Y", BUTTON_Y},
        {"DLTRIGGER", BUTTON_LTRIGGER},
        {"DRTRIGGER", BUTTON_RTRIGGER},
        {"DLJOYUP", BUTTON_STICK_LEFT_UP},
        {"DLJOYDOWN", BUTTON_STICK_LEFT_DOWN},
        {"DLJOYLEFT", BUTTON_STICK_LEFT_LEFT},
        {"DLJOYRIGHT", BUTTON_STICK_LEFT_RIGHT},
        {"DRJOYUP", BUTTON_STICK_RIGHT_UP},
        {"DRJOYDOWN", BUTTON_STICK_RIGHT_DOWN},
        {"DRJOYLEFT", BUTTON_STICK_RIGHT_LEFT},
        {"DRJOYRIGHT", BUTTON_STICK_RIGHT_RIGHT},
    };

    std::map<std::string, int> analog_strings = {
        {"LTRIGGER", TRIGGER_LEFT},
        {"RTRIGGER", TRIGGER_RIGHT},
        {"LJOY", STICK_LEFT + JOY_ID_START},
        {"RJOY", STICK_RIGHT + JOY_ID_START},
    };

    std::map<std::string, enum EInputSourceMode> analog_input_modes = {
        {"joystick_move", k_EInputSourceMode_JoystickMove},
        {"joystick_camera", k_EInputSourceMode_JoystickCamera},
        {"trigger", k_EInputSourceMode_Trigger},
    };

    std::map<std::string, ControllerActionSetHandle_t> action_handles;
    std::map<std::string, ControllerDigitalActionHandle_t> digital_action_handles;
    std::map<std::string, ControllerAnalogActionHandle_t> analog_action_handles;

    std::map<ControllerActionSetHandle_t, struct Controller_Map> controller_maps;
    std::map<ControllerHandle_t, struct Controller_Action> controllers;

    std::map<EInputActionOrigin, std::string> steaminput_glyphs;
    std::map<EControllerActionOrigin, std::string> steamcontroller_glyphs;

    bool disabled;
    bool initialized;

    void set_handles(std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_sets) {
        uint64 handle_num = 1;
        for (auto & set : action_sets) {
            ControllerActionSetHandle_t action_handle_num = handle_num;
            ++handle_num;

            action_handles[set.first] = action_handle_num;
            for (auto & config_key : set.second) {
                uint64 current_handle_num = handle_num;
                ++handle_num;

                for (auto & button_string : config_key.second.first) {
                    auto digital = button_strings.find(button_string);
                    if (digital != button_strings.end()) {
                        ControllerDigitalActionHandle_t digital_handle_num = current_handle_num;

                        if (digital_action_handles.find(config_key.first) == digital_action_handles.end()) {
                            digital_action_handles[config_key.first] = digital_handle_num;
                        } else {
                            digital_handle_num = digital_action_handles[config_key.first];
                        }

                        controller_maps[action_handle_num].active_digital[digital_handle_num].insert(digital->second);
                    } else {
                        auto analog = analog_strings.find(button_string);
                        if (analog != analog_strings.end()) {
                            ControllerAnalogActionHandle_t analog_handle_num = current_handle_num;

                            enum EInputSourceMode source_mode;
                            if (analog->second == TRIGGER_LEFT || analog->second == TRIGGER_RIGHT) {
                                source_mode = k_EInputSourceMode_Trigger;
                            } else {
                                source_mode = k_EInputSourceMode_JoystickMove;
                            }

                            auto input_mode = analog_input_modes.find(config_key.second.second);
                            if (input_mode != analog_input_modes.end()) {
                                source_mode = input_mode->second;
                            }

                            if (analog_action_handles.find(config_key.first) == analog_action_handles.end()) {
                                analog_action_handles[config_key.first] = analog_handle_num;
                            } else {
                                analog_handle_num = analog_action_handles[config_key.first];
                            }

                            controller_maps[action_handle_num].active_analog[analog_handle_num].first.insert(analog->second);
                            controller_maps[action_handle_num].active_analog[analog_handle_num].second = source_mode;

                        } else {
                            PRINT_DEBUG("Did not recognize controller button %s\n", button_string.c_str());
                            continue;
                        }
                    }
                }
            }
        }
    }

public:

static void steam_run_every_runcb(void *object)
{
    PRINT_DEBUG("steam_controller_run_every_runcb\n");

    Steam_Controller *steam_controller = (Steam_Controller *)object;
    steam_controller->RunCallbacks();
}

Steam_Controller(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
    this->settings = settings;
    this->run_every_runcb = run_every_runcb;
    this->run_every_runcb->add(&Steam_Controller::steam_run_every_runcb, this);

    this->callback_results = callback_results;
    this->callbacks = callbacks;

    set_handles(settings->controller_settings.action_sets);
    disabled = !action_handles.size();
    initialized = false;
}

~Steam_Controller()
{
    //TODO rm network callbacks
    this->run_every_runcb->remove(&Steam_Controller::steam_run_every_runcb, this);
}

// Init and Shutdown must be called when starting/ending use of this interface
bool Init()
{
    PRINT_DEBUG("Steam_Controller::Init()\n");
    std::lock_guard<std::recursive_mutex> lock(global_mutex);
    if (disabled) {
        return true;
    }

    GamepadInit();
    GamepadUpdate();

    for (int i = 1; i < 5; ++i) {
        struct Controller_Action cont_action(i);
        //Activate the action set if there is only one present.
        //TODO: I don't know if one gets activated by default when there's more than one
        if (action_handles.size() == 1) {
            cont_action.activate_action_set(action_handles.begin()->second, controller_maps);
        }

        controllers.insert(std::pair<ControllerHandle_t, struct Controller_Action>(i, cont_action));
    }

    initialized = true;
    return true;
}

bool Init( const char *pchAbsolutePathToControllerConfigVDF )
{
    PRINT_DEBUG("Steam_Controller::Init() old\n");
    return Init();
}

bool Shutdown()
{
    PRINT_DEBUG("Steam_Controller::Shutdown()\n");
    std::lock_guard<std::recursive_mutex> lock(global_mutex);
    if (disabled) {
        return true;
    }

    GamepadShutdown();
    return true;
}

void SetOverrideMode( const char *pchMode )
{
    PRINT_DEBUG("Steam_Controller::SetOverrideMode\n");
}

// Synchronize API state with the latest Steam Controller inputs available. This
// is performed automatically by SteamAPI_RunCallbacks, but for the absolute lowest
// possible latency, you call this directly before reading controller state.
void RunFrame()
{
    PRINT_DEBUG("Steam_Controller::RunFrame()\n");
    if (disabled || !initialized) {
        return;
    }

    GamepadUpdate();
}

bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pState )
{
    PRINT_DEBUG("Steam_Controller::GetControllerState()\n");
    return false;
}

// Enumerate currently connected controllers
// handlesOut should point to a STEAM_CONTROLLER_MAX_COUNT sized array of ControllerHandle_t handles
// Returns the number of handles written to handlesOut
int GetConnectedControllers( ControllerHandle_t *handlesOut )
{
    PRINT_DEBUG("Steam_Controller::GetConnectedControllers\n");
    if (!handlesOut) return 0;
    if (disabled) {
        return 0;
    }

    int count = 0;
    if (GamepadIsConnected(GAMEPAD_0)) {*handlesOut = GAMEPAD_0 + 1; ++handlesOut; ++count;};
    if (GamepadIsConnected(GAMEPAD_1)) {*handlesOut = GAMEPAD_1 + 1; ++handlesOut; ++count;};
    if (GamepadIsConnected(GAMEPAD_2)) {*handlesOut = GAMEPAD_2 + 1; ++handlesOut; ++count;};
    if (GamepadIsConnected(GAMEPAD_3)) {*handlesOut = GAMEPAD_3 + 1; ++handlesOut; ++count;};

    PRINT_DEBUG("returned %i connected controllers\n", count);
    return count;
}


// Invokes the Steam overlay and brings up the binding screen
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowBindingPanel( ControllerHandle_t controllerHandle )
{
    PRINT_DEBUG("Steam_Controller::ShowBindingPanel\n");
    return false;
}


// ACTION SETS
// Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls.
ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName )
{
    PRINT_DEBUG("Steam_Controller::GetActionSetHandle %s\n", pszActionSetName);
    if (!pszActionSetName) return 0;
    std::string upper_action_name(pszActionSetName);
    std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); });

    auto set_handle = action_handles.find(upper_action_name);
    if (set_handle == action_handles.end()) return 0;

    return set_handle->second;
}


// Reconfigure the controller to use the specified action set (ie 'Menu', 'Walk' or 'Drive')
// This is cheap, and can be safely called repeatedly. It's often easier to repeatedly call it in
// your state loops, instead of trying to place it in all of your state transitions.
void ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle )
{
    PRINT_DEBUG("Steam_Controller::ActivateActionSet %llu %llu\n", controllerHandle, actionSetHandle);
    if (controllerHandle == STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS) {
        for (auto & c: controllers) {
            c.second.activate_action_set(actionSetHandle, controller_maps);
        }
    }

    auto controller = controllers.find(controllerHandle);
    if (controller == controllers.end()) return;

    controller->second.activate_action_set(actionSetHandle, controller_maps);
}

ControllerActionSetHandle_t GetCurrentActionSet( ControllerHandle_t controllerHandle )
{
    PRINT_DEBUG("Steam_Controller::GetCurrentActionSet %llu\n", controllerHandle);
    auto controller = controllers.find(controllerHandle);
    if (controller == controllers.end()) return 0;

    return controller->second.active_set;
}


void ActivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
    PRINT_DEBUG("Steam_Controller::ActivateActionSetLayer\n");
}

void DeactivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
    PRINT_DEBUG("Steam_Controller::DeactivateActionSetLayer\n");
}

void DeactivateAllActionSetLayers( ControllerHandle_t controllerHandle )
{
    PRINT_DEBUG("Steam_Controller::DeactivateAllActionSetLayers\n");
}

int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t *handlesOut )
{
    PRINT_DEBUG("Steam_Controller::GetActiveActionSetLayers\n");
    return 0;
}



// ACTIONS
// Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls.
ControllerDigitalActionHandle_t GetDigitalActionHandle( const char *pszActionName )
{
    PRINT_DEBUG("Steam_Controller::GetDigitalActionHandle %s\n", pszActionName);
    if (!pszActionName) return 0;
    std::string upper_action_name(pszActionName);
    std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); });

    auto handle = digital_action_handles.find(upper_action_name);
    if (handle == digital_action_handles.end()) return 0;

    return handle->second;
}


// Returns the current state of the supplied digital game action
ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle )
{
    PRINT_DEBUG("Steam_Controller::GetDigitalActionData %llu %llu\n", controllerHandle, digitalActionHandle);
    ControllerDigitalActionData_t digitalData;
    digitalData.bActive = false;
    digitalData.bState = false;

    auto controller = controllers.find(controllerHandle);
    if (controller == controllers.end()) return digitalData;

    std::set<int> buttons = controller->second.button_id(digitalActionHandle);
    if (!buttons.size()) return digitalData;
    digitalData.bActive = true;

    GAMEPAD_DEVICE device = (GAMEPAD_DEVICE)(controllerHandle - 1);

    for (auto button : buttons) {
        bool pressed = false;
        if (button < BUTTON_COUNT) {
            pressed = GamepadButtonDown(device, (GAMEPAD_BUTTON)button);
        } else {
            switch (button) {
                case BUTTON_LTRIGGER:
                    pressed = GamepadTriggerLength(device, TRIGGER_LEFT) > 0.8;
                    break;
                case BUTTON_RTRIGGER:
                    pressed = GamepadTriggerLength(device, TRIGGER_RIGHT) > 0.8;
                    break;
                case BUTTON_STICK_LEFT_UP:
                case BUTTON_STICK_LEFT_DOWN:
                case BUTTON_STICK_LEFT_LEFT:
                case BUTTON_STICK_LEFT_RIGHT:
                    pressed = GamepadStickLength(device, STICK_LEFT) > 0.1 &&
                                        ((int)GamepadStickDir(device, STICK_LEFT) == ((button - BUTTON_STICK_LEFT_UP) + 1));
                    break;
                case BUTTON_STICK_RIGHT_UP:
                case BUTTON_STICK_RIGHT_DOWN:
                case BUTTON_STICK_RIGHT_LEFT:
                case BUTTON_STICK_RIGHT_RIGHT:
                    pressed = GamepadStickLength(device, STICK_RIGHT) > 0.1 &&
                                        ((int)GamepadStickDir(device, STICK_RIGHT) == ((button - BUTTON_STICK_RIGHT_UP) + 1));
                    break;
                default:
                    break;
            }
        }

        if (pressed) {
            digitalData.bState = true;
            break;
        }
    }

    return digitalData;
}


// Get the origin(s) for a digital action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action.
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut )
{
    PRINT_DEBUG("Steam_Controller::GetDigitalActionOrigins\n");
    EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
    int ret = GetDigitalActionOrigins(controllerHandle, actionSetHandle, digitalActionHandle, origins );
    for (int i = 0; i < ret; ++i) {
        originsOut[i] = (EControllerActionOrigin)(origins[i] - (k_EInputActionOrigin_XBox360_A - k_EControllerActionOrigin_XBox360_A));
    }

    return ret;
}

int GetDigitalActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin *originsOut )
{
    PRINT_DEBUG("Steam_Controller::GetDigitalActionOrigins steaminput\n");
    auto controller = controllers.find(inputHandle);
    if (controller == controllers.end()) return 0;

    auto map = controller_maps.find(actionSetHandle);
    if (map == controller_maps.end()) return 0;

    auto a = map->second.active_digital.find(digitalActionHandle);
    if (a == map->second.active_digital.end()) return 0;

    int count = 0;
    for (auto button: a->second) {
        switch (button) {
            case BUTTON_A:
                originsOut[count] = k_EInputActionOrigin_XBox360_A;
                break;
            case BUTTON_B:
                originsOut[count] = k_EInputActionOrigin_XBox360_B;
                break;
            case BUTTON_X:
                originsOut[count] = k_EInputActionOrigin_XBox360_X;
                break;
            case BUTTON_Y:
                originsOut[count] = k_EInputActionOrigin_XBox360_Y;
                break;
            case BUTTON_LEFT_SHOULDER:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftBumper;
                break;
            case BUTTON_RIGHT_SHOULDER:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightBumper;
                break;
            case BUTTON_START:
                originsOut[count] = k_EInputActionOrigin_XBox360_Start;
                break;
            case BUTTON_BACK:
                originsOut[count] = k_EInputActionOrigin_XBox360_Back;
                break;
            case BUTTON_LTRIGGER:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Click;
                break;
            case BUTTON_RTRIGGER:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Click;
                break;
            case BUTTON_LEFT_THUMB:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Click;
                break;
            case BUTTON_RIGHT_THUMB:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Click;
                break;

            case BUTTON_STICK_LEFT_UP:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadNorth;
                break;
            case BUTTON_STICK_LEFT_DOWN:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadSouth;
                break;
            case BUTTON_STICK_LEFT_LEFT:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadWest;
                break;
            case BUTTON_STICK_LEFT_RIGHT:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadEast;
                break;

            case BUTTON_STICK_RIGHT_UP:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadNorth;
                break;
            case BUTTON_STICK_RIGHT_DOWN:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadSouth;
                break;
            case BUTTON_STICK_RIGHT_LEFT:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadWest;
                break;
            case BUTTON_STICK_RIGHT_RIGHT:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadEast;
                break;

            case BUTTON_DPAD_UP:
                originsOut[count] = k_EInputActionOrigin_XBox360_DPad_North;
                break;
            case BUTTON_DPAD_DOWN:
                originsOut[count] = k_EInputActionOrigin_XBox360_DPad_South;
                break;
            case BUTTON_DPAD_LEFT:
                originsOut[count] = k_EInputActionOrigin_XBox360_DPad_West;
                break;
            case BUTTON_DPAD_RIGHT:
                originsOut[count] = k_EInputActionOrigin_XBox360_DPad_East;
                break;

            default:
                originsOut[count] = k_EInputActionOrigin_None;
                break;
        }

        ++count;
        if (count >= STEAM_INPUT_MAX_ORIGINS) {
            break;
        }
    }

    return count;
}

// Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls.
ControllerAnalogActionHandle_t GetAnalogActionHandle( const char *pszActionName )
{
    PRINT_DEBUG("Steam_Controller::GetAnalogActionHandle %s\n", pszActionName);
    if (!pszActionName) return 0;
    std::string upper_action_name(pszActionName);
    std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); });

    auto handle = analog_action_handles.find(upper_action_name);
    if (handle == analog_action_handles.end()) return 0;

    return handle->second;
}


// Returns the current state of these supplied analog game action
ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle )
{
    PRINT_DEBUG("Steam_Controller::GetAnalogActionData %llu %llu\n", controllerHandle, analogActionHandle);
    ControllerAnalogActionData_t data;
    data.eMode = k_EInputSourceMode_None;
    data.x = data.y = 0;
    data.bActive = false;

    auto controller = controllers.find(controllerHandle);
    if (controller == controllers.end()) return data;

    auto analog = controller->second.analog_id(analogActionHandle);
    if (!analog.first.size()) return data;

    data.bActive = true;
    data.eMode = analog.second;

    for (auto a : analog.first) {
        if (a >= JOY_ID_START) {
            int joystick_id = a - JOY_ID_START;
            GamepadStickNormXY((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_STICK) joystick_id, &data.x, &data.y);
            float length = GamepadStickLength((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_STICK) joystick_id);
            data.x = data.x * length;
            data.y = data.y * length;
        } else {
            data.x = GamepadTriggerLength((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_TRIGGER) a);
        }

        if (data.x || data.y) {
            break;
        }
    }

    return data;
}


// Get the origin(s) for an analog action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action.
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut )
{
    PRINT_DEBUG("Steam_Controller::GetAnalogActionOrigins\n");
    EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
    int ret = GetAnalogActionOrigins(controllerHandle, actionSetHandle, analogActionHandle, origins );
    for (int i = 0; i < ret; ++i) {
        originsOut[i] = (EControllerActionOrigin)(origins[i] - (k_EInputActionOrigin_XBox360_A - k_EControllerActionOrigin_XBox360_A));
    }

    return ret;
}

int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin *originsOut )
{
    PRINT_DEBUG("Steam_Controller::GetAnalogActionOrigins steaminput\n");
    auto controller = controllers.find(inputHandle);
    if (controller == controllers.end()) return 0;

    auto map = controller_maps.find(actionSetHandle);
    if (map == controller_maps.end()) return 0;

    auto a = map->second.active_analog.find(analogActionHandle);
    if (a == map->second.active_analog.end()) return 0;

    int count = 0;
    for (auto b: a->second.first) {
        switch (b) {
            case TRIGGER_LEFT:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Pull;
                break;
            case TRIGGER_RIGHT:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Pull;
                break;
            case STICK_LEFT + JOY_ID_START:
                originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Move;
                break;
            case STICK_RIGHT + JOY_ID_START:
                originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Move;
                break;
            default:
                originsOut[count] = k_EInputActionOrigin_None;
                break;
        }

        ++count;
        if (count >= STEAM_INPUT_MAX_ORIGINS) {
            break;
        }
    }

    return count;
}

    
void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction )
{
    PRINT_DEBUG("Steam_Controller::StopAnalogActionMomentum %llu %llu\n", controllerHandle, eAction);
}


// Trigger a haptic pulse on a controller
void TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
    PRINT_DEBUG("Steam_Controller::TriggerHapticPulse\n");
}

void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
    PRINT_DEBUG("Steam_Controller::TriggerHapticPulse old\n");
    TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec );
}

// Trigger a pulse with a duty cycle of usDurationMicroSec / usOffMicroSec, unRepeat times.
// nFlags is currently unused and reserved for future use.
void TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags )
{
    PRINT_DEBUG("Steam_Controller::TriggerRepeatedHapticPulse\n");
}


// Tigger a vibration event on supported controllers.  
void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed )
{
    PRINT_DEBUG("Steam_Controller::TriggerVibration %hu %hu\n", usLeftSpeed, usRightSpeed);
    auto controller = controllers.find(controllerHandle);
    if (controller == controllers.end()) return;

    unsigned int rumble_length_ms = 0;
#if defined(__linux__)
    //FIXME: shadow of the tomb raider on linux doesn't seem to turn off the rumble so I made it expire after 100ms. Need to check if this is how linux steam actually behaves.
    rumble_length_ms = 100;
#endif
    GamepadSetRumble((GAMEPAD_DEVICE)(controllerHandle - 1), ((double)usLeftSpeed) / 65535.0, ((double)usRightSpeed) / 65535.0, rumble_length_ms);
}


// Set the controller LED color on supported controllers.  
void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags )
{
    PRINT_DEBUG("Steam_Controller::SetLEDColor\n");
}


// Returns the associated gamepad index for the specified controller, if emulating a gamepad
int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle )
{
    PRINT_DEBUG("Steam_Controller::GetGamepadIndexForController\n");
    auto controller = controllers.find(ulControllerHandle);
    if (controller == controllers.end()) return -1;

    return ulControllerHandle - 1;
}


// Returns the associated controller handle for the specified emulated gamepad
ControllerHandle_t GetControllerForGamepadIndex( int nIndex )
{
    PRINT_DEBUG("Steam_Controller::GetControllerForGamepadIndex %i\n", nIndex);
    ControllerHandle_t out = nIndex + 1;
    auto controller = controllers.find(out);
    if (controller == controllers.end()) return 0;
    return out;
}


// Returns raw motion data from the specified controller
ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle )
{
    PRINT_DEBUG("Steam_Controller::GetMotionData\n");
    ControllerMotionData_t data = {};
    return data;
}


// Attempt to display origins of given action in the controller HUD, for the currently active action set
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition )
{
    PRINT_DEBUG("Steam_Controller::ShowDigitalActionOrigins\n");
    return true;
}

bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition )
{
    PRINT_DEBUG("Steam_Controller::ShowAnalogActionOrigins\n");
    return true;
}


// Returns a localized string (from Steam's language setting) for the specified origin
const char *GetStringForActionOrigin( EControllerActionOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetStringForActionOrigin\n");
    return "Button String";
}

const char *GetStringForActionOrigin( EInputActionOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetStringForActionOrigin steaminput\n");
    return "Button String";
}


// Get a local path to art for on-screen glyph for a particular origin 
const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin %i\n", eOrigin);

    if (steamcontroller_glyphs.empty()) {
        std::string dir = settings->glyphs_directory;
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_A] = dir + "button_a.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_B] = dir + "button_b.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_X] = dir + "button_x.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Y] = dir + "button_y.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftBumper] = dir + "shoulder_l.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightBumper] = dir + "shoulder_r.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Start] = dir + "xbox_button_start.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Back] = dir + "xbox_button_select.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftTrigger_Pull] = dir + "trigger_l_pull.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftTrigger_Click] = dir + "trigger_l_click.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightTrigger_Pull] = dir + "trigger_r_pull.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightTrigger_Click] = dir + "trigger_r_click.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_Move] = dir + "stick_l_move.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_Click] = dir + "stick_l_click.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadNorth] = dir + "stick_dpad_n.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadSouth] = dir + "stick_dpad_s.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadWest] = dir + "stick_dpad_w.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadEast] = dir + "stick_dpad_e.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_Move] = dir + "stick_r_move.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_Click] = dir + "stick_r_click.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadNorth] = dir + "stick_dpad_n.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadSouth] = dir + "stick_dpad_s.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadWest] = dir + "stick_dpad_w.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadEast] = dir + "stick_dpad_e.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_North] = dir + "xbox_button_dpad_n.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_South] = dir + "xbox_button_dpad_s.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_West] = dir + "xbox_button_dpad_w.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_East] = dir + "xbox_button_dpad_e.png";
        steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_Move] = dir + "xbox_button_dpad_move.png";
    }

    auto glyph = steamcontroller_glyphs.find(eOrigin);
    if (glyph == steamcontroller_glyphs.end()) return "";
    return glyph->second.c_str();
}

const char *GetGlyphForActionOrigin( EInputActionOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin steaminput %i\n", eOrigin);
    if (steaminput_glyphs.empty()) {
        std::string dir = settings->glyphs_directory;
        steaminput_glyphs[k_EInputActionOrigin_XBox360_A] = dir + "button_a.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_B] = dir + "button_b.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_X] = dir + "button_x.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_Y] = dir + "button_y.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftBumper] = dir + "shoulder_l.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightBumper] = dir + "shoulder_r.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_Start] = dir + "xbox_button_start.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_Back] = dir + "xbox_button_select.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftTrigger_Pull] = dir + "trigger_l_pull.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftTrigger_Click] = dir + "trigger_l_click.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightTrigger_Pull] = dir + "trigger_r_pull.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightTrigger_Click] = dir + "trigger_r_click.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_Move] = dir + "stick_l_move.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_Click] = dir + "stick_l_click.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadNorth] = dir + "stick_dpad_n.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadSouth] = dir + "stick_dpad_s.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadWest] = dir + "stick_dpad_w.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadEast] = dir + "stick_dpad_e.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_Move] = dir + "stick_r_move.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_Click] = dir + "stick_r_click.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadNorth] = dir + "stick_dpad_n.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadSouth] = dir + "stick_dpad_s.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadWest] = dir + "stick_dpad_w.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadEast] = dir + "stick_dpad_e.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_North] = dir + "xbox_button_dpad_n.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_South] = dir + "xbox_button_dpad_s.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_West] = dir + "xbox_button_dpad_w.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_East] = dir + "xbox_button_dpad_e.png";
        steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_Move] = dir + "xbox_button_dpad_move.png";
        //steaminput_glyphs[] = dir + "";
    }

    auto glyph = steaminput_glyphs.find(eOrigin);
    if (glyph == steaminput_glyphs.end()) return "";
    return glyph->second.c_str();
}

// Returns the input type for a particular handle
ESteamInputType GetInputTypeForHandle( ControllerHandle_t controllerHandle )
{
    PRINT_DEBUG("Steam_Controller::GetInputTypeForHandle %llu\n", controllerHandle);
    auto controller = controllers.find(controllerHandle);
    if (controller == controllers.end()) return k_ESteamInputType_Unknown;
    return k_ESteamInputType_XBox360Controller;
}

const char *GetStringForXboxOrigin( EXboxOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetStringForXboxOrigin\n");
    return "";
}

const char *GetGlyphForXboxOrigin( EXboxOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetGlyphForXboxOrigin\n");
    return "";
}

EControllerActionOrigin GetActionOriginFromXboxOrigin_( ControllerHandle_t controllerHandle, EXboxOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetActionOriginFromXboxOrigin\n");
    return k_EControllerActionOrigin_None;
}

EInputActionOrigin GetActionOriginFromXboxOrigin( InputHandle_t inputHandle, EXboxOrigin eOrigin )
{
    PRINT_DEBUG("Steam_Controller::GetActionOriginFromXboxOrigin steaminput\n");
    return k_EInputActionOrigin_None;
}

EControllerActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin )
{
    PRINT_DEBUG("Steam_Controller::TranslateActionOrigin\n");
    return k_EControllerActionOrigin_None;
}

EInputActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin )
{
    PRINT_DEBUG("Steam_Controller::TranslateActionOrigin steaminput\n");
    return k_EInputActionOrigin_None;
}

bool GetControllerBindingRevision( ControllerHandle_t controllerHandle, int *pMajor, int *pMinor )
{
    PRINT_DEBUG("Steam_Controller::GetControllerBindingRevision\n");
    return false;
}

bool GetDeviceBindingRevision( InputHandle_t inputHandle, int *pMajor, int *pMinor )
{
    PRINT_DEBUG("Steam_Controller::GetDeviceBindingRevision\n");
    return false;
}

uint32 GetRemotePlaySessionID( InputHandle_t inputHandle )
{
    PRINT_DEBUG("Steam_Controller::GetRemotePlaySessionID\n");
    return 0;
}

void RunCallbacks()
{
    RunFrame();
}

};
