/*
 * 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 testing copy and move
//
class TestCM {
    public:
        inline TestCM()
        : _ptr(0)
        {}

        inline TestCM(void* ptr)
        : _ptr(ptr)
        {}

        inline TestCM(const TestCM& inst)
        : _ptr(inst._ptr)
        {}

        inline TestCM(TestCM&& inst)
        : _ptr(inst._ptr)
        { inst._ptr = 0; }

        inline ~TestCM()
        { _ptr = 0; }

        inline TestCM& operator=(const TestCM& inst)
        {
            _ptr = inst._ptr;
            return *this;
        }

        inline TestCM& operator=(TestCM&& inst)
        {
            _ptr      = inst._ptr;
            inst._ptr = 0;
            return *this;
        }

        inline operator eCxx::size_t () const
        { return (eCxx::size_t) _ptr; }

    private:
        void* _ptr;
};


//
// A template test class for testing argument type
//
template <typename Value0T, typename Value1T, typename Value2T>
class TestTA;


//
// A template test class for testing integer sequence from template parameter pack
//
template <eCxx::uint8_t... Numbers>
struct TestISPP {
    using ISEQ = eCxx::integerSequence<eCxx::uint8_t, Numbers...>;

    static constexpr eCxx::uint8_t Val0 = eCxx::get<0>( ISEQ{} );
    static constexpr eCxx::uint8_t Val1 = eCxx::get<1>( ISEQ{} );
    static constexpr eCxx::uint8_t Val2 = eCxx::get<2>( ISEQ{} );
};


//
// Enable this to check the size of time_t at compile time
//
#if 0
    template<unsigned int N> struct CompileTimeSize;
    static constexpr CompileTimeSize< sizeof(time_t) > check;
#endif


//
// Test application
//
int main()
{
#ifdef ECXX_HAVE_PROGRAM_STRING_LITERALS

    eCxx::PGMCharPointer    strTest1 = "Program string test (near)"_pgm;
    eCxx::PGMCharPointerFar strTest2 = "Program string test (far )"_pgmf;

    _CODESTR_NL1_CODERES_NL2( "%s", (const char*) strTest1.p );
    _CODESTR_NL1_CODERES_NL2( "%s", (const char*) strTest2.p );

    _DIVIDERLINE_DASHSTR_NL2();

#endif



    eCxx::asmNOP< 1>(); eCxx::asmNOP< 2>(); eCxx::asmNOP< 3>(); eCxx::asmNOP< 4>(); eCxx::asmNOP< 5>();
    eCxx::asmNOP< 6>(); eCxx::asmNOP< 7>(); eCxx::asmNOP< 8>(); eCxx::asmNOP< 9>();
    eCxx::asmNOP<10>(); eCxx::asmNOP<20>(); eCxx::asmNOP<30>(); eCxx::asmNOP<40>(); eCxx::asmNOP<50>();
    eCxx::asmNOP<60>(); eCxx::asmNOP<70>(); eCxx::asmNOP<80>(); eCxx::asmNOP<90>();



    const eCxx::int8_t a = 10;
    const eCxx::int8_t b = 20;

    _CODESTR_NL1_CODERES_NL2( "%d", (int) eCxx::min(a, b) );
    _CODESTR_NL1_CODERES_NL2( "%d", (int) eCxx::max(a, b) );
    _DIVIDERLINE_DASHSTR_NL2();

    _EXECUTECODE_CODESTR_NL2( const auto cmp =  [](eCxx::int8_t a, eCxx::int8_t b) { return a < b; }; );
    _CODESTR_NL1_CODERES_NL2( "%d", (int) eCxx::min(a, b, cmp)                                        );
    _CODESTR_NL1_CODERES_NL2( "%d", (int) eCxx::max(a, b, cmp)                                        );
    _DIVIDERLINE_DASHSTR_NL2();

    _EXECUTECODE_CODESTR_NL1( TestCM test1((void*) 1234567890);                                               );
    _EXECUTECODE_CODESTR_NL1( TestCM test2(test1);                                                            );
    _EXECUTECODE_CODESTR_NL2( TestCM test3(eCxx::move(test2));                                                );
    _CODESTR_NL1_CODERES_NL2( "%zd %zd %zd", (eCxx::size_t) test1, (eCxx::size_t) test2, (eCxx::size_t) test3 );
    _DIVIDERLINE_DASHSTR_NL2();



    eCxx::int8_t ary[5] = { 1, 2, 3, 4, 5 };

    typedef eCxx::ReverseIterator<      eCxx::int8_t*> RIterator;
    typedef eCxx::ReverseIterator<const eCxx::int8_t*> ConstRIterator;

    RIterator      ib  = eCxx::makeReverseIterator( ary + eCxx::arraySize(ary) );
    RIterator      ie  = eCxx::makeReverseIterator( ary                        );
    ConstRIterator cib = ib;
    ConstRIterator cie = ie;

    printf( "%d %d %d\n\n", cib[0], cib[2], cib[4] );
    for(; cib != cie; ++cib) printf("%d\n", (int) *cib);
    printf("\n");

    _DIVIDERLINE_DASHSTR_NL2();



    constexpr eCxx::tuple<eCxx::int8_t, eCxx::uint8_t> tuple1{ -10, 10 };
    printf( "%d %d\n", (int) eCxx::get<0>(tuple1), (int) eCxx::get<1>(tuple1) );

    constexpr eCxx::tuple<char, bool> tuple2 = eCxx::makeTuple('a', false);
    printf( "%c %d\n", (char) eCxx::get<0>(tuple2), (int) eCxx::get<1>(tuple2) );

    printf("\n");

    _DIVIDERLINE_DASHSTR_NL2();



    typedef TestTA<char, bool, eCxx::int8_t> DeclaredTestTA;

    printf( "%d\n", (int) eCxx::templateArgNthIs< 0, char,         DeclaredTestTA>::Value );
    printf( "%d\n", (int) eCxx::templateArgNthIs< 1, char,         DeclaredTestTA>::Value );
    printf( "%d\n", (int) eCxx::templateArgNthIs< 2, char,         DeclaredTestTA>::Value );
    printf("\n");

    printf( "%d\n", (int) eCxx::templateArgNthIs< 0, bool,         DeclaredTestTA>::Value );
    printf( "%d\n", (int) eCxx::templateArgNthIs< 1, bool,         DeclaredTestTA>::Value );
    printf( "%d\n", (int) eCxx::templateArgNthIs< 2, bool,         DeclaredTestTA>::Value );
    printf("\n");

    printf( "%d\n", (int) eCxx::templateArgNthIs< 0, eCxx::int8_t, DeclaredTestTA>::Value );
    printf( "%d\n", (int) eCxx::templateArgNthIs< 1, eCxx::int8_t, DeclaredTestTA>::Value );
    printf( "%d\n", (int) eCxx::templateArgNthIs< 2, eCxx::int8_t, DeclaredTestTA>::Value );
    printf("\n");

    _DIVIDERLINE_DASHSTR_NL2();



    auto iseq1a = eCxx::integerSequence    <size_t, 5, 3, 2> {};
    auto iseq1b = eCxx::  indexSequence    <        5, 3, 2> {};
    auto iseq2a = eCxx::makeIntegerSequence<size_t, 3      > {};
    auto iseq2b = eCxx::  makeIndexSequence<        3      > {};
    auto iseq3  = eCxx::indexSequenceFor   <char, int, char> {};

    printf( "%zd %zd %zd\n", eCxx::get<0>(iseq1a), eCxx::get<1>(iseq1a), eCxx::get<2>(iseq1a) );
    printf( "%zd %zd %zd\n", eCxx::get<0>(iseq1b), eCxx::get<1>(iseq1b), eCxx::get<2>(iseq1b) );
    printf( "%zd %zd %zd\n", eCxx::get<0>(iseq2a), eCxx::get<1>(iseq2a), eCxx::get<2>(iseq2a) );
    printf( "%zd %zd %zd\n", eCxx::get<0>(iseq2b), eCxx::get<1>(iseq2b), eCxx::get<2>(iseq2b) );
    printf( "%zd %zd %zd\n", eCxx::get<0>(iseq3 ), eCxx::get<1>(iseq3 ), eCxx::get<2>(iseq3 ) );
    printf("\n");

    using Test = TestISPP<5, 3, 8>;
    printf( "%zd %zd %zd\n", (size_t) Test::Val0, (size_t) Test::Val1 , (size_t) Test::Val2  );
    printf("\n");



    return 0;
}
