#ifndef CORE_ARRAYLIST_HPP
#define CORE_ARRAYLIST_HPP

#include "utils/AlignedData.hpp"
#include "utils/ArrayString.hpp"

namespace Core {
    template<typename T, int N>
    class ArrayList final {
        static_assert(N > 0, "ArrayList size must be positive");

        AlignedType<T> data[static_cast<unsigned int>(N)];
        int length;

    public:
        ArrayList() : length(0) {
        }

        ArrayList(const ArrayList& other) : ArrayList() {
            copy(other);
        }

        ArrayList(ArrayList&& other) : ArrayList() {
            move(Core::move(other));
            other.clear();
        }

        ~ArrayList() {
            clear();
        }

        ArrayList& operator=(const ArrayList& other) {
            if(&other != this) {
                clear();
                copy(other);
            }
            return *this;
        }

        ArrayList& operator=(ArrayList&& other) {
            if(&other != this) {
                clear();
                move(Core::move(other));
                other.clear();
            }
            return *this;
        }

        T* begin() {
            return reinterpret_cast<T*>(data);
        }

        T* end() {
            return begin() + length;
        }

        const T* begin() const {
            return reinterpret_cast<const T*>(data);
        }

        const T* end() const {
            return begin() + length;
        }

        template<typename... Args>
        check_return Error put(T*& t, Args&&... args) {
            if(length >= N) {
                return Error::CAPACITY_REACHED;
            }
            t = new(begin() + length++) T(Core::forward<Args>(args)...);
            return Error::NONE;
        }

        template<typename... Args>
        check_return Error add(Args&&... args) {
            T* t = nullptr;
            return put(t, Core::forward<Args>(args)...);
        }

        T& operator[](int index) {
            return begin()[index];
        }

        const T& operator[](int index) const {
            return begin()[index];
        }

        int getLength() const {
            return length;
        }

        void clear() {
            for(int i = 0; i < length; i++) {
                begin()[i].~T();
            }
            length = 0;
        }

        check_return Error removeBySwap(int index) {
            if(index < 0 || index >= length) {
                return Error::INVALID_INDEX;
            }
            length--;
            if(index != length) {
                begin()[index] = Core::move(begin()[length]);
            }
            begin()[length].~T();
            return Error::NONE;
        }

        check_return Error removeLast() {
            return removeBySwap(length - 1);
        }

        template<typename String>
        check_return Error toString(String& s) const {
            return Core::toString(s, *this);
        }

    private:
        void copy(const ArrayList& other) {
            for(int i = 0; i < other.length; i++) {
                (void)add(other[i]);
            }
        }

        void move(ArrayList&& other) {
            for(int i = 0; i < other.length; i++) {
                (void)add(Core::move(other[i]));
            }
        }
    };
}

#endif