/**
 * @file ArrayList.h
 * @author Anatole SCHRAMM-HENRY
 * @brief ArrayList type object implementing a List interface
 * @version 0.1
 * @date 2025-10-29
 * 
 * @copyright MIT
 * 
 */
#ifndef ARRAYLIST_H
#define ARRAYLIST_H
#include "ListInterface.h"

//The startup size is the list size on creation
#define STARTUP_SIZE 5
//The reallocation block size corresponds to the size the list is expanded by
#define REALLOC_BLKSIZE 5

template <typename T>
class ArrayList : public ListInterface<T>
{
    /* To be implemented for non pointer objects */
};

template <typename T>
class ArrayList<T*> : public ListInterface<T*>
{
    public :
        ArrayList(size_t reserve = STARTUP_SIZE) : ListInterface<T*>(), _physicalSize(reserve)
        {
            _array = (T**) malloc(_physicalSize * sizeof(T*));
        }

        virtual ~ArrayList()
        {
            clear();
            free(_array);
        }

        virtual boolean addFirst(T* element)
        {
            if(!_array) return false;
            //If there is no more space available we expand the list
            if(_logicalSize == _physicalSize)
            {
                if(!allocateMore())
                {
                    delete element;
                    return false;
                }
            }

            shiftArray(1);

            _array[0] = element;
            _logicalSize++;

            return true;
        }

        virtual boolean add(T* element)
        {
            if(!_array) return false;
            //If there is no more space available we expand the list
            if(_logicalSize == _physicalSize)
            {
                if(!allocateMore())
                {
                    delete element;
                    return false;
                }
            }

            _array[_logicalSize] = element;
            _logicalSize++;

            return true;
        }

        virtual T* remove(size_t index)
        {
            if(!_array || index >= _logicalSize) return NULL;

            T* p = _array[index];

            shiftArray(-1, index);
            _logicalSize--;

            if(_physicalSize - _logicalSize == REALLOC_BLKSIZE)
            {
                allocateLess();
            }

            return p;
        }

        virtual T* removeFirst()
        {
            if(!_array || !_logicalSize) return NULL;

            T* p = _array[0];

            shiftArray(-1);
            _logicalSize--;

            if(_physicalSize - _logicalSize == REALLOC_BLKSIZE)
            {
                allocateLess();
            }

            return p;
        }

        virtual T* removeLast()
        {
            if(!_array || !_logicalSize) return NULL;

            T* p = _array[_logicalSize - 1];
            _logicalSize--;

            if(_physicalSize - _logicalSize == REALLOC_BLKSIZE)
            {
                allocateLess();
            }

            return p;
        }

        virtual T* get(size_t index)
        {
            if(index >= _logicalSize || !_array)
                return NULL;
            return _array[index];
        }

        virtual T* operator[](size_t index)
        {
            return get(index);
        }

        virtual T* getFirst()
        {
            if(!_array || !_logicalSize) return NULL;

            return _array[0];
        }

        virtual T* getLast()
        {
            if(!_array || !_logicalSize) return NULL;

            return _array[_logicalSize - 1];
        }

        //Contains returns -1 if the element wasn't found or the element's index otherwise
        virtual int64_t contains(T* element)
        {
            for(size_t i(0); i < _logicalSize; i++)
            {
                if(_array[i] == element || *_array[i] == *element)
                    return i;
            }

            return -1;
        }

        virtual size_t count()
        {
            return _logicalSize;
        }

        virtual size_t clear()
        {
            return dispose();
        }

        virtual size_t dispose()
        {
            size_t size = _logicalSize;

            if(!_array) return 0;

            for(size_t i(0); i < _logicalSize; i++)
                delete _array[i];

             _logicalSize = 0;
             _physicalSize = STARTUP_SIZE;

            void *p = realloc(_array, _physicalSize * sizeof(T*));

            if(p)
                _array = (T**)p;

            return size;
        }

    protected :
    private :
        //Shifts the array place places. If place is positive, it shifts down and
        //If place is negative, it shifts up
        void shiftArray(int64_t place, size_t from = 0)
        {
            if(place > 0)
            {
                for(int64_t i(_logicalSize + place - 1); i > from; i--)
                    _array[i] = _array[i-1];
            }
            else if(place < 0)
            {
                for(int64_t i(from); i < _logicalSize - place - 2; i++)
                    _array[i] = _array[i+1];
            }
        }

        boolean allocateMore()
        {
            void *p = realloc(_array, (_physicalSize + REALLOC_BLKSIZE) * sizeof(T*));

            if(p)
            {
                _physicalSize += REALLOC_BLKSIZE;
                _array = (T**)p;
            }

            else
                return false;
            return true;
        }

        boolean allocateLess()
        {
            void *p = realloc(_array, (_physicalSize - REALLOC_BLKSIZE) * sizeof(T*));

            if(p)
            {
                _physicalSize -= REALLOC_BLKSIZE;
                _array = (T**)p;
            }

            else
                return false;
            return true;
        }

        T**_array = NULL;
        size_t _logicalSize = 0, _physicalSize;
};

#endif //ARRAYLIST_H
