// Copyright (C) 2005  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/stack.h>

#include "tester.h"

namespace  
{

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

    logger dlog("test.stack");

    template <
        typename stack
        >
    void stack_kernel_test (
    )
    /*!
        requires
            - stack is an implementation of stack/stack_sort_abstract.h 
              stack is instantiated with int
        ensures
            - runs tests on stack for compliance with the specs
    !*/
    {        


        srand(static_cast<unsigned int>(time(0)));

        print_spinner();

        stack a1, a2;



        DLIB_TEST(a1.size() == 0);
        DLIB_TEST(a1.at_start());
        DLIB_TEST(a1.current_element_valid() == false);
        DLIB_TEST(a1.move_next() == false);
        DLIB_TEST(a1.size() == 0);
        DLIB_TEST(a1.current_element_valid() == false);
        DLIB_TEST(a1.at_start() == false);
        DLIB_TEST(a1.move_next() == false);
        DLIB_TEST(a1.current_element_valid() == false);
        DLIB_TEST(a1.size() == 0);
        DLIB_TEST(a1.at_start() == false);            
        DLIB_TEST(a1.size() == 0);

        swap(a1,a2);
        DLIB_TEST(a2.size() == 0);
        DLIB_TEST(a2.current_element_valid() == false);
        DLIB_TEST(a2.at_start() == false);
        DLIB_TEST(a2.move_next() == false);
        DLIB_TEST(a2.current_element_valid() == false);
        DLIB_TEST(a2.size() == 0);
        DLIB_TEST(a2.at_start() == false);            
        DLIB_TEST(a2.size() == 0);



        DLIB_TEST(a1.size() == 0);
        DLIB_TEST(a1.at_start());
        DLIB_TEST(a1.current_element_valid() == false);
        DLIB_TEST(a1.move_next() == false);
        DLIB_TEST(a1.size() == 0);
        DLIB_TEST(a1.current_element_valid() == false);
        DLIB_TEST(a1.at_start() == false);
        DLIB_TEST(a1.move_next() == false);
        DLIB_TEST(a1.current_element_valid() == false);
        DLIB_TEST(a1.size() == 0);
        DLIB_TEST(a1.at_start() == false);            
        DLIB_TEST(a1.size() == 0);

        a1.reset();
        a2.reset();

        for (unsigned long k = 0; k < 4; ++k)
        {

            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start());
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.at_start() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start() == false);            
            DLIB_TEST(a1.size() == 0);

            swap(a1,a2);
            DLIB_TEST(a2.size() == 0);
            DLIB_TEST(a2.current_element_valid() == false);
            DLIB_TEST(a2.at_start() == false);
            DLIB_TEST(a2.move_next() == false);
            DLIB_TEST(a2.current_element_valid() == false);
            DLIB_TEST(a2.size() == 0);
            DLIB_TEST(a2.at_start() == false);            
            DLIB_TEST(a2.size() == 0);



            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start());
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.at_start() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start() == false);            
            DLIB_TEST(a1.size() == 0);

            a1.clear();
            a2.clear();


            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start());
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.at_start() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start() == false);            
            DLIB_TEST(a1.size() == 0);

            swap(a1,a2);
            DLIB_TEST(a2.size() == 0);
            DLIB_TEST(a2.current_element_valid() == false);
            DLIB_TEST(a2.at_start() == false);
            DLIB_TEST(a2.move_next() == false);
            DLIB_TEST(a2.current_element_valid() == false);
            DLIB_TEST(a2.size() == 0);
            DLIB_TEST(a2.at_start() == false);            
            DLIB_TEST(a2.size() == 0);



            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start());
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.at_start() == false);
            DLIB_TEST(a1.move_next() == false);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a1.at_start() == false);            
            DLIB_TEST(a1.size() == 0);

            a1.clear();
            a2.clear();


            for (unsigned long i = 0; i < 100; ++i)
            {
                int a = (int)i;
                a1.push(a);
            }

            DLIB_TEST(a1.size() == 100);

            int count = 99;
            while (a1.move_next())
            {
                DLIB_TEST_MSG(a1.element() == count,a1.element() << " : " << count);
                --count;
            }

            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.at_start() == false);

            a1.swap(a2);

            count = 99;
            DLIB_TEST(a2.current_element_valid() == false);
            DLIB_TEST(a2.at_start() == false);
            DLIB_TEST(a1.current_element_valid() == false);
            DLIB_TEST(a1.at_start() == true);

            DLIB_TEST(a1.size() == 0);
            DLIB_TEST(a2.size() == 100);
            DLIB_TEST(a2.current() == 99);

            a2.reset();
            while (a2.move_next())
            {
                DLIB_TEST(a2.element() == count--);
            }                

            DLIB_TEST(a2.current_element_valid() == false);
            DLIB_TEST(a2.at_start() == false);
            int b = 4;
            a2.push(b);
            DLIB_TEST(a2.current_element_valid() == false);
            DLIB_TEST(a2.at_start() == true);

            DLIB_TEST(a2.current() == 4);
            int c = 0;
            a2.pop(c);
            DLIB_TEST(c == 4);

            // serialize the state of a2, then clear a2, then
            // load the state back into a2.
            ostringstream sout;
            serialize(a2,sout);
            DLIB_TEST(a2.at_start() == true);
            istringstream sin(sout.str());
            a2.clear();
            deserialize(a2,sin);


            count = 99;
            while (a2.size())
            {
                int a = 0;
                DLIB_TEST(a2.current() == count);
                DLIB_TEST(const_cast<const stack&>(a2).current() == count);
                a2.pop(a);
                DLIB_TEST(a == count--);
            }






            a1.clear();
            a2.clear();
        }


        {
            a1.clear();
            remover<int>& go = a1;
            for (int i = 0; i < 100; ++i)
            {
                int a = 3;
                a1.push(a);
            }
            DLIB_TEST(go.size() == 100);                
            for (int i = 0; i < 100; ++i)
            {
                int a = 9;
                a1.remove_any(a);
                DLIB_TEST(a == 3);
            }
            DLIB_TEST(go.size() == 0);
        }

    }




    class stack_tester : public tester
    {
    public:
        stack_tester (
        ) :
            tester ("test_stack",
                    "Runs tests on the stack component.")
        {}

        void perform_test (
        )
        {
            dlog << LINFO << "testing kernel_1a";
            stack_kernel_test<stack<int>::kernel_1a>  ();
            dlog << LINFO << "testing kernel_1a_c";
            stack_kernel_test<stack<int>::kernel_1a_c>();
        }
    } a;

}