/* Copyright (C) 2024 Kevin Koster, OmberTech. All rights reserved.

 This software may be distributed and modified under the terms of the GNU
 General Public License version 2 (GPL2) as published by the Free Software
 Foundation and appearing in the file GPL2.TXT included in the packaging of
 this file. Please note that GPL2 Section 2[b] requires that all works based
 on this software must also be made publicly available under the terms of
 the GPL2 ("Copyleft").
*/

#if !defined(__HIDJOYPARSER_H__)
#define __HIDJOYPARSER_H__

#include "Usb.h"
#include "hid_report_parser/hid_host_joy.h"
#include "controllerEnums.h"

/* In case HID descriptor request fails, grab as many bytes as we dare */
#define FALLBACK_HID_REPORT_DESCRIPTOR_SIZE	300

/** Buttons on gamepads */
const uint8_t HIDJOY_BUTTONS[] PROGMEM = {
        UP, // UP
        RIGHT, // RIGHT
        DOWN, // DOWN
        LEFT, // LEFT

        0x0C, // SELECT
        0x0D, // START
        0x0E, // L3
        0x0F, // R3

        0x0A, // L2
        0x0B, // R2
        0x08, // L1
        0x09, // R1

        0x07, // TRIANGLE
        0x06, // CIRCLE
        0x05, // CROSS
        0x04, // SQUARE

        0x10, // PS
        0x11, // TOUCHPAD
};

union HIDJoyButtons {
        struct {
                uint8_t dpad : 4;
                uint8_t square : 1;
                uint8_t cross : 1;
                uint8_t circle : 1;
                uint8_t triangle : 1;

                uint8_t l1 : 1;
                uint8_t r1 : 1;
                uint8_t l2 : 1;
                uint8_t r2 : 1;
                uint8_t share : 1;
                uint8_t options : 1;
                uint8_t l3 : 1;
                uint8_t r3 : 1;

                uint8_t ps : 1;
                uint8_t touchpad : 1;
                uint8_t reportCounter : 6;
        } __attribute__((packed));
        uint32_t val : 24;
} __attribute__((packed));

class HIDJoyParser {
public:
        /** Constructor for the HIDJoyParser class. */
        HIDJoyParser() {
                Reset();
        };

        /** @name joystick functions */
        /**
         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
         *
         * While getButtonClick(ButtonEnum b) will only return it once.
         *
         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
         * @param  b          ::ButtonEnum to read.
         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
         */
        bool getButtonPress(ButtonEnum b);
        bool getButtonClick(ButtonEnum b);
        /**@}*/
        /** @name joystick functions */
        /**
         * Used to read the analog joystick.
         * @param  a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
         * @return   Return the analog value in the range of 0-255.
         */
        uint8_t getAnalogHat(AnalogHatEnum a);

protected:
        bool initDone;

        /**
         * Used to parse data sent from the joystick.
         * @param len Length of the data.
         * @param buf Pointer to the data buffer.
         */
        void Parse(uint8_t len, uint8_t *buf);

        /** Used to reset the different buffers to their default values */
        void Reset();
        
        /** Read HID report descriptor and set up report parser accordingly */
        void Init();

        /**
         * Get the HID Report Descriptor from the device.
         * This is implemented in HIDJoyUSB.h (HIDJoyBT.h TODO).
         * @param desc_len Pointer to variable to write the HID Report Descriptor
         *                 size in bytes, read from HID Descriptor's wDescriptorLength value.
         * @return         Return a pointer to the HID Report Descriptor, or NULL on failure.
         */
        virtual uint8_t* getHIDReportDescriptor(uint16_t *desc_len) = 0;

private:
        bool checkDpad(ButtonEnum b); // Used to check gamepad DPAD buttons

        tusb_hid_simple_joysick_values_t oldJoyValues;
        HIDJoyButtons oldButtonState, buttonClickState;
        uint8_t oldDpad;
        int32_t oldHat;
};

#endif // __HIDJOYPARSER_H__
