/**************************************************
Includes
**************************************************/
#pragma once
#include <stdlib.h>
#include "Arduino.h"
#include "config.h"

/**************************************************
Definitions
**************************************************/
using namespace std;

/**************************************************
Class definition
**************************************************/
/* In our application - Elementtype is uint16_t */

template <typename Elementtype>
class DataContainer
{
private:
    Elementtype *data_array; // ** is a pointer pointing to another pointer, common for 2D array allocation
    int max_blocks;
    std::size_t current_blocks; // How much data is saved in data_array

    int index_data(int i, int n) const;

public:
    DataContainer(int max_capacity);
    ~DataContainer();
    Elementtype get_data_item(int i, int n) const;
    void init_data_saving();
    void add_data(Elementtype &new_Element, const int typenmbr);
    void end_data_saving();
    const std::size_t get_currentsize() const;
    Elementtype *get_all_data() const;
    void reset_all_data();
};

/**************************************************
Protected function definitions
**************************************************/
/* Index an item within the array */
template <typename Elementtype>
int DataContainer<Elementtype>::index_data(int i, int n) const
{
    return i + SPIRU_SENSOR * n;
}

/**************************************************
Publicfunction definitions
**************************************************/
/* Constructor for new Datacontainer with total capacity of maxsize */
template <typename Elementtype>
DataContainer<Elementtype>::DataContainer(int max_capacity) : current_blocks(0)
{
    /* Calculate number of "rows", but since it is a 1D array simulating a 2D array -
    it is rather the number of "blocks" within the array */
    max_blocks = max_capacity / SPIRU_SENSOR;
    /* Allocate a huge 1D array, containing the same space as a 2D array but contiguously */
    data_array = new Elementtype [max_capacity]; //
}

/* Deletes the data_array */
template <typename Elementtype>
DataContainer<Elementtype>::~DataContainer()
{
    delete[] data_array;
}

/* Return an item from the simulated 2D array */
template <typename Elementtype>
Elementtype DataContainer<Elementtype>::get_data_item(int i, int n) const
{
    return data_array[index_data(i, n)];
}

/*call this before you want to add a new datastack*/
/*checks for saving space. If full: shifts all data to the left*/
template <typename Elementtype>
void DataContainer<Elementtype>::init_data_saving()
{
    if (max_blocks == current_blocks)
    {
        current_blocks--;
        for (int i = 0; i < current_blocks; i++)
        {
            for (int n = 0; n < SPIRU_SENSOR; n++)
            {
                data_array[index_data(i, n)] = data_array[index_data(i + 1, n)];
            }
        }
    }
}

/* Adds a new <Element> to the array */
/*SMART sensor enum: 0: Photodiode, 1: Temperature */
/*SUPER sensor enum: 0: Photodiode, 1: Temperature, 2: pH , 3: EC , 4: DO  */
template <typename Elementtype>
void DataContainer<Elementtype>::add_data(Elementtype &new_Element, const int Sensortype)
{
    data_array[index_data(new_Element, Sensortype)] = new_Element;
}

/* Call this when datastack is complete to update currentsize */
template <typename Elementtype>
void DataContainer<Elementtype>::end_data_saving()
{
    current_blocks++;
}

/* Returns all data */
template <typename Elementtype>
Elementtype *DataContainer<Elementtype>::get_all_data() const
{
    return data_array;
}

/* Returns size of contentlist */
template <typename Elementtype>
const std::size_t DataContainer<Elementtype>::get_currentsize() const
{
    return current_blocks;
}

/* Deletes all Data of Datacontainer and allocates new memory */
template <typename Elementtype>
void DataContainer<Elementtype>::reset_all_data()
{
    /* Release the memory */
    delete[] data_array;
    /* Allocate a huge 1D array, containing the same space as a 2D array but contiguously */
    data_array = new Elementtype [max_blocks * SPIRU_SENSOR];
    /* Reset current blocks to 0 */
    current_blocks = 0;
}
