/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#include "TestUtility.h"


//
// A test class for Array<> with a non POD type
//
struct ValStore {
    typedef eCxx::uint8_t ValueType;

    const char* dummy;
    ValueType   value;

    inline ValStore()
    : value(-1)
    {
        _____________CODERES_NL1("###  ValStore(  ) this = %p cur = %3d", this, value);
    }

    inline ValStore(ValueType value_)
    : value(value_)
    {
        _____________CODERES_NL1("###  ValStore(v ) this = %p cur = %3d", this, value);
    }

    inline ValStore(const ValStore& valInc)
    : value(valInc.value)
    {
        _____________CODERES_NL1("###  ValStore(& ) this = %p cur = %3d ref = %3d", this, value, valInc.value);
    }

    inline ValStore(ValStore&& valInc)
    : value(valInc.value)
    {
        valInc.value = -1;
        _____________CODERES_NL1("###  ValStore(&&) this = %p cur = %3d ref = %3d", this, value, valInc.value);
    }

    inline ~ValStore()
    {
        value = -1;
        _____________CODERES_NL1("### ~ValStore(  ) this = %p cur = %3d", this, value);
    }

    inline ValStore& operator=(const ValStore& valInc)
    {
        value = valInc.value;
        _____________CODERES_NL1("### operator=(& ) this = %p cur = %3d ref = %3d", this, value, valInc.value);
        return *this;
    }

    inline ValStore& operator=(ValStore&& valInc)
    {
        value        = valInc.value;
        valInc.value = -1;
        _____________CODERES_NL1("### operator=(&&) this = %p cur = %3d ref = %3d", this, value, valInc.value);
        return *this;
    }

    inline bool operator!=(const ValStore& r) const
    { return value != r.value; }

    inline bool operator==(const ValStore& r) const
    { return value == r.value; }
};


//
// Test application
//
int main()
{
    _____________USERSTR_NL2("Array of integers : initialization with a braced-init-list");

    eCxx::Array<eCxx::uint32_t, 5> aUI32{ 3, 2, 1 };

    for(eCxx::size_t i = 0; i < aUI32.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aUI32[i] );
    }

    _DIVIDERLINE_DASHSTR_NL2();



    _____________USERSTR_NL2("Array of structs : pushBack() and popBack()");

    eCxx::Array<ValStore, 5> aValStore;
    aValStore.pushBack( ValStore( 0                        ) );
    aValStore.pushBack( ValStore( aValStore.back().value + 1 ) );
    aValStore.pushBack( ValStore( aValStore.back().value + 1 ) );
    _____________________NL1();

    for(eCxx::size_t i = 0; i < aValStore.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", aValStore[i].value );
    }

    _EXECUTECODE_CODESTR_NL1( aValStore.popBack(); );
    _EXECUTECODE_CODESTR_NL1( aValStore.popBack(); );
    _EXECUTECODE_CODESTR_NL1( aValStore.popBack(); );
    _EXECUTECODE_CODESTR_NL1( aValStore.popBack(); );
    _EXECUTECODE_CODESTR_NL2( aValStore.popBack(); );
    _DIVIDERLINE_DASHSTR_NL2();



    _____________USERSTR_NL2("Array of structs : initialization with a braced-init-list");

    eCxx::Array<ValStore, 5> aValStoreBIL{ 'A', 'B', 'C' };
    _____________________NL1();

    for(eCxx::size_t i = 0; i < aValStoreBIL.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStoreBIL[i].value );
    }

    _DIVIDERLINE_DASHSTR_NL2();



    // It has to be a static/global and simple symbol for PROGMEM
    _____________USERSTR_NL2("Array of structs : initialization and comparison with PROGMEM");

    static const char PROGMEM pmXYZ_[] = { 'X', 'Y', 'Z' };

    eCxx::PGMCharPointer    pmXYZ    = eCxx::PGM_PTR    (pmXYZ_);
    eCxx::PGMCharPointerFar pmXYZfar = eCxx::PGM_PTR_FAR(pmXYZ_);

    eCxx::Array<ValStore, 5> aValStoreRAW(pmXYZ, 3);
    _____________________NL1();

    for(eCxx::size_t i = 0; i < aValStoreRAW.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStoreRAW[i].value );
    }

    _CODESTR_NL1_CODERES_NL2( "%d", (int) (aValStore == aValStore   ) );
    _CODESTR_NL1_CODERES_NL2( "%d", (int) (aValStore == aValStoreBIL) );
    _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStoreBIL.equalsTo(pmXYZfar, 3) );
    _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStoreRAW.equalsTo(pmXYZfar, 3) );
    _DIVIDERLINE_DASHSTR_NL2();



    _____________USERSTR_NL2("Array of structs : copy and move assignment operators and constructors");

    _EXECUTECODE_CODESTR_NL2( aValStore = aValStoreBIL; );
    _EXECUTECODE_CODESTR_NL2( aValStore = eCxx::move(aValStoreBIL); );

    _EXECUTECODE_CODESTR_NL2( eCxx::Array<ValStore, 5> aValStoreTs1(aValStore); );
    _EXECUTECODE_CODESTR_NL2( eCxx::Array<ValStore, 5> aValStoreTs2(eCxx::move(aValStore)); );

    for(eCxx::size_t i = 0; i < aValStoreTs1.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStoreTs1[i].value );
    }

    _EXECUTECODE_CODESTR_NL2( aValStoreTs1.resize(2); );

    for(eCxx::size_t i = 0; i < aValStoreTs1.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStoreTs1[i].value );
    }

    _EXECUTECODE_CODESTR_NL2( aValStoreTs1.resize(100); );

    for(eCxx::size_t i = 0; i < aValStoreTs1.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStoreTs1[i].value );
    }

    _DIVIDERLINE_DASHSTR_NL2();



    _____________USERSTR_NL2("Array of structs : copy and move assignment operators and constructors with an array of different capacity");

    _EXECUTECODE_CODESTR_NL2( aValStore.clear(); );
    _EXECUTECODE_CODESTR_NL2( aValStore.pushBack(1); aValStore.pushBack(2); aValStore.pushBack(3); aValStore.pushBack(4); aValStore.pushBack(5); );
    _____________________NL1();

    for(eCxx::size_t i = 0; i < aValStore.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStore[i].value );
    }

    _EXECUTECODE_CODESTR_NL2( eCxx::Array<ValStore, 3> aValStore3a(aValStore); );

    for(eCxx::size_t i = 0; i < aValStore3a.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStore3a[i].value );
    }

    _EXECUTECODE_CODESTR_NL2( eCxx::Array<ValStore, 3> aValStore3b( eCxx::move(aValStore) ); );

    for(eCxx::size_t i = 0; i < aValStore3b.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStore3b[i].value );
    }

    for(eCxx::size_t i = 0; i < aValStore.size(); ++i) {
        _CODESTR_NL1_CODERES_NL2( "%d", (int) aValStore[i].value );
    }



     // Done
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL1();
    _DIVIDERLINE_DASHSTR_NL2();

    return 0;
}
