#pragma once

#include <iostream>
#include <vector>
#include <array>
#include <cmath>
#include <numbers>

#include <unistd.h>

#include <wiringPiSPI.h>

using namespace std;

/**
 * @brief   Accelerometer AXDL345
 *          Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
 */
class ADXL345{
    /**
     * @brief  Channel on which is accelerometer connected
     */
    bool channel;

    /**
     * @brief Some of ADXL345 regsisters
     */
    enum class registers: uint8_t{
        DEVID       = 0x00,
        OFSX        = 0x1e,
        OFSY        = 0x1f,
        OFSZ        = 0x20,
        BW_RATE     = 0x2c,
        POWER_CTL   = 0x2d,
        DATA_FORMAT = 0x31,
        DATAX0      = 0x32,
        DATAX1      = 0x33,
        DATAY0      = 0x34,
        DATAY1      = 0x35,
        DATAZ0      = 0x36,
        DATAZ1      = 0x37
    };

    /**
     * @brief   Dummy packet, is not interpreted as any command by ADXL
                Is used as padding for SPI transfer
     */
    uint8_t dummy_packet = 0xaa;

public:

    /**
     * @brief Construct a new ADXL345 object
     *
     * @param channel SPI channel of Rpi, has only two channels, 0 or 1
     */
    ADXL345(bool channel = 0);

    /**
     * @brief   Reads ID from accelerometer
     *          Value is read only
     *          Suitable for testing of connection
     *
     * @return uint8_t  Always should returns 0xe5 if communications is working
     */
    uint8_t ID();

    /**
     * @brief   Calculates roll angle of sensor based on value from X and Z values of acceleration
     *
     * @return double Roll angle of sensor (around X axis of IC), From 0 to 360°, clockwise rotation
     */
    double Inclination();

    /**
     * @brief   Trims offsets of accelerometer based on current values !!
     *          During this calibration process accelerometer should be position in base position (0 degrees)

     * @return array<int8_t, 3> Values which was used as trimming values for coresponding axis [X,Y,Z]
     */
    array<int, 3> Trim_offsets();

    /**
     * @brief   Trims offsets of accelerometer with defined values
     *
     * @param axis_offsets  Trim values for all accelerometer axis [X,Y,Z]
     *                      Value is multiplied by 4 and added to resulting acceleration of respective axis
     */
    void Trim_offsets(array<int8_t, 3> axis_offsets);


private:
    /**
     * @brief   Reads raw values of acceleration for all axis
     *
     * @return array<int16_t, 3>    Acceleration values from sensor for each axis [X,Y,Z]
     */
    array<int16_t, 3> Raw_values();

    /**
     * @brief   Read one byte of data from ADXL memory
     *
     * @param address   Address in ADXL memory from which are data read
     * @return uint8_t  Data from memory
     */
    uint8_t Read(registers address);

    /**
     * @brief       Reads sequence of data from ADXL memory
     *
     * @param address   Address in ADXL memory from which reading starts
     * @param length    Amount of bytes to read
     * @return vector<uint8_t>  Readout data
     */
    vector<uint8_t> Read_sequence(registers address, short length);

    /**
     * @brief   Write one byte of data into ADXL memory
     *
     * @param address   Address to which will be writing performed
     * @param data      Data to write
     */
    void Write(registers address, uint8_t data);
};
