/*
 *             Copyright Andrey Semashev 2014.
 * 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)
 */
/*!
 * \file   scoped_enum.cpp
 * \author Andrey Semashev
 * \date   06.06.2014
 *
 * \brief  This test checks that scoped enum emulation works similar to C++11 scoped enums.
 */

#include <boost/core/scoped_enum.hpp>
#include <boost/core/lightweight_test.hpp>

#if defined(_MSC_VER)
# pragma warning(disable: 4244) // conversion from enum_type to underlying_type
#endif

BOOST_SCOPED_ENUM_DECLARE_BEGIN(namespace_enum1)
{
    value0,
    value1,
    value2
}
BOOST_SCOPED_ENUM_DECLARE_END(namespace_enum1)

BOOST_SCOPED_ENUM_UT_DECLARE_BEGIN(namespace_enum2, unsigned char)
{
    // Checks that enum value names do not clash
    value0 = 10,
    value1 = 20,
    value2 = 30
}
BOOST_SCOPED_ENUM_DECLARE_END(namespace_enum2)

struct my_struct
{
    // Checks that declarations are valid in class scope
    BOOST_SCOPED_ENUM_DECLARE_BEGIN(color)
    {
        red,
        green,
        blue
    }
    BOOST_SCOPED_ENUM_DECLARE_END(color)

    color m_color;

    explicit my_struct(color col) : m_color(col)
    {
    }

    color get_color() const
    {
        return m_color;
    }
};

void check_operators()
{
    namespace_enum1 enum1 = namespace_enum1::value0;
    BOOST_TEST(enum1 == namespace_enum1::value0);
    BOOST_TEST(enum1 != namespace_enum1::value1);
    BOOST_TEST(enum1 != namespace_enum1::value2);

    enum1 = namespace_enum1::value1;
    BOOST_TEST(enum1 != namespace_enum1::value0);
    BOOST_TEST(enum1 == namespace_enum1::value1);
    BOOST_TEST(enum1 != namespace_enum1::value2);

    BOOST_TEST(!(enum1 < namespace_enum1::value0));
    BOOST_TEST(!(enum1 <= namespace_enum1::value0));
    BOOST_TEST(enum1 >= namespace_enum1::value0);
    BOOST_TEST(enum1 > namespace_enum1::value0);

    BOOST_TEST(!(enum1 < namespace_enum1::value1));
    BOOST_TEST(enum1 <= namespace_enum1::value1);
    BOOST_TEST(enum1 >= namespace_enum1::value1);
    BOOST_TEST(!(enum1 > namespace_enum1::value1));

    namespace_enum1 enum2 = namespace_enum1::value0;
    BOOST_TEST(enum1 != enum2);

    enum2 = enum1;
    BOOST_TEST(enum1 == enum2);
}

void check_argument_passing()
{
    my_struct str(my_struct::color::green);
    BOOST_TEST(str.get_color() == my_struct::color::green);
}

void check_switch_case()
{
    my_struct str(my_struct::color::blue);

    switch (boost::native_value(str.get_color()))
    {
    case my_struct::color::blue:
        break;
    default:
        BOOST_ERROR("Unexpected color value in switch/case");
    }
}

template< typename T >
struct my_trait
{
    enum _ { value = 0 };
};

template< >
struct my_trait< BOOST_SCOPED_ENUM_NATIVE(namespace_enum2) >
{
    enum _ { value = 1 };
};

template< typename T >
void native_type_helper(T)
{
    BOOST_TEST(my_trait< T >::value != 0);
}

void check_native_type()
{
    BOOST_TEST(my_trait< int >::value == 0);
    BOOST_TEST(my_trait< BOOST_SCOPED_ENUM_NATIVE(namespace_enum2) >::value != 0);
    BOOST_TEST(my_trait< boost::native_type< namespace_enum2 >::type >::value != 0);

    namespace_enum2 enum1 = namespace_enum2::value0;
    native_type_helper(boost::native_value(enum1));
}

void check_underlying_cast()
{
    namespace_enum2 enum1 = namespace_enum2::value1;
    BOOST_TEST(boost::underlying_cast< unsigned char >(enum1) == 20);
}

void check_underlying_type()
{
    // The real check for the type is in the underlying_type trait test.
    namespace_enum2 enum1 = namespace_enum2::value1;
    BOOST_TEST(sizeof(enum1) == sizeof(unsigned char));
    (void)enum1;
}

int main(int, char*[])
{
    check_operators();
    check_argument_passing();
    check_switch_case();
    check_native_type();
    check_underlying_cast();
    check_underlying_type();

    return boost::report_errors();
}
