#pragma once 
#include "../jabyengine_defines.hpp"

namespace JabyEngine {
    namespace Periphery {
        enum struct ControllerType : uint8_t {
            Unkown            = 0x0,
            Mouse             = 0x1,
            NegCon            = 0x2, 
            HyperBlaster      = 0x3, // Konami Lightgun
            Controller        = 0x4,
            ArcadeFlightStick = 0x5,
            GCon              = 0x6,
            DualShock         = 0x7,
            MultiTap          = 0x8
        };

        enum struct GenericButton : uint16_t {
            D8  = (1 << 0),
            D9  = (1 << 1),
            D10 = (1 << 2),
            D11 = (1 << 3),
            D12 = (1 << 4),
            D13 = (1 << 5),
            D14 = (1 << 6),
            D15 = (1 << 7),
            D0  = (1 << 8),
            D1  = (1 << 9),
            D2  = (1 << 10),
            D3  = (1 << 11),
            D4  = (1 << 12),
            D5  = (1 << 13),
            D6  = (1 << 14),
            D7  = (1 << 15)
        };

        struct LED {
            enum struct State : uint8_t {
                Off = 0x0,
                On  = 0x1
            };

            enum struct Lock {
                Off = 0x2,
                On  = 0x3
            };
        };

        class RawController {
        public:
            enum struct State : uint8_t
            {
                Disconnected    = 0,
                EnterConfigMode = (1 << 0),
                LockAnalog      = (1 << 1),
                UnlockRumble    = (1 << 2),
                ExitConfigMode  = (1 << 3),
                Stable          = (1 << 4)
            };

        protected:
            struct Header {
                uint8_t type;
                State   state;
                uint8_t rumble0;
                uint8_t rumble1;

                void clear() {
                    this->type  = 0;
                    this->state = State::Disconnected;
                }
            };

        public:
            class ButtonState {
            private:
                uint16_t oldState;
                uint16_t currentState;

                static bool is_down(uint16_t data, uint16_t button) {
                    return ((data & button) == 0);
                }

                void exchange_state() {
                    this->oldState = this->currentState;
                }

            public:
                template<typename T>
                bool is_down(T button) const {
                    return ButtonState::is_down(this->currentState, static_cast<uint16_t>(button));
                }

                template<typename T>
                bool was_down(T button) const {
                    return ButtonState::is_down(this->oldState, static_cast<uint16_t>(button));
                }

                template<typename T>
                bool went_down(T button) const {
                    return (!ButtonState::was_down(button) && ButtonState::is_down(button));
                }

                template<typename T>
                bool went_up(T button) const {
                    return (ButtonState::was_down(button) && !ButtonState::is_down(button));
                }

                friend class  RawController;
                friend struct ControllerHelper;
                friend void   query_controller();
            };

            Header      header;
            ButtonState button;
            uint32_t    special;

        public:
            ControllerType get_type() const {
                return static_cast<ControllerType>((this->header.type >> 4));
            }

            ButtonState get_button_state() const {
                return this->button;
            }

            //For debugging only
            uint8_t get_raw_type() const {
                return this->header.type;
            }

            uint16_t get_raw_button_state() const {
                return this->button.currentState;
            }
        };
    }
}