//-----------------------------------------------------------------------
//  Generic Array class
//      Macro definition "DEBUG_ARRAY_CHECK" enables to check
//      range of index.
//
//  2022/09/29, Copyright (c) 2022 MIKAMI, Naoki
//-----------------------------------------------------------------------

#include "mbed.h"

#ifndef MIKAMI_ARRAY_HPP
#define MIKAMI_ARRAY_HPP

namespace Mikami
{
    template <class T> class Array
    {
    public:
        explicit Array(int n = 1) { ArrayNew(n); }  // default constructor
        Array(const Array<T>& a) { Copy(a); }       // copy constructor
        Array(int n, T initialVal);                 // constructor with initialization
        Array(int n, const T val[]);                // constructor with assignment built-in array
        ~Array() { delete[] v_; }                   // destructor
        void Fill(T val);                           // fill with same value
        void Assign(const T val[]);                 // assign built-in array
        void SetSize(int n);                        // setting size
        int Length() const { return size_; }        // get size of array
        Array<T>& operator=(const Array<T>& a);     // assignment
        T& operator[](int n);                       // non-const [] operator
        const T& operator[](int n) const;           // const [] operator
        operator T* () const { return v_; }         // non-const type conversion
        operator const T* () const { return v_; }   // const type conversion

    private:
        T    *v_;
        int  size_;                         // size of array

        void Range(int pos) const;          // range checking for Array
        void Copy(const Array<T>& v_src);   // copy of object
        void ArrayNew(const int n);         // routine for constructor
    };

//-----------------------------------------------------------------------
// implementation of generic array class
//-----------------------------------------------------------------------

    // constructor with initialization
    template <class T> inline Array<T>::Array(int n, T initialVal)
    {
        ArrayNew(n);
        Fill(initialVal);
    }

    // constructor with assignment built-in array
    template <class T> inline Array<T>::Array(int n, const T val[])
    {
        ArrayNew(n);
        Assign(val);
    }

    template <class T> inline void Array<T>::SetSize(int n)
    {
        delete[] v_;
        v_ = new T[size_ = n];
    }

    // fill with same value
    template <class T> inline void Array<T>::Fill(T val)
    {
        for (int n=0; n<size_; n++) v_[n] = val;
    }

    // assign built-in array
    template <class T> inline void Array<T>::Assign(const T val[])
    {
        for (int n=0; n<size_; n++) v_[n] = val[n];
    }

    template <class T> inline Array<T>& Array<T>::operator=(const Array<T>& a)
    {
        if (this != &a) // prohibition of self-assignment
        {
            delete [] v_;
            Copy(a);
        }
        return *this;
    }

    template <class T> inline T& Array<T>::operator[](int n)
    {
    #ifdef DEBUG_ARRAY_CHECK
        Range(n);       // out of bound ?
    #endif
        return v_[n];
    }

    template <class T> inline const T& Array<T>::operator[](int n) const
    {
    #ifdef DEBUG_ARRAY_CHECK
        Range(n);       // out of bounds ?
    #endif
        return v_[n];
    }

    template <class T> void Array<T>::Range(int pos) const
    {
        if ((pos < 0) || (pos >= size_))
            mbed_assert_internal("Out of range", __FILE__, __LINE__);   // mbed_assert.h
    }

    template <class T> inline void Array<T>::Copy(const Array<T>& v_src)
    {
        v_ = new T[size_ = v_src.size_];
        for (int n=0; n<size_; n++) v_[n] = v_src.v_[n];
    }

    // routine for constructor
    template <class T> inline void Array<T>::ArrayNew(int n)
    {
        v_ = new T[size_ = n];
    }
}
#endif  // MIKAMI_ARRAY_HPP
