// Copyright (C) 2007  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.


#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <dlib/tuple.h>

#include "tester.h"

namespace  
{
    using namespace test;
    using namespace dlib;
    using namespace std;

    logger dlog("test.tuple");

    struct s_nil
    {
        template <typename T>
        void operator() (
            const T& 
        ) const
        {
        }
    };


    struct inc 
    {
        template <typename T>
        void operator() (
            T& a
        ) const
        {
            a += 1;
        }
    };


    template <typename T>
    void check_const (
        const T& t
    )
    {
        t.template get<0>();

        typedef typename T::template get_type<0>::type type0;
        t.template get<type0>();
        t.template index<type0>();
    }

    template <typename T>
    void check_nonconst (
        T& t
    )
    {
        t.template get<0>();

        typedef typename T::template get_type<0>::type type0;
        t.template get<type0>();
        t.template index<type0>();
    }

    void tuple_test (
    )
    /*!
        ensures
            - runs tests on tuple functions for compliance with the specs 
    !*/
    {        

        print_spinner();

        using dlib::tuple;

        tuple<> a;
        tuple<int> b;
        tuple<int, float> c;


        a.get<1>();
        a.get<2>();
        a.get<3>();
        a.get<4>();
        a.get<5>();

        check_nonconst(b);
        check_nonconst(c);
        check_const(b);
        check_const(c);

        COMPILE_TIME_ASSERT((is_same_type<tuple<>::get_type<0>::type, null_type>::value));
        COMPILE_TIME_ASSERT((is_same_type<tuple<int>::get_type<0>::type, int>::value));
        COMPILE_TIME_ASSERT((is_same_type<tuple<int,float>::get_type<0>::type, int>::value));
        COMPILE_TIME_ASSERT((is_same_type<tuple<int,float>::get_type<1>::type, float>::value));
        COMPILE_TIME_ASSERT((is_same_type<tuple<int,float>::get_type<2>::type, null_type>::value));

        b.get<0>() = 8;
        DLIB_TEST(b.get<int>() == 8);
        DLIB_TEST(b.index<int>() == 0);

        c.get<0>() = 9;
        DLIB_TEST(c.get<int>() == 9);
        DLIB_TEST(c.index<int>() == 0);
        c.get<1>() = 3.0;
        DLIB_TEST(c.get<float>() == 3.0);
        DLIB_TEST(c.index<float>() == 1);



        {
            typedef tuple<int, short, long> T;
            T a, b;
            a.get<0>() = 1;
            a.get<1>() = 3;
            a.get<2>() = 2;

            b = a;

            inc i;
            s_nil n;
            a.for_each(inc());
            a.for_each(i);
            const_cast<const T&>(a).for_each(s_nil());
            const_cast<const T&>(a).for_each(n);

            DLIB_TEST(a.get<0>() == b.get<0>()+2);
            DLIB_TEST(a.get<1>() == b.get<1>()+2);
            DLIB_TEST(a.get<2>() == b.get<2>()+2);

            ostringstream sout;

            serialize(a,sout);
            istringstream sin(sout.str());
            deserialize(b,sin);

            DLIB_TEST(a.get<0>() == b.get<0>());
            DLIB_TEST(a.get<1>() == b.get<1>());
            DLIB_TEST(a.get<2>() == b.get<2>());

            a.for_index(i,0);
            a.for_index(inc(),1);
            const_cast<const T&>(a).for_index(n,2);
            const_cast<const T&>(a).for_index(s_nil(),0);

            DLIB_TEST(a.get<0>() == b.get<0>()+1);
            DLIB_TEST(a.get<1>() == b.get<1>()+1);
            DLIB_TEST(a.get<2>() == b.get<2>()+0);

            swap(a,b);

            DLIB_TEST(b.get<0>() == a.get<0>()+1);
            DLIB_TEST(b.get<1>() == a.get<1>()+1);
            DLIB_TEST(b.get<2>() == a.get<2>()+0);
        }


    }




    class tuple_tester : public tester
    {
    public:
        tuple_tester (
        ) :
            tester ("test_tuple",
                    "Runs tests on the tuple object")
        {}

        void perform_test (
        )
        {
            tuple_test();
        }
    } a;

}