/** 
 * @addtogroup table_3d 
 *  @{
 */

/** \file
 * @brief 3D table axis types and iterators
 */

#pragma once

#include "table3d_typedefs.h"

/**\enum axis_domain
 * @brief Encodes the real world measurement that a table axis captures
 * */
enum axis_domain {
    /** RPM (engine speed) */
    axis_domain_Rpm,
    /** Load */
    axis_domain_Load,
    /** Throttle position */ 
    axis_domain_Tps 
};

/** @brief Iterate over table axis elements */
class table_axis_iterator
{
public:

    /** @brief Construct */
    table_axis_iterator(table3d_axis_t *pStart, const table3d_axis_t *pEnd, axis_domain domain)
     : _stride(pEnd>pStart ? stride_inc : stride_dec)
     , _pStart(pStart)
     , _pEnd(pEnd + _stride)
     , _domain(domain) //cppcheck-suppress misra-c2012-10.4
    {
    }

    axis_domain get_domain(void) const { return _domain; }

    /** @brief Advance the iterator
     * @param steps The number of elements to move the iterator
    */
    table_axis_iterator& advance(int8_t steps)
    {
        _pStart = _pStart + ((int16_t)_stride * steps);
        return *this;
    }

    /** @brief Increment the iterator by one element*/
    table_axis_iterator& operator++(void)
    {
        return advance(1);
    }

    /** @brief Test for end of iteration */
    bool at_end(void) const
    {
        return _pStart == _pEnd;
    }

    /** @brief Dereference the iterator */
    table3d_axis_t& operator*(void)
    {
        return *_pStart;
    }
    /** @copydoc table_axis_iterator::operator*()  */
    const table3d_axis_t& operator*(void) const
    {
        return *_pStart;
    }    
    
private:

    static constexpr int8_t stride_inc = 1;
    static constexpr int8_t stride_dec = -1;
    int8_t _stride;
    table3d_axis_t *_pStart;
    const table3d_axis_t *_pEnd;
    const axis_domain _domain;
};

#define TABLE3D_TYPENAME_AXIS(size, domain) table3d ## size ## domain ## _axis

#define TABLE3D_GEN_AXIS(size, dom) \
    /** @brief The dxis for a 3D table with size x size dimensions and domain 'domain' */ \
    struct TABLE3D_TYPENAME_AXIS(size, dom) { \
        /** @brief The length of the axis in elements */ \
        static constexpr table3d_dim_t length = (size); \
        /** @brief The domain the axis represents */ \
        static constexpr axis_domain domain = axis_domain_ ## dom; \
        /**
          @brief The axis elements\
        */ \
        table3d_axis_t axis[(size)]; \
        \
        /** @brief Iterate over the axis elements */ \
        table_axis_iterator begin(void) \
        {  \
            return table_axis_iterator(axis+(size)-1, axis, domain); \
        } \
        /** @brief Iterate over the axis elements, from largest to smallest */ \
        table_axis_iterator rbegin(void) \
        {  \
            return table_axis_iterator(axis, axis+(size)-1, domain); \
        } \
    };

// This generates the axis types for the following sizes & domains:
TABLE3D_GEN_AXIS(6, Rpm)
TABLE3D_GEN_AXIS(6, Load)
TABLE3D_GEN_AXIS(4, Rpm)
TABLE3D_GEN_AXIS(4, Load)
TABLE3D_GEN_AXIS(8, Rpm)
TABLE3D_GEN_AXIS(8, Load)
TABLE3D_GEN_AXIS(8, Tps)
TABLE3D_GEN_AXIS(16, Rpm)
TABLE3D_GEN_AXIS(16, Load)

/** @} */