// Copyright (C) 2011  Davis E. King (davis@dlib.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#include <dlib/any.h>
#include <sstream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <vector>
#include "../rand.h"
#include "tester.h"
namespace  
{
    using namespace test;
    using namespace dlib;
    using namespace std;
    logger dlog("test.any_function");
// ----------------------------------------------------------------------------------------
    int add ( int a, int b) { return a + b; }
    string cat ( string a, string b) { return a + b; }
// ----------------------------------------------------------------------------------------
    void set_vals1( int& a) { a = 1; }
    void set_vals2( int& a, int& b) { a = 1; b = 2; }
    void set_vals3( int& a, int& b, int& c) { a = 1; b = 2; c = 3; }
    void set_vals4( int& a, int& b, int& c, int& d) { a = 1; b = 2; c = 3; d = 4; }
    void set_vals5( int& a, int& b, int& c, int& d, int& e) { a = 1; b = 2; c = 3; d = 4; e = 5; }
    void set_vals6( int& a, int& b, int& c, int& d, int& e, int& f) { a = 1; b = 2; c = 3; d = 4; e = 5; f = 6; }
    void set_vals7( int& a, int& b, int& c, int& d, int& e, int& f, int& g) { a = 1; b = 2; c = 3; d = 4; e = 5; f = 6; g = 7; }
    void set_vals8( int& a, int& b, int& c, int& d, int& e, int& f, int& g, int& h) 
    { a = 1; b = 2; c = 3; d = 4; e = 5; f = 6; g = 7; h = 8; }
    void set_vals9( int& a, int& b, int& c, int& d, int& e, int& f, int& g, int& h, int& i) 
    { a = 1; b = 2; c = 3; d = 4; e = 5; f = 6; g = 7; h = 8; i = 9;}
    void set_vals10( int& a, int& b, int& c, int& d, int& e, int& f, int& g, int& h, int& i, int& j) 
    { a = 1; b = 2; c = 3; d = 4; e = 5; f = 6; g = 7; h = 8; i = 9; j = 10;}
    void zero_vals( int& a, int& b, int& c, int& d, int& e, int& f, int& g, int& h, int& i, int& j) 
    { a = 0; b = 0; c = 0; d = 0; e = 0; f = 0; g = 0; h = 0; i = 0; j = 0;}
// ----------------------------------------------------------------------------------------
    struct test
    {
        int operator()() const { return 4; }
    };
    struct test2
    {
        int v;
        test2() : v(0) {}
        test2(int val) : v(val) {}
        int operator()() const { return v; }
    };
// ----------------------------------------------------------------------------------------
    void test_contains_4(
        const any_function<int()> a
    )
    {
        DLIB_TEST(a.is_empty() == false);
        DLIB_TEST(a.is_set() == true);
        DLIB_TEST(a.contains<test>() == true);
        DLIB_TEST(a.contains<int(*)()>() == false);
        DLIB_TEST(any_cast<test>(a)() == 4);
        DLIB_TEST(a() == 4);
    }
// ----------------------------------------------------------------------------------------
    void run_test()
    {
        any_function<int()> a, b, c;
        DLIB_TEST(a.is_empty());
        DLIB_TEST(a.is_set()==false);
        DLIB_TEST(a.contains<int(*)()>() == false);
        DLIB_TEST(a.contains<test>() == false);
        DLIB_TEST(a.is_empty());
        a = b;
        swap(a,b);
        a.swap(b);
        a = test();
        test_contains_4(a);
        bool error = false;
        try
        {
            any_cast<int(*)()>(a);
        }
        catch (bad_any_cast&)
        {
            error = true;
        }
        DLIB_TEST(error);
        swap(a,b);
        test_contains_4(b);
        DLIB_TEST(a.is_empty());
        a = b;
        test_contains_4(a);
        c.get<test2>() = test2(10);
        DLIB_TEST(c.get<test2>().v == 10); 
        a = c;
        DLIB_TEST(a.cast_to<test2>().v == 10); 
        a.clear();
        DLIB_TEST(a.is_empty());
        error = false;
        try
        {
            any_cast<test>(a);
        }
        catch (bad_any_cast&)
        {
            error = true;
        }
        DLIB_TEST(error);
    }
// ----------------------------------------------------------------------------------------
    void run_test2()
    {
        any_function<int(int,int)> f = &add;
        DLIB_TEST(f(1,3) == 4);
        any_function<string(string,string)> g(&cat);
        DLIB_TEST(g("one", "two") == "onetwo");
    }
// ----------------------------------------------------------------------------------------
    void run_test3()
    {
        any_function<void(int&)> f1;
        any_function<void(int&,int&)> f2;
        any_function<void(int&,int&,int&)> f3;
        any_function<void(int&,int&,int&,int&)> f4;
        any_function<void(int&,int&,int&,int&,int&)> f5;
        any_function<void(int&,int&,int&,int&,int&,int&)> f6;
        any_function<void(int&,int&,int&,int&,int&,int&,int&)> f7;
        any_function<void(int&,int&,int&,int&,int&,int&,int&,int&)> f8;
        any_function<void(int&,int&,int&,int&,int&,int&,int&,int&,int&)> f9;
        any_function<void(int&,int&,int&,int&,int&,int&,int&,int&,int&,int&)> f10;
        f1 = set_vals1;
        f2 = set_vals2;
        f3 = set_vals3;
        f4 = set_vals4;
        f5 = set_vals5;
        f6 = set_vals6;
        f7 = set_vals7;
        f8 = set_vals8;
        f9 = set_vals9;
        f10 = set_vals10;
        int a,b,c,d,e,f,g,h,i,j;
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f1(a);
        DLIB_TEST(a==1);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f2(a,b);
        DLIB_TEST(a==1 && b==2);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f3(a,b,c);
        DLIB_TEST(a==1 && b==2 && c==3);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f4(a,b,c,d);
        DLIB_TEST(a==1 && b==2 && c==3 && d==4);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f5(a,b,c,d,e);
        DLIB_TEST(a==1 && b==2 && c==3 && d==4 && e==5);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f6(a,b,c,d,e,f);
        DLIB_TEST(a==1 && b==2 && c==3 && d==4 && e==5 && f==6);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f7(a,b,c,d,e,f,g);
        DLIB_TEST(a==1 && b==2 && c==3 && d==4 && e==5 && f==6 && g==7);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f8(a,b,c,d,e,f,g,h);
        DLIB_TEST(a==1 && b==2 && c==3 && d==4 && e==5 && f==6 && g==7 && h==8);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        f9(a,b,c,d,e,f,g,h,i);
        DLIB_TEST(a==1 && b==2 && c==3 && d==4 && e==5 && f==6 && g==7 && h==8 && i==9);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
        
        f10(a,b,c,d,e,f,g,h,i,j);
        DLIB_TEST(a==1 && b==2 && c==3 && d==4 && e==5 && f==6 && g==7 && h==8 && i==9 && j==10);
        zero_vals(a,b,c,d,e,f,g,h,i,j);
    }
// ----------------------------------------------------------------------------------------
    class test_any_function : public tester
    {
    public:
        test_any_function (
        ) :
            tester ("test_any_function",
                    "Runs tests on the any_function component.")
        {}
        void perform_test (
        )
        {
            print_spinner();
            run_test();
            print_spinner();
            run_test2();
            print_spinner();
            run_test3();
        }
    } a;
}