#pragma once
#include "pico/stdlib.h"
#include "hardware/uart.h"

#include "registers_map.hpp"

/**
 * @brief Interface to communicate with TMC2209 driver via UART
 * This implements basic read and write operations to the TMC2209 driver
 * It does not implement any register specific operations
 */
class TMC2209_UART {
private:
    uint8_t node_address;
    uart_inst_t *uart_inst;
    uint baudrate;
    uint tx_pin;
    uint rx_pin;


public:
    /**
     * @brief Construct a new tmc2209 uart object
     * This does not initialize the UART peripheral, it just sets the parameters
     * 
     * @param uart_id which UART to use (uart0 or uart1)
     * @param node_address this is the address of the TMC2209 driver set by ms1, ms2 pins (0-3)
     * @param baudrate UART baudrate to use (250k > baudrate > 2000, good value 115200, the tmc2209 has automatic baudrate detection)
     * @param tx_pin TX pin of RP2040. Depending on the UART peripheral, diffrent pins are valid
     * @param rx_pin RX pin of RP2040. Depending on the UART peripheral, diffrent pins are valid
     */
    TMC2209_UART(uart_inst_t *uart_inst, uint8_t node_address, uint baudrate, uint tx_pin, uint rx_pin);

    /**
     * @brief Initialize UART peripheral and pins
     * 
     */
    void init();

    /**
     * @brief Calculate CRC8-ATM checksum for the given data
     * the specific implementation is taken from TMC2209 datasheet
     * 
     * @param data Data to calculate CRC for
     * @param size Size of the data (without CRC byte)
     * @return uint8_t Calculated CRC8 checksum
     */
    uint8_t calc_crc8_atm(uint8_t *data, uint8_t size);
    
    /**
     * @brief Perform a read operation to the TMC2209 driver
     * This does not check if register is write only.
     * It will wait for the response from the driver
     * 
     * @param address 
     * @return uint32_t Data read from the register
     */
    uint32_t read(uint8_t address);

    /**
     * @brief Perform a write operation to the TMC2209 driver
     * This does not check if register is read only.
     * It will wait for the response from the driver
     * 
     * @param address Where to write the data (register Address)
     * @param value Data to write to the register
     */
    void write(uint8_t address, uint32_t value);

    /**
     * @brief This if any CRC error occured during the last read/write operation. Should be checked after each operation
     */
    bool crc_error_flag = false;
};

/**
 * @brief Higher level interface to TMC2209 driver
 * This class simplifies dealing with the registers of the TMC2209 driver
 * so you can just set the values
 */
class TMC2209_Registers {

private:
    TMC2209_UART uart_driver;

    REG_CHOPCONF chopconfBuffer;

public:
    TMC2209_Registers(TMC2209_UART uart_driver);

    void set_reg_gconf(
        bool i_scale_analog,
        bool internal_rsense,
        bool en_SpreadCycle,
        bool shaft,
        bool index_otpw,
        bool index_step,
        bool pdn_disable,
        bool mstep_reg_select,
        bool multistep_filt
    );

    void set_holdCurrent(
        uint8_t currentHold,
        uint8_t currentRun,
        uint8_t holdDelay
    );

    void set_thresholdPowerdown(uint8_t threshold);
    void set_thresholdStealthChop(uint32_t threshold);
    void set_thresholdCoolStep(uint32_t threshold);
    void set_thresholdStall(uint8_t threshold);

    void set_chopConfig(
        bool lowsideShortProtection,
        bool GNDShortProtection,
        bool doubleEdge,
        bool interpolation,
        uint8_t microstepResolution,
        bool voltageSensatifity,
        uint8_t hysteresisOffset,
        uint8_t hysteresisStart
    );

};
