// ------------------------------------------------------------------------
/*
author: Jaap Suter

Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#include <iostream>
#include <ostream>
#include <exception>
#include <assert.h>
#include <malloc.h>
#include <stdlib.h>

#include "reveal/reveal.h"
#include "reveal/renderer.h"
#include "reveal/comment_parser.h"
#include "reveal/win32_pc_input_state_impl.h"
#include "reveal/random_input_state_impl.h"

#include "reveal/callback/mem_fun_adapter.h"

#include "reveal/type_traits/type_traits.h"

#include "reveal/callback/unit_test.h"
#include "reveal/smart_ptr/unit_test.h"
#include "reveal/type_traits/unit_test.h"

#include "reveal/custom_memory.h"

#include "reveal/context/bound_context.h"
#include "reveal/context/context_adapters.h"
#include "reveal/context/context_container.h"

#include "gdi_renderer.h"

#include "reveal/global_enable.h"

#include "reveal/graph.h"

namespace
{
    class TestFailure
    {
    public:
        TestFailure( const char* s ) : mWhat( s ) {};
        const char* What() const { return mWhat.c_str(); }
    private:
        std::string mWhat;
    };
}

// -------------------------------------------------------------------------------
// Memory implementation;
// -------------------------------------------------------------------------------
namespace Reveal {
    namespace Memory {
        void* RevealMalloc( size_t size )
        {
            void* ptr = new char[ size ];
            return ptr;
        }

        void RevealFree( void* ptr )
        {
            delete [] ptr;
        }
    }
}

// -------------------------------------------------------------------------------
// Test Enum;
// -------------------------------------------------------------------------------
typedef enum
{
    OPTION_1,
    OPTION_2,
    OPTION_3,
    OPTION_4,
    OPTION_5,
    OPTION_6,
    OPTION_7,
    OPTION_8,
    OPTION_9,
    OPTION_0

} SomeEnumType;

// -------------------------------------------------------------------------------
// Test Callback Functions;
// -------------------------------------------------------------------------------
namespace
{
    SomeEnumType gSomeEnum = OPTION_1;

    float gFloat = 0.0f;
    void SetFloatRange( float f ) { gTweaker.SetStatusBarMessage( "Float range set" ); gFloat = f; }
    float GetFloatRange() { return gFloat; }

    int gInt = 0;
    void SetIntegerRange( int i ) { gTweaker.SetStatusBarMessage( "Integer range set" ); gInt = i; }
    int  GetIntegerRange() { return gInt; }

    bool gBool = false;
    void SetCheckBox( bool b ) { gTweaker.SetStatusBarMessage( "Checkbox set" ); gBool = b; }
    bool GetCheckBox( ) { return gBool; }

    Reveal::Color gColor;
    void SetColor( const Reveal::Color& color ) { gTweaker.SetStatusBarMessage( "Color Callback called" ); gColor = color; }
    Reveal::Color GetColor() { return gColor; }

    void PauseSystem( bool pause )					  { gTweaker.SetStatusBarMessage( "Pause button clicked." ); }
    void Button()									  { gTweaker.SetStatusBarMessage( "Button pressed" ); }
    void EnumCallback( SomeEnumType e )				  { gTweaker.SetStatusBarMessage( "Input Callback called" ); }
    bool AuxCallback0( const Reveal::InputState& )	  { gTweaker.SetStatusBarMessage( "Auxilliary Callback 0 called" ); return true; }
    bool AuxCallback1( const Reveal::InputState& )	  { gTweaker.SetStatusBarMessage( "Auxilliary Callback 1 called" ); return true; }
    int  ReturnInt() { return 0; }

    float gWatchFloat = 0.0f;
    float GetWatchFloat() { return gWatchFloat; }

    int gWatchInt = 0;
    int GetWatchInt() { return gWatchInt; }

    //debug ConfirmMenuFlag
    void SetConfirmMenuFlag( bool v )
    {
        char buffer[256];
        if (v)
        {
            sprintf(buffer, "Enabled ConfirmMenuFlag");
        }
        else
        {
            sprintf(buffer, "Disabled ConfirmMenuFlag");
        }

        gTweaker.SetConfirmMenuExpansion(v);

        gTweaker.SetStatusBarMessage(buffer);
    }

}

template < class T >
struct ContextSensitiveObject
{
public:
    ContextSensitiveObject( T t, float x, float y, float z ) : mT( t ), mX( x ), mY( y ), mZ( z ) {}
    T Get() { return mT; }
    void Set( T t )
    {
        mT = t;
    }
    void Button()
    {
        if ( Reveal::TypeTraits::IsFloatingPoint< T >::value )
            gTweaker.SetStatusBarMessage( "float\n" );
        else
            gTweaker.SetStatusBarMessage( "int\n" );
    }

    void PositionCallback( float& x, float& y, float& z ) const
    {
        x = mX;
        y = mY;
        z = mZ;
    }

    void Render( const Reveal::Renderer& renderer )
    {
        renderer.Bar( static_cast< int >( mX ) - 15,
            static_cast< int >( mY ) - 15,
            30, 30, 0xFFFFFFFF );
    }

    void ClickAndHoldCallback( const Reveal::InputState& inputState )
    {
        mX = static_cast< float >( inputState.MouseX() );
        mY = static_cast< float >( inputState.MouseY() );
    }

private:
    T mT;
    float mX, mY, mZ;
};

typedef ContextSensitiveObject< float > ContextSensitiveFloat;
typedef ContextSensitiveObject<   int > ContextSensitiveInt;

struct IntegerFunctor
{
    void operator () ( int i )
    {
        gTweaker.SetStatusBarMessage( "functor called" );
    }

    int operator () ()
    {
        return 2;
    }
};

struct MemberFunctionTest
{
    int mInt;
    void SetMemberFunction( int i )
    {
        mInt = i;
        gTweaker.SetStatusBarMessage( "member function called" );
    }

    int GetMemberFunction()
    {
        return mInt;
    }
};

void PrintStupidMessage()
{
    gTweaker.SetStatusBarMessage( "stupid message" );
}

void RemoveEntireMenu()
{
    gTweaker.SetStatusBarMessage( "menu removed" );
    gTweaker.Remove( "dynamic/added control with long blah", NULL );
}

void RemoveControl()
{
    gTweaker.SetStatusBarMessage( "control removed" );
    gTweaker.Remove( "dynamic/added control with long blah", "even way longer blah" );
}

void AddControl()
{
    gTweaker.SetStatusBarMessage( "button removed" );
    gTweaker.AddButtonControl( "dynamic/added control with long blah", "even way longer blah", &PrintStupidMessage );
}

// -------------------------------------------------------------------------------
// Large list of enums;
// -------------------------------------------------------------------------------
const size_t gLargeEnumDescSize = 70;
const Reveal::Option< SomeEnumType > gLargeEnumDesc[ gLargeEnumDescSize ] =
{
    { OPTION_0, "option  0" },
    { OPTION_1, "option  1" },
    { OPTION_2, "option  2" },
    { OPTION_3, "option  3" },
    { OPTION_4, "option  4" },
    { OPTION_5, "option  5" },
    { OPTION_6, "option  6" },
    { OPTION_7, "option  7" },
    { OPTION_9, "option  8" },
    { OPTION_0, "option  9" },
    { OPTION_1, "option  0" },
    { OPTION_2, "option 11" },
    { OPTION_3, "option 12" },
    { OPTION_4, "option 13" },
    { OPTION_5, "option 14" },
    { OPTION_6, "option 15" },
    { OPTION_7, "option 16" },
    { OPTION_8, "option 17" },
    { OPTION_9, "option 18" },
    { OPTION_0, "option 19" },
    { OPTION_9, "option 20" },
    { OPTION_8, "option 21" },
    { OPTION_7, "option 22" },
    { OPTION_6, "option 23" },
    { OPTION_5, "option 24" },
    { OPTION_4, "option 25" },
    { OPTION_3, "option 26" },
    { OPTION_2, "option 27" },
    { OPTION_1, "option 28" },
    { OPTION_0, "option 29" },
    { OPTION_9, "option 30" },
    { OPTION_8, "option 31" },
    { OPTION_7, "option 32" },
    { OPTION_6, "option 33" },
    { OPTION_5, "option 34" },
    { OPTION_4, "option 35" },
    { OPTION_3, "option 36" },
    { OPTION_2, "option 37" },
    { OPTION_1, "option 38" },
    { OPTION_0, "option 39" },
    { OPTION_9, "option 40" },
    { OPTION_8, "option 41" },
    { OPTION_7, "option 42" },
    { OPTION_6, "option 43" },
    { OPTION_5, "option 44" },
    { OPTION_4, "option 45" },
    { OPTION_3, "option 46" },
    { OPTION_2, "option 47" },
    { OPTION_1, "option 48" },
    { OPTION_2, "option 49" },
    { OPTION_3, "option 50" },
    { OPTION_4, "option 51" },
    { OPTION_5, "option 52" },
    { OPTION_6, "option 53" },
    { OPTION_7, "option 54" },
    { OPTION_8, "option 55" },
    { OPTION_9, "option 56" },
    { OPTION_0, "option 57" },
    { OPTION_2, "option 58" },
    { OPTION_2, "option 59" },
    { OPTION_1, "option 60" },
    { OPTION_2, "option 61" },
    { OPTION_2, "option 62" },
    { OPTION_2, "option 63" },
    { OPTION_6, "option 64" },
    { OPTION_8, "option 65" },
    { OPTION_9, "option 66" },
    { OPTION_5, "option 67" },
    { OPTION_4, "option 68" },
    { OPTION_7, "option 69" },
};

// -------------------------------------------------------------------------------
struct EnableReveal
{
    EnableReveal( bool enable ) { Reveal::SetGlobalRevealEnable( enable ); }
} enableReveal( true );

// -------------------------------------------------------------------------------
// Application entry point;
// -------------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int showCmd )
{
    bool soak = false;
    if ( strstr( cmdLine, "test" ) != 0 ) soak = true;
    unsigned int soakDuration = 4096;

    try
    {
        ShowCursor( false );

#ifdef REVEAL
        if ( Reveal::IsRevealGloballyEnabled() )
        {
            if ( !Reveal::Callback::UnitTest::Test()   ) throw TestFailure( "Callback Library Test Failed" );
            if ( !Reveal::SmartPtr::UnitTest::Test()   ) throw TestFailure( "SmartPtr Library Test Failed" );
            if ( !Reveal::TypeTraits::UnitTest::Test() ) throw TestFailure( "TypeTraits Library Test Failed" );
        }
#endif

        GdiRenderer renderer( hInstance, showCmd );

        int i = 0;
        bool b = false;
        unsigned int uint = 0;;

        MemberFunctionTest m;
        m.mInt = 0;

        gTweaker.SetPauseCallback( &PauseSystem );


        gTweaker.AddCheckBoxControl( "camera/depth of field", "readonly", &SetCheckBox, ReturnRefAdapter( b ) );
        gTweaker.AddCheckBoxControl( "camera/depth of field", "enable", AssignToRefAdapter( b ), ReturnRefAdapter( b ) );		
        gTweaker.AddRangeControl(	 "camera/depth of field", "sharpness",  0.0f, 1.0f, &SetFloatRange, &GetFloatRange );
        gTweaker.AddRangeControl(	 "camera/depth of field", "focus",      0,      10, Callback1< void, int >( IntegerFunctor() ), Callback0< int >( IntegerFunctor() ) );		

        gTweaker.AddRangeControl( "camera", "blah", 0, 10, MemFunAdapter1( m, &MemberFunctionTest::SetMemberFunction ), MemFunAdapter0( m, &MemberFunctionTest::GetMemberFunction ) );

        gTweaker.AddCheckBoxControl("test/", "ConfirmMenu", &SetConfirmMenuFlag);

        gTweaker.AddRangeControl( "test/", "unsigned", static_cast< unsigned int >( 0 ), static_cast< unsigned int >( 10 ), AssignToRefAdapter( uint ), ReturnRefAdapter( uint ) );
        gTweaker.AddRangeControl( "test/read", "only",   0, 90, Reveal::Callback::CallbackStub1< int >(), &ReturnInt );
        gTweaker.AddRangeControl( "test/deep0/deep1/deep2/deep3/deep4/deep5/deep6/deep7/deep8/deep9/", "rotation",   1000, 9000, &SetIntegerRange, &GetIntegerRange );

        gTweaker.AddButtonControl( "test/dynamic", "rem menu",    &RemoveEntireMenu );
        gTweaker.AddButtonControl( "test/dynamic", "rem control", &RemoveControl );		
        gTweaker.AddButtonControl( "test/dynamic", "add control", &AddControl );

        gTweaker.AddRangeControl( "camera/model", "fov",        0,      90, AssignToRefAdapter( i ), ReturnRefAdapter( i ) );
        gTweaker.AddRangeControl( "camera/model", "fov",        0,      90, &SetIntegerRange, ReturnRefAdapter( i ) );
        gTweaker.AddRangeControl( "camera/model", "resolution", 0,      10, &SetIntegerRange, &ReturnInt );

        gTweaker.AddColorControl( "color", "test1", &SetColor, &GetColor );

        Reveal::Impl::OptionControl< SomeEnumType > *largeOptionControl =
            gTweaker.AddOptionControl( "fifa/player", "select player1", &EnumCallback, ReturnRefAdapter( gSomeEnum ), gLargeEnumDesc, gLargeEnumDesc + gLargeEnumDescSize );
        if ( largeOptionControl )
        {
            largeOptionControl->AddOptions( gLargeEnumDesc, gLargeEnumDesc + 2 );
            largeOptionControl->SetNumOptionsVisible( 10 );
        }

        gTweaker.AddOptionControl( "fifa/player3", "select player2", &EnumCallback, ReturnRefAdapter( gSomeEnum ), gLargeEnumDesc, gLargeEnumDesc + gLargeEnumDescSize );

        Reveal::Impl::OptionControl< SomeEnumType > *smallOptionControl =
            gTweaker.AddOptionControl( "fifa/player2", "select player2", &EnumCallback, ReturnRefAdapter( gSomeEnum ), gLargeEnumDesc, gLargeEnumDesc + 2 );
        if ( smallOptionControl )
        {
            smallOptionControl->AddOptions( gLargeEnumDesc, gLargeEnumDesc + 6 );
            smallOptionControl->SetNumOptionsVisible( 5 );
        }

        Reveal::Impl::OptionControl< SomeEnumType > *emptyOptionControl = 		
            gTweaker.AddOptionControl( "fifa/player3", "select player3", &EnumCallback );
        if ( emptyOptionControl )
        {
            emptyOptionControl->AddOptions(	   gLargeEnumDesc, gLargeEnumDesc + 2 );
            emptyOptionControl->AddOptions(    gLargeEnumDesc, gLargeEnumDesc + 40 );
            emptyOptionControl->RemoveOptions( gLargeEnumDesc, gLargeEnumDesc + 1 );
            emptyOptionControl->SetNumOptionsVisible( 3 );
        }

        //NEW added by Marcos: Aux button #'s are auto assigned now
        // Set two auxilliary callbacks;
        gTweaker.AddAuxCallback( "aux 0", &AuxCallback0 );
        gTweaker.AddAuxCallback( "aux 1", &AuxCallback1 );

        // If we really want to, we can set a specific aux button (2 in this case)
        // Add the window system to Reveal
        // Todo: document this reveal feature;
        gTweaker.SetAuxCallback( 2, "hide", Reveal::Callback::MemFunAdapter1( gTweaker, &Tweaker::WindowSystem ), Tweaker::DRAW_WINDOWS_AND_MOUSE );

        // Test the Reveal comment parser;
        {
            Reveal::CommentParser commentParser;
            commentParser.Parse( "// Reveal::Tweaker::AddRangeControl(\"path\", \"name\", -3, 6 );" );
            if ( commentParser.Type() != Reveal::CommentParser::RANGE_CONTROL ) throw TestFailure( "CommentParser Test Failed" );
            commentParser.Reset();
            commentParser.Parse( "// Reveal::Tweaker::AddOptionControl(\"path\", \"name\", \"a = 3, b, c = 6, d, e \");" );
            if ( commentParser.Type() != Reveal::CommentParser::OPTION_CONTROL ) throw TestFailure( "CommentParser Test Failed" );
            commentParser.Reset();
            commentParser.Parse( "// Reveal::Tweaker::Afdsa_GARBAGE_lhfdjsath\", \"name\", \"a = 3, b, c = 6, d, e \");" );
            if ( commentParser.Type() != Reveal::CommentParser::INVALID ) throw TestFailure( "CommentParser Test Failed" );
            commentParser.Reset();
            commentParser.Parse( "// Reveal::Tweaker::AddOptionControl(\"pat_GARBAGE FDSJK LHFE UIHFES  FDJLK LAF EUI  " );
            if ( commentParser.Type() != Reveal::CommentParser::INVALID ) throw TestFailure( "CommentParser Test Failed" );
            commentParser.Reset();
        }

        // Change the GUI colors to something nice;
        Reveal::GuiColors guiColors;
        guiColors.mCursorColor = 0xFFAAAAAA;
        guiColors.mTextColor = 0xFFFFFFFF;
        guiColors.mStatusBarColor = 0x66000000;
        guiColors.mSliderColor = 0xFFFFFFFF;
        guiColors.mBorderColor = 0xFFFFFFFF;
        guiColors.mWidgetBackgroundColor = 0x66000000;
        guiColors.mWidgetHighlightedBackgroundColor = 0xFFD0E0EF;
        guiColors.mWidgetSameLevelBackgroundColor = 0x88000000;
        guiColors.mMruButtonColor = 0x66000000;
        guiColors.mHighlightedMruButtonColor = 0xFF777777;
        guiColors.mInteractiveBackgroundColor = 0xFFAAAAAA;
        guiColors.mInteractiveForegroundColor = 0xFF000000;
        guiColors.mButtonColor = 0xFF444444;
        guiColors.mButtonDownColor = 0xFFAAAAAA;
        guiColors.mOptionBoxSelectionColor = 0xFF777777;
        guiColors.mOptionBoxMouseOverColor = 0xFFD0E0EF;
        guiColors.mMruUnlockedColor = 0xFF000000;
        guiColors.mMruLockedColor = 0xFFD0E0EF;
        gTweaker.SetGuiColors( guiColors );

        // Add graphs to Reveal
#if defined _MSC_VER
        Reveal::Graph< 100, float >* graph0 = gTweaker.AddGraph< 100 >( "graphs", "float", 0.0f );
        Reveal::Graph< 300, int   >* graph1 = gTweaker.AddGraph< 300 >( "graphs", "int",     30 );

        gTweaker.AddWatch("watch", "wFloat", &GetWatchFloat );
        gTweaker.AddWatch("watch", "wInt", &GetWatchInt);
        gTweaker.AddWatch("watch", "wIntRef", ReturnRefAdapter(gWatchInt));

#endif

        // Add a context container and some contexts to Reveal;
        Reveal::Context::ContextContainer contextContainer;
        Reveal::Context::BoundContext< ContextSensitiveFloat > floatContext( "float" );
        Reveal::Context::BoundContext< ContextSensitiveInt   > intContext( "int" );

        floatContext.AddRangeControl( "", "test", 0.0f, 1.0f, Reveal::Context::ContextAdapter1( &ContextSensitiveFloat::Set, floatContext ), Reveal::Context::ContextAdapter0( &ContextSensitiveFloat::Get, floatContext ) );	
        intContext.AddRangeControl( "", "test", 0, 10, &SetIntegerRange, &GetIntegerRange );	
        floatContext.AddButtonControl( "", "button", Reveal::Context::ContextAdapter0( &ContextSensitiveFloat::Button, floatContext ) );
        intContext.AddButtonControl( "", "button", Reveal::Context::ContextAdapter0( &ContextSensitiveInt::Button, intContext ) );	

        ContextSensitiveFloat sensitiveFloat( 0.0f, 100.0f, 100.0f, 0.0f );
        ContextSensitiveInt   sensitiveInt( 1, 400.0f, 400.0f, 0.0f );
        contextContainer.BindObjectToContext( 30.0f, Reveal::Callback::MemFunAdapter1( sensitiveFloat, &ContextSensitiveFloat::ClickAndHoldCallback ), Reveal::Callback::MemFunAdapter3( sensitiveFloat, &ContextSensitiveFloat::PositionCallback ), sensitiveFloat, floatContext );
        contextContainer.BindObjectToContext( 30.0f, Reveal::Callback::MemFunAdapter1(   sensitiveInt, &ContextSensitiveInt::ClickAndHoldCallback ), Reveal::Callback::MemFunAdapter3(   sensitiveInt,   &ContextSensitiveInt::PositionCallback ), sensitiveInt, intContext );

        // NEW added by Marcos: we now have text windows
        Reveal::TextWindow<32>* pWindow = gTweaker.AddTextWindow<32>( "new features", "text window" );
        pWindow->AddLine("Hi!");
        pWindow->AddLine("How do you like our new text window?");
        pWindow->AddLine("New lines can be added with a simple AddLine()");
        pWindow->AddLine("You can choose how many lines the buffer stores using a");
        pWindow->AddLine("template param. And it uses a circular buffer, so if the");
        pWindow->AddLine("buffer runs out, the oldest lines are overwritten");
        pWindow->AddLine("And of course, the window is resiable to view more/less text");
        pWindow->AddLine("Lastly, you can provide a callback Update() function,");
        pWindow->AddLine("where you can update the content of the window.");

        //NEW added by Marcos: help for menus + menu items
        gTweaker.AddHelp("new features", "You can now add help to menus "
            "and menu items with a simple call "
            "to AddHelp()");
        gTweaker.AddHelp("new features/text window", "Here you would describe all "
            "the cool stuff that will happen "
            "when you click this item!");

        // Create the two different input states;
        Reveal::RandomInputStateImpl soakInputState;
        Reveal::Win32PcInputStateImpl inputState( renderer.GetHwnd() );

        // Make sure the tweaker is visible;
        gTweaker.SetEnable( true );

        // -----------------------------------------------------------------------------------------------------------------
        // Main loop;
        // -----------------------------------------------------------------------------------------------------------------
        while ( ! GetAsyncKeyState( 'Q' ) && soakDuration )
        {	
            MSG message;
            if ( PeekMessage( &message, renderer.GetHwnd(), 0, 0, true ) ) DispatchMessage( &message );

            if ( soak )
            {
                soakInputState.Update();
                gTweaker.Handle( soakInputState );
            }
            else
            {
                inputState.Update();
                gTweaker.Handle( inputState );
            }

            gTweaker.Render( renderer );
            renderer.FlipAndClear();

            if ( soak )
            {
                --soakDuration;

                // Enable or disable the tweaker menu;
                if ( (rand() & 127) == 127 ) gTweaker.SetEnable( false );
                else if ( gTweaker.Enabled() == false )  if ( (rand() & 15) == 15 ) gTweaker.SetEnable( true );

                // Add and remove random stuff from the option control;
                if ( emptyOptionControl )
                {
                    if ( (rand() & 255) == 255 ) emptyOptionControl->RemoveOptions( gLargeEnumDesc, gLargeEnumDesc + (rand() % gLargeEnumDescSize) );
                    if ( (rand() & 255) == 255 ) emptyOptionControl->AddOptions( gLargeEnumDesc, gLargeEnumDesc + (rand() % gLargeEnumDescSize) );
                }

                // Add and remove random controls and menus;
                const int removeControlOrMenu = rand() % 8;
                if ( removeControlOrMenu == 0 ) RemoveControl();
                if ( removeControlOrMenu == 1 ) RemoveEntireMenu();
                if ( removeControlOrMenu == 2 ) AddControl();

                if ( GetAsyncKeyState( 'O' ) ) soak = false;
            }
            else
            {
                if ( GetAsyncKeyState( 'P' ) ) soak = true;
            }

            // Update the picking container;
            contextContainer.Handle( inputState, renderer );
            contextContainer.Render( renderer );
            sensitiveFloat.Render( renderer );
            sensitiveInt.Render( renderer );

            // Update the two graphs with some funky values;
#if defined _MSC_VER
            static float f; f += 0.1f;				
            if ( graph0 ) graph0->AddValue( (sinf( f ) * 16.0f + cosf( f * 7.0f )) * 10.0f );
            if ( graph1 ) graph1->AddValue( static_cast< int >( sinf( f * 3.0f ) * 10.0f ) );

            gWatchFloat = (sinf( f ) * 16.0f + cosf( f * 7.0f )) * 10.0f;
            gWatchInt = static_cast< int >( sinf( f * 3.0f ) * 10.0f );
#endif
        }
    }
    catch ( TestFailure& e )
    {
        std::cout << "[test] Unit Test Failure in Reveal Test\n";
        std::cout << "[test] Details: " << e.What() << '\n';
        return EXIT_FAILURE;
    }
    catch ( GdiRenderer::UnableToCreateWindow& )
    {
        std::cout << "[test] Reveal Test could not create window\n";
        return EXIT_FAILURE;
    }
    catch ( GdiRenderer::UnableToInitGdiplus& )
    {
        std::cout << "[test] Reveal Test could not initialize GDI plus\n";
        return EXIT_FAILURE;
    }
    catch ( ... )
    {
        std::cout << "[test] Unknown failure in Reveal Test\n";
        return EXIT_FAILURE;
    }

    std::cout << "[test] Reveal Test succeeded\n\n";

    return EXIT_SUCCESS;
}
