// Copyright Alexander Nasonov 2007-2008
//
// Distributed under the Boost Software License, Version 1.0. 
// (See accompanying file LICENSE_1_0.txt or copy at 
// http://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <ostream>
#include <string>

#include <boost/scope_exit.hpp>

#include <boost/typeof/typeof.hpp>
#include <boost/typeof/std/string.hpp>
#include <boost/test/unit_test.hpp>

using namespace boost::unit_test;

std::string g_str;

template<int Dummy = 0>
struct Holder
{
    static long g_long;
};

template<int Dummy> long Holder<Dummy>::g_long;

void test_non_local()
{
    // ... and one local variable as well:
    int i = 0;

    BOOST_SCOPE_EXIT( (i) )
    {
        BOOST_CHECK(i == 0);
        BOOST_CHECK(Holder<>::g_long == 3);
        BOOST_CHECK(g_str == "try: g_str");
    } BOOST_SCOPE_EXIT_END

    BOOST_SCOPE_EXIT( (&i) )
    {
        BOOST_CHECK(i == 3);
        BOOST_CHECK(Holder<>::g_long == 3);
        BOOST_CHECK(g_str == "try: g_str");
    } BOOST_SCOPE_EXIT_END

    {
        g_str = "";
        Holder<>::g_long = 1;

        BOOST_SCOPE_EXIT( (&i) )
        {
            i = 1;
            g_str = "g_str";
        } BOOST_SCOPE_EXIT_END

        BOOST_SCOPE_EXIT( (&i) )
        {
          try
          {
              i = 2;
              Holder<>::g_long = 2;
              throw 0;
          } catch(...) {}
        } BOOST_SCOPE_EXIT_END

        BOOST_CHECK(i == 0);
        BOOST_CHECK(g_str == "");
        BOOST_CHECK(Holder<>::g_long == 1);
    }

    BOOST_CHECK(Holder<>::g_long == 2);
    BOOST_CHECK(g_str == "g_str");
    BOOST_CHECK(i == 1); // Check that first declared is executed last

    BOOST_SCOPE_EXIT( (&i) )
    {
        BOOST_CHECK(i == 3);
        BOOST_CHECK(Holder<>::g_long == 3);
        BOOST_CHECK(g_str == "try: g_str");
    } BOOST_SCOPE_EXIT_END

    BOOST_SCOPE_EXIT( (i) )
    {
        BOOST_CHECK(i == 1);
        BOOST_CHECK(Holder<>::g_long == 3);
        BOOST_CHECK(g_str == "try: g_str");
    } BOOST_SCOPE_EXIT_END

    try
    {
        BOOST_SCOPE_EXIT( (&i) )
        {
            i = 3;
            g_str = "try: g_str";
        } BOOST_SCOPE_EXIT_END
        
        BOOST_SCOPE_EXIT( (&i) )
        {
            i = 4;
            Holder<>::g_long = 3;
        } BOOST_SCOPE_EXIT_END

        BOOST_CHECK(i == 1);
        BOOST_CHECK(g_str == "g_str");
        BOOST_CHECK(Holder<>::g_long == 2);

        throw 0;
    }
    catch(int)
    {
        BOOST_CHECK(Holder<>::g_long == 3);
        BOOST_CHECK(g_str == "try: g_str");
        BOOST_CHECK(i == 3); // Check that first declared is executed last
    }
}

bool foo()
{
    return true;
}

void test_types()
{
    bool (*pf)() = 0;
    bool (&rf)() = foo;
    bool results[2] = {};

    {
        BOOST_SCOPE_EXIT( (&results)(&pf)(&rf) )
        {
            results[0] = pf();
            results[1] = rf();
        }
        BOOST_SCOPE_EXIT_END

        pf = &foo;

        BOOST_CHECK(results[0] == false);
        BOOST_CHECK(results[1] == false);
    }

    BOOST_CHECK(results[0] == true);
    BOOST_CHECK(results[1] == true);

    {
        BOOST_SCOPE_EXIT( (&results)(pf) )
        {
            results[0] = !pf();
            results[1] = !pf();
        }
        BOOST_SCOPE_EXIT_END

        pf = 0;

        BOOST_CHECK(results[0] == true);
        BOOST_CHECK(results[1] == true);
    }

    BOOST_CHECK(results[0] == false);
    BOOST_CHECK(results[1] == false);
}

test_suite* init_unit_test_suite( int, char* [] )
{
    framework::master_test_suite().p_name.value = "Unit test for ScopeExit";
    framework::master_test_suite().add( BOOST_TEST_CASE( &test_non_local ));
    framework::master_test_suite().add( BOOST_TEST_CASE( &test_types     ));
    return 0;
}

