// Copyright (c) 2021-2021, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto.  Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#include <OgnReadKeyboardStateDatabase.h>

#include <carb/input/IInput.h>
#include <carb/input/InputTypes.h>

#include <omni/kit/IAppWindow.h>

using namespace carb::input;

namespace omni {
namespace graph {
namespace action {

// This list matches carb::input::KeyboardInput
constexpr size_t s_numNames = size_t(carb::input::KeyboardInput::eCount);
static constexpr std::array<const char*, s_numNames> s_keyNames = {
    "Unknown",
    "Space",
    "Apostrophe",
    "Comma",
    "Minus",
    "Period",
    "Slash",
    "Key0",
    "Key1",
    "Key2",
    "Key3",
    "Key4",
    "Key5",
    "Key6",
    "Key7",
    "Key8",
    "Key9",
    "Semicolon",
    "Equal",
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "O",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z",
    "LeftBracket",
    "Backslash",
    "RightBracket",
    "GraveAccent",
    "Escape",
    "Tab",
    "Enter",
    "Backspace",
    "Insert",
    "Del",
    "Right",
    "Left",
    "Down",
    "Up",
    "PageUp",
    "PageDown",
    "Home",
    "End",
    "CapsLock",
    "ScrollLock",
    "NumLock",
    "PrintScreen",
    "Pause",
    "F1",
    "F2",
    "F3",
    "F4",
    "F5",
    "F6",
    "F7",
    "F8",
    "F9",
    "F10",
    "F11",
    "F12",
    "Numpad0",
    "Numpad1",
    "Numpad2",
    "Numpad3",
    "Numpad4",
    "Numpad5",
    "Numpad6",
    "Numpad7",
    "Numpad8",
    "Numpad9",
    "NumpadDel",
    "NumpadDivide",
    "NumpadMultiply",
    "NumpadSubtract",
    "NumpadAdd",
    "NumpadEnter",
    "NumpadEqual",
    "LeftShift",
    "LeftControl",
    "LeftAlt",
    "LeftSuper",
    "RightShift",
    "RightControl",
    "RightAlt",
    "RightSuper",
    "Menu"
};
static_assert(s_keyNames.size() == size_t(carb::input::KeyboardInput::eCount), "enum must match this table");

static std::array<NameToken, s_numNames> s_keyTokens;

class OgnReadKeyboardState
{
public:

    static bool compute(OgnReadKeyboardStateDatabase& db)
    {
        static NameToken const emptyToken = db.stringToToken("");
        NameToken const& keyIn = db.inputs.key();
        auto contextObj = db.abi_context();

        // First time look up all the token string values
        static bool callOnce = ([&contextObj] {
            std::transform(s_keyNames.begin(), s_keyNames.end(), s_keyTokens.begin(),
                [&contextObj](auto const& s) { return contextObj.iToken->getHandle(s); });
        } (), true);

        omni::kit::IAppWindow* appWindow = omni::kit::getDefaultAppWindow();
        if (!appWindow)
        {
            return false;
        }

        Keyboard* keyboard = appWindow->getKeyboard();
        if (!keyboard)
        {
            CARB_LOG_ERROR_ONCE("No Keyboard!");
            return false;
        }

        IInput* input = carb::getCachedInterface<IInput>();
        if (!input)
        {
            CARB_LOG_ERROR_ONCE("No Input!");
            return false;
        }

        bool isPressed = false;
        // Get the index of the token of the key of interest
        auto iter = std::find(s_keyTokens.begin(), s_keyTokens.end(), keyIn);
        if (iter != s_keyTokens.end())
        {
            size_t index = iter - s_keyTokens.begin();
            KeyboardInput key = KeyboardInput(index);

            isPressed = (carb::input::kButtonFlagStateDown & input->getKeyboardButtonFlags(keyboard, key));
        }
        db.outputs.shiftOut() =
            (carb::input::kButtonFlagStateDown & input->getKeyboardButtonFlags(keyboard, KeyboardInput::eLeftShift)) ||
            (carb::input::kButtonFlagStateDown & input->getKeyboardButtonFlags(keyboard, KeyboardInput::eRightShift));
        db.outputs.ctrlOut() =
            (carb::input::kButtonFlagStateDown & input->getKeyboardButtonFlags(keyboard, KeyboardInput::eLeftControl)) ||
            (carb::input::kButtonFlagStateDown & input->getKeyboardButtonFlags(keyboard, KeyboardInput::eRightControl));
        db.outputs.altOut() =
            (carb::input::kButtonFlagStateDown & input->getKeyboardButtonFlags(keyboard, KeyboardInput::eLeftAlt)) ||
            (carb::input::kButtonFlagStateDown & input->getKeyboardButtonFlags(keyboard, KeyboardInput::eRightAlt));
        db.outputs.isPressed() = isPressed;
        return true;
    }
};

REGISTER_OGN_NODE()

} // namespace nodes
} // namespace graph
} // namespace omni
