#include <SFGUI/SFGUI.hpp>
#include <SFGUI/Widgets.hpp>
#include <SFGUI/Renderers.hpp>

#include <SFML/Graphics.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/OpenGL.hpp>
#include <sstream>
#include <cmath>
#include <memory>

class SampleApp {
	public:
		SampleApp();
		~SampleApp();
		void Run();

	private:
		void OnAddButtonHClick();
		void OnAddButtonVClick();
		void OnToggleTitlebarClick();
		void OnHideWindowClicked();
		void OnToggleOrientationClick();
		void OnToggleSpaceClick();
		void OnLimitCharsToggle();
		void OnLoadThemeClick();
		void OnAdjustmentChange();
		void OnToggleSpinner();
		void OnMirrorImageClick();
		void OnSwitchRendererClick();
		void RenderCustomGL();
		void RenderCustomSFML();

		sf::RenderWindow m_window;

		// Create an SFGUI. This is required before doing anything with SFGUI.
		sfg::SFGUI m_sfgui;

		sfg::Window::Ptr m_wndmain;
		sfg::Box::Ptr m_boxbuttonsh;
		sfg::Box::Ptr m_boxbuttonsv;
		sfg::Box::Ptr m_boxorientation;
		sfg::Entry::Ptr m_entry;
		sfg::Table::Ptr m_table;
		sfg::ScrolledWindow::Ptr m_scrolled_window;
		sfg::Box::Ptr m_scrolled_window_box;
		sfg::ToggleButton::Ptr m_titlebar_toggle;
		sfg::CheckButton::Ptr m_limit_check;
		sfg::Scale::Ptr m_scale;
		sfg::ComboBox::Ptr m_combo_box;
		sfg::ProgressBar::Ptr m_progress;
		sfg::ProgressBar::Ptr m_progress_vert;
		sfg::Spinner::Ptr m_spinner;
		sfg::Image::Ptr m_image;
		sfg::Canvas::Ptr m_gl_canvas;
		//sfg::Canvas::Ptr m_sfml_canvas;
		sfg::Button::Ptr m_switch_renderer;

		sfg::Desktop m_desktop;

		unsigned int m_fps_counter;
		sf::Clock m_fps_clock;

		sf::Texture m_background_texture;
		sf::Sprite m_background_sprite;
		sf::Sprite m_canvas_sprite;

		GLuint m_custom_draw_display_list;
};

class Ouchy : public std::enable_shared_from_this<Ouchy> {
	public:
		typedef std::shared_ptr<Ouchy> Ptr; //!< Shared pointer.
		Ouchy( sfg::Button::Ptr button );

		void DoOuch();

		static std::vector<Ptr> m_ouchies;
	private:
		sfg::Button::Ptr m_button;
		bool m_state;
};

std::vector<Ouchy::Ptr> Ouchy::m_ouchies;

Ouchy::Ouchy( sfg::Button::Ptr button ) :
	m_button( button ),
	m_state( false )
{
}

void Ouchy::DoOuch() {
	if( !m_state ) {
		m_button->SetLabel( "Ouch" );
	}
	else {
		m_button->SetLabel( "Boom" );
	}

	m_state = !m_state;
}

SampleApp::SampleApp() :
	m_window( sf::VideoMode( 1024, 768, 32 ), "SFGUI test", sf::Style::Default, sf::ContextSettings( 16, 0, 0, 2, 1 ) ),
	m_desktop(),
	m_custom_draw_display_list( 0 )
{
	m_background_texture.create( 1024, 768 );

	std::vector<sf::Uint8> pixels( 1024 * 768 * 4 );

	sf::Uint8 pixel_value = 139;

	for( std::size_t index = 0; index < 1024 * 768; ++index ) {
		pixel_value = static_cast<sf::Uint8>( pixel_value ^ ( index + 809 ) );
		pixel_value = static_cast<sf::Uint8>( pixel_value << ( index % 11 ) );
		pixel_value = static_cast<sf::Uint8>( pixel_value * 233 );

		pixels[ index * 4 + 0 ] = static_cast<sf::Uint8>( pixel_value % 16 + 72 ); // R

		pixel_value ^= static_cast<sf::Uint8>( index );
		pixel_value = static_cast<sf::Uint8>( pixel_value * 23 );

		pixels[ index * 4 + 1 ] = static_cast<sf::Uint8>( pixel_value % 16 + 72 ); // G

		pixel_value ^= static_cast<sf::Uint8>( index );
		pixel_value = static_cast<sf::Uint8>( pixel_value * 193 );

		pixels[ index * 4 + 2 ] = static_cast<sf::Uint8>( pixel_value % 16 + 72 ); // B

		pixels[ index * 4 + 3 ] = 255; // A
	}

	m_background_texture.update( pixels.data() );

	m_background_sprite.setTexture( m_background_texture );
}

SampleApp::~SampleApp() {
	if( m_custom_draw_display_list ) {
		glDeleteLists( m_custom_draw_display_list, 1 );
	}

	Ouchy::m_ouchies.clear();
}

void SampleApp::Run() {
	sf::Event event;

	//m_window.SetFramerateLimit( 60 );
	//m_window.EnableVerticalSync( true );
	m_window.setActive( true );

	std::string renderer_string;

	// Tune Renderer
	if( m_sfgui.GetRenderer().GetName() == "Non-Legacy Renderer" ) {
		static_cast<sfg::NonLegacyRenderer*>( &m_sfgui.GetRenderer() )->TuneUseFBO( true );
		static_cast<sfg::NonLegacyRenderer*>( &m_sfgui.GetRenderer() )->TuneCull( true );

		renderer_string = "NLR";
	}
	if( m_sfgui.GetRenderer().GetName() == "Vertex Buffer Renderer" ) {
		static_cast<sfg::VertexBufferRenderer*>( &m_sfgui.GetRenderer() )->TuneUseFBO( true );
		static_cast<sfg::VertexBufferRenderer*>( &m_sfgui.GetRenderer() )->TuneAlphaThreshold( .2f );
		static_cast<sfg::VertexBufferRenderer*>( &m_sfgui.GetRenderer() )->TuneCull( true );

		renderer_string = "VBR";
	}
	else if( m_sfgui.GetRenderer().GetName() == "Vertex Array Renderer" ) {
		static_cast<sfg::VertexArrayRenderer*>( &m_sfgui.GetRenderer() )->TuneAlphaThreshold( .2f );
		static_cast<sfg::VertexArrayRenderer*>( &m_sfgui.GetRenderer() )->TuneCull( true );

		renderer_string = "VAR";
	}

	// Play around with resource manager.
	std::shared_ptr<sf::Font> my_font = std::make_shared<sf::Font>();
	my_font->loadFromFile( "data/linden_hill.otf" );
	m_desktop.GetEngine().GetResourceManager().AddFont( "custom_font", my_font );

	// Set properties.
	// Multiple properties can be set at once to save calls.

	// m_desktop.SetProperty( "Button#close:Normal", "Color", sf::Color::Yellow );
	// #close is sufficient since there is only 1 widget with this id
	// m_desktop.SetProperty( "#close", "FontName", "data/linden_hill.otf" );
	// m_desktop.SetProperty( "#close", "FontSize", 15.f );

	// We will batch the above properties into this call.
	m_desktop.SetProperties(
		"Window#second_window > Box > Label {"
		"	FontName: custom_font;"
		"	FontSize: 18;"
		"}"
		"Button#close:Normal {"
		"	Color: #FFFF00FF;"
		"}"
		"#close {"
		"	FontName: data/linden_hill.otf;"
		"	FontSize: 15;"
		"}"
	);

	// Create widgets.
	m_wndmain = sfg::Window::Create( sfg::Window::Style::TITLEBAR | sfg::Window::Style::BACKGROUND | sfg::Window::Style::RESIZE | sfg::Window::Style::CLOSE );
	m_wndmain->SetTitle( L"Example application" );

	auto btnaddbuttonh = sfg::Button::Create( L"Add button horizontally" );
	auto btnaddbuttonv = sfg::Button::Create( L"Add button vertically" );
	m_titlebar_toggle = sfg::ToggleButton::Create( "Toggle titlebar" );
	m_titlebar_toggle->SetActive( true );

	{
		sf::Image add_image;
		if( add_image.loadFromFile( "data/add.png" ) ) {
			auto image = sfg::Image::Create( add_image );
			btnaddbuttonh->SetImage( image );

			image = sfg::Image::Create( add_image );
			btnaddbuttonv->SetImage( image );
		}
	}

	auto btnhidewindow = sfg::Button::Create( L"Close window" );
	btnhidewindow->SetId( "close" );

	{
		sf::Image close_image;
		if( close_image.loadFromFile( "data/delete.png" ) ) {
			auto image = sfg::Image::Create( close_image );
			btnhidewindow->SetImage( image );
		}
	}

	auto btntoggleori = sfg::Button::Create( L"Box Orientation" );
	auto btntogglespace = sfg::Button::Create( L"Box Spacing" );
	auto btnloadstyle = sfg::Button::Create( L"Load theme" );

	m_entry = sfg::Entry::Create( L"Type" );
	m_entry->SetRequisition( sf::Vector2f( 100.f, .0f ) );
	m_entry->AppendText( L" something!" );

	m_limit_check = sfg::CheckButton::Create( L"Limit to 4 chars" );
	m_limit_check->SetId( "limit_check" );

	auto password = sfg::Entry::Create();
	password->HideText( '*' );

	// Layout.
	auto boxtoolbar = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL );
	boxtoolbar->SetSpacing( 5.f );
	boxtoolbar->Pack( btnaddbuttonh, false );
	boxtoolbar->Pack( btnaddbuttonv, false );
	boxtoolbar->Pack( m_titlebar_toggle, false );
	boxtoolbar->Pack( btnhidewindow, false );
	boxtoolbar->Pack( m_entry, true );
	boxtoolbar->Pack( m_limit_check, false );

	auto frame1 = sfg::Frame::Create( L"Toolbar 1" );
	frame1->Add( boxtoolbar );

	auto boxtoolbar2 = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL );
	boxtoolbar2->SetSpacing( 5.f );
	boxtoolbar2->Pack( btntoggleori, false );
	boxtoolbar2->Pack( btntogglespace, false );
	boxtoolbar2->Pack( btnloadstyle, false );

	m_boxbuttonsh = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL );
	m_boxbuttonsh->SetSpacing( 5.f );

	m_boxbuttonsv = sfg::Box::Create( sfg::Box::Orientation::VERTICAL );
	m_boxbuttonsv->SetSpacing( 5.f );

	m_boxorientation = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL );
	m_boxorientation->SetSpacing( 5.f );
	m_boxorientation->Pack( sfg::Button::Create( L"Hello" ) );
	m_boxorientation->Pack( sfg::Button::Create( L"World" ) );

	auto username_entry = sfg::Entry::Create();
	username_entry->SetMaximumLength( 8 );

	m_progress = sfg::ProgressBar::Create( sfg::ProgressBar::Orientation::HORIZONTAL );
	m_progress->SetRequisition( sf::Vector2f( 0.f, 20.f ) );

	m_progress_vert = sfg::ProgressBar::Create( sfg::ProgressBar::Orientation::VERTICAL );
	m_progress_vert->SetRequisition( sf::Vector2f( 20.f, 0.f ) );

	auto separatorv = sfg::Separator::Create( sfg::Separator::Orientation::VERTICAL );

	m_table = sfg::Table::Create();
	m_table->Attach( sfg::Label::Create( L"Please login using your username and password (span example)." ), sf::Rect<sf::Uint32>( 0, 0, 2, 1 ), sfg::Table::FILL, sfg::Table::FILL | sfg::Table::EXPAND );
	m_table->Attach( sfg::Label::Create( L"Username:" ), sf::Rect<sf::Uint32>( 0, 1, 1, 1 ), sfg::Table::FILL, sfg::Table::FILL );
	m_table->Attach( username_entry, sf::Rect<sf::Uint32>( 1, 1, 1, 1 ), sfg::Table::EXPAND | sfg::Table::FILL, sfg::Table::FILL );
	m_table->Attach( sfg::Label::Create( L"Password:" ), sf::Rect<sf::Uint32>( 0, 2, 1, 1 ), sfg::Table::FILL, sfg::Table::FILL );
	m_table->Attach( password, sf::Rect<sf::Uint32>( 1, 2, 1, 1 ), sfg::Table::FILL, sfg::Table::FILL );
	m_table->Attach( sfg::Button::Create( L"Login" ), sf::Rect<sf::Uint32>( 2, 1, 1, 2 ), sfg::Table::FILL, sfg::Table::FILL );
	m_table->Attach( separatorv, sf::Rect<sf::Uint32>( 3, 0, 1, 3 ), sfg::Table::FILL, sfg::Table::FILL );
	m_table->Attach( m_progress_vert, sf::Rect<sf::Uint32>( 4, 0, 1, 3 ), sfg::Table::FILL, sfg::Table::FILL );
	m_table->SetRowSpacings( 5.f );
	m_table->SetColumnSpacings( 5.f );

	m_scrolled_window_box = sfg::Box::Create( sfg::Box::Orientation::VERTICAL );

	for( int i = 0; i < 5; i++ ) {
		auto box = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL );

		for( int j = 0; j < 20; j++ ) {
			box->Pack( sfg::Button::Create( L"One button among many" ), true );
		}

		m_scrolled_window_box->Pack( box, false );
	}

	m_scrolled_window = sfg::ScrolledWindow::Create();
	m_scrolled_window->SetRequisition( sf::Vector2f( .0f, 160.f ) );
	m_scrolled_window->SetScrollbarPolicy( sfg::ScrolledWindow::HORIZONTAL_AUTOMATIC | sfg::ScrolledWindow::VERTICAL_AUTOMATIC );
	m_scrolled_window->SetPlacement( sfg::ScrolledWindow::Placement::TOP_LEFT );
	m_scrolled_window->AddWithViewport( m_scrolled_window_box );

	auto scrollbar = sfg::Scrollbar::Create();
	scrollbar->SetRange( .0f, 100.f );

	m_scale = sfg::Scale::Create();
	m_scale->SetAdjustment( scrollbar->GetAdjustment() );
	m_scale->SetRequisition( sf::Vector2f( 100.f, .0f ) );
	boxtoolbar2->Pack( m_scale, false );

	m_combo_box = sfg::ComboBox::Create();

	for( int index = 0; index < 30; ++index ) {
		std::stringstream sstr;

		sstr << "Item " << index;

		m_combo_box->AppendItem( sstr.str() );
	}

	boxtoolbar2->Pack( m_combo_box, true );

	m_switch_renderer = sfg::Button::Create( "Renderer: " + renderer_string );

	boxtoolbar2->Pack( m_switch_renderer, false );

	auto frame2 = sfg::Frame::Create( L"Toolbar 2" );
	frame2->Add( boxtoolbar2 );
	frame2->SetAlignment( sf::Vector2f( .8f, .0f ) );

	auto separatorh = sfg::Separator::Create( sfg::Separator::Orientation::HORIZONTAL );

	auto box_image = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL );
	box_image->SetSpacing( 15.f );

	auto fixed_container = sfg::Fixed::Create();
	auto fixed_button = sfg::Button::Create( L"I'm at (34,61)" );
	fixed_container->Put( fixed_button, sf::Vector2f( 34.f, 61.f ) );
	box_image->Pack( fixed_container, false );

	sf::Image sfgui_logo;
	m_image = sfg::Image::Create();

	if( sfgui_logo.loadFromFile( "data/sfgui.png" ) ) {
		m_image->SetImage( sfgui_logo );
		box_image->Pack( m_image, false );
	}

	auto mirror_image = sfg::Button::Create( L"Mirror Image" );

	box_image->Pack( mirror_image, false );

	auto spinner_box = sfg::Box::Create( sfg::Box::Orientation::VERTICAL );

	m_spinner = sfg::Spinner::Create();
	m_spinner->SetRequisition( sf::Vector2f( 40.f, 40.f ) );
	m_spinner->Start();
	auto spinner_toggle = sfg::ToggleButton::Create( L"Spin" );
	spinner_toggle->SetActive( true );
	spinner_box->SetSpacing( 5.f );
	spinner_box->Pack( m_spinner, false );
	spinner_box->Pack( spinner_toggle, false );

	box_image->Pack( spinner_box, false );

	auto radio_box = sfg::Box::Create( sfg::Box::Orientation::VERTICAL );

	auto radio1 = sfg::RadioButton::Create( "Radio 1" );
	auto radio2 = sfg::RadioButton::Create( "Radio 2", radio1->GetGroup() );
	auto radio3 = sfg::RadioButton::Create( "Radio 3", radio2->GetGroup() );

	radio_box->Pack( radio1 );
	radio_box->Pack( radio2 );
	radio_box->Pack( radio3 );

	box_image->Pack( radio_box, false );

	auto spinbutton = sfg::SpinButton::Create( scrollbar->GetAdjustment() );
	spinbutton->SetRequisition( sf::Vector2f( 80.f, 0.f ) );
	spinbutton->SetDigits( 3 );

	auto spinbutton_box = sfg::Box::Create( sfg::Box::Orientation::VERTICAL );
	spinbutton_box->Pack( spinbutton, false, false );

	box_image->Pack( spinbutton_box, false, false );

	auto aligned_combo_box = sfg::ComboBox::Create();
	aligned_combo_box->AppendItem( L"I'm way over here" );
	aligned_combo_box->AppendItem( L"Me too" );
	aligned_combo_box->AppendItem( L"Me three" );
	aligned_combo_box->SelectItem( 0 );

	auto alignment = sfg::Alignment::Create();
	alignment->Add( aligned_combo_box );
	box_image->Pack( alignment, true );
	alignment->SetAlignment( sf::Vector2f( 1.f, .5f ) );
	alignment->SetScale( sf::Vector2f( 0.f, .01f ) );

	auto boxmain = sfg::Box::Create( sfg::Box::Orientation::VERTICAL );
	boxmain->SetSpacing( 5.f );
	boxmain->Pack( scrollbar, false );
	boxmain->Pack( m_progress, false );
	boxmain->Pack( frame1, false );
	boxmain->Pack( frame2, false );
	boxmain->Pack( m_boxbuttonsh, false );
	boxmain->Pack( m_boxbuttonsv, false );
	boxmain->Pack( box_image, true );
	boxmain->Pack( separatorh, false );
	boxmain->Pack( m_table, true );
	boxmain->Pack( m_boxorientation, true );
	boxmain->Pack( m_scrolled_window );

	auto notebook1 = sfg::Notebook::Create();
	auto notebook2 = sfg::Notebook::Create();
	auto notebook3 = sfg::Notebook::Create();
	auto notebook4 = sfg::Notebook::Create();

	notebook1->SetTabPosition( sfg::Notebook::TabPosition::TOP );
	notebook2->SetTabPosition( sfg::Notebook::TabPosition::RIGHT );
	notebook3->SetTabPosition( sfg::Notebook::TabPosition::BOTTOM );
	notebook4->SetTabPosition( sfg::Notebook::TabPosition::LEFT );

	auto vertigo_box = sfg::Box::Create( sfg::Box::Orientation::HORIZONTAL );
	auto vertigo_button = sfg::Button::Create( L"Vertigo" );
	vertigo_box->Pack( vertigo_button, true, true );

	notebook1->AppendPage( boxmain, sfg::Label::Create( "Page Name Here" ) );
	notebook1->AppendPage( notebook2, sfg::Label::Create( "Another Page" ) );
	notebook2->AppendPage( notebook3, sfg::Label::Create( "Yet Another Page" ) );
	notebook2->AppendPage( sfg::Label::Create( L"" ), sfg::Label::Create( "Dummy Page" ) );
	notebook3->AppendPage( notebook4, sfg::Label::Create( "And Another Page" ) );
	notebook3->AppendPage( sfg::Label::Create( L"" ), sfg::Label::Create( "Dummy Page" ) );
	notebook4->AppendPage( vertigo_box, sfg::Label::Create( "And The Last Page" ) );
	notebook4->AppendPage( sfg::Label::Create( L"" ), sfg::Label::Create( "Dummy Page" ) );

	m_wndmain->Add( notebook1 );

	// Signals.
	m_wndmain->GetSignal( sfg::Window::OnCloseButton ).Connect( [this] { OnHideWindowClicked(); } );
	btnaddbuttonh->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnAddButtonHClick(); } );
	btnaddbuttonv->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnAddButtonVClick(); } );
	m_titlebar_toggle->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnToggleTitlebarClick(); } );
	btnhidewindow->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnHideWindowClicked(); } );
	btntoggleori->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnToggleOrientationClick(); } );
	btntogglespace->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnToggleSpaceClick(); } );
	m_limit_check->GetSignal( sfg::ToggleButton::OnToggle ).Connect( [this] { OnLimitCharsToggle(); } );
	btnloadstyle->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnLoadThemeClick(); } );
	m_scale->GetAdjustment()->GetSignal( sfg::Adjustment::OnChange ).Connect( [this] { OnAdjustmentChange(); } );
	spinner_toggle->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnToggleSpinner(); } );
	mirror_image->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnMirrorImageClick(); } );
	m_switch_renderer->GetSignal( sfg::Widget::OnLeftClick ).Connect( [this] { OnSwitchRendererClick(); } );

	spinbutton->SetValue( 20.f );
	spinbutton->GetAdjustment()->SetMinorStep( .8f );

	m_wndmain->SetPosition( sf::Vector2f( 100.f, 100.f ) );

	// Another window
	auto second_window = sfg::Window::Create( sfg::Window::TITLEBAR | sfg::Window::BACKGROUND | sfg::Window::RESIZE );
	second_window->SetId( "second_window" );
	second_window->SetTitle( "Resize this window to see ad-hoc wrapping." );
	auto box = sfg::Box::Create( sfg::Box::Orientation::VERTICAL, 5.f );

	auto lipsum = sfg::Label::Create(
		"Nullam ut ante leo. Quisque consequat condimentum pulvinar. "
		"Duis a enim sapien, ut vestibulum est. Vestibulum commodo, orci non gravida. "
		"Aliquam sed pretium lacus. "
		"Nullam placerat mauris vel nulla sagittis pellentesque. "
		"Suspendisse in justo dui.\n"
		"Ut dolor massa, gravida eu facilisis convallis, convallis sed odio.\n"
		"Nunc placerat consequat vehicula."
	);

	lipsum->SetRequisition( sf::Vector2f( 400.f, 0.f ) );
	lipsum->SetLineWrap( true );

	box->Pack( lipsum );
	second_window->Add( box );
	second_window->SetPosition( sf::Vector2f( 10.f, 10.f ) );
	second_window->SetId( "second_window" );
	m_desktop.Add( second_window );

	auto third_window = sfg::Window::Create( sfg::Window::TITLEBAR | sfg::Window::BACKGROUND | sfg::Window::RESIZE );

	m_gl_canvas = sfg::Canvas::Create( true );
	m_gl_canvas->SetRequisition( sf::Vector2f( 200.f, 150.f ) );

	third_window->Add( m_gl_canvas );

	third_window->SetId( "third_window" );
	third_window->SetTitle( "Embedded OpenGL drawing" );
	third_window->SetPosition( sf::Vector2f( 480.f, 20.f ) );
	m_desktop.Add( third_window );

	sf::Texture texture;
	texture.loadFromImage( sfgui_logo );
	m_canvas_sprite.setTexture( texture );

	//auto fourth_window = sfg::Window::Create( sfg::Window::TITLEBAR | sfg::Window::BACKGROUND | sfg::Window::RESIZE );

	//m_sfml_canvas = sfg::Canvas::Create();
	//m_sfml_canvas->SetRequisition( sf::Vector2f( static_cast<float>( texture.getSize().x ), static_cast<float>( texture.getSize().y ) ) );

	//fourth_window->Add( m_sfml_canvas );

	//fourth_window->SetId( "fourth_window" );
	//fourth_window->SetTitle( "Embedded SFML drawing" );
	//fourth_window->SetPosition( sf::Vector2f( 760.f, 20.f ) );
	//m_desktop.Add( fourth_window );

	// Add window to desktop
	m_desktop.Add( m_wndmain );

	m_fps_counter = 0;
	m_fps_clock.restart();

	sf::Clock clock;
	sf::Clock frame_time_clock;

	sf::Int64 frame_times[5000];
	std::size_t frame_times_index = 0;

	std::fill( std::begin( frame_times ), std::end( frame_times ), 0 );

	m_desktop.Update( 0.f );

	while( m_window.isOpen() ) {
		while( m_window.pollEvent( event ) ) {
			if( event.type == sf::Event::Closed ) {
				return;
			}

			m_desktop.HandleEvent( event );
		}

		m_window.draw( m_background_sprite );

		auto microseconds = clock.getElapsedTime().asMicroseconds();

		// Only update every 5ms
		if( microseconds > 5000 ) {
			m_desktop.Update( static_cast<float>( microseconds ) / 1000000.f );
			clock.restart();

			// Only refresh canvas contents every 5ms too
			m_gl_canvas->Bind();
			m_gl_canvas->Clear( sf::Color( 0, 0, 0, 0 ), true );
			RenderCustomGL();
			m_gl_canvas->Display();
			m_gl_canvas->Unbind();

			//m_sfml_canvas->Bind();
			//m_sfml_canvas->Clear( sf::Color( 0, 0, 0, 0 ) );
			//RenderCustomSFML();
			//m_sfml_canvas->Display();
			//m_sfml_canvas->Unbind();

			m_window.setActive( true );
		}

		m_sfgui.Display( m_window );

		m_window.display();

		auto frame_time = frame_time_clock.getElapsedTime().asMicroseconds();
		frame_time_clock.restart();

		frame_times[ frame_times_index ] = frame_time;
		frame_times_index = ( frame_times_index + 1 ) % 5000;

		if( m_fps_clock.getElapsedTime().asMicroseconds() >= 1000000 ) {
			m_fps_clock.restart();

			sf::Int64 total_time = 0;

			for( std::size_t index = 0; index < 5000; ++index ) {
				total_time += frame_times[index];
			}

			std::stringstream sstr;
			sstr << "SFGUI test -- FPS: " << m_fps_counter << " -- Frame Time (microsecs): min: "
			<< *std::min_element( frame_times, frame_times + 5000 ) << " max: "
			<< *std::max_element( frame_times, frame_times + 5000 ) << " avg: "
			<< static_cast<float>( total_time ) / 5000.f;

			m_window.setTitle( sstr.str() );

			m_fps_counter = 0;
		}

		++m_fps_counter;
	}
}

void SampleApp::OnAddButtonHClick() {
	auto button = sfg::Button::Create( L"New ->" );

	auto ouchy = std::make_shared<Ouchy>( button );
	Ouchy::m_ouchies.push_back( ouchy );

	std::weak_ptr<Ouchy> weak_ouchy = ouchy;

	button->GetSignal( sfg::Widget::OnLeftClick ).Connect( [weak_ouchy] {
		auto strong_ouchy = weak_ouchy.lock();

		if( strong_ouchy ) {
			strong_ouchy->DoOuch();
		}
	} );

	m_boxbuttonsh->Pack( button, true );
}

void SampleApp::OnAddButtonVClick() {
	auto button = sfg::Button::Create( L"<- New" );

	auto ouchy = std::make_shared<Ouchy>( button );
	Ouchy::m_ouchies.push_back( ouchy );

	std::weak_ptr<Ouchy> weak_ouchy = ouchy;

	button->GetSignal( sfg::Widget::OnLeftClick ).Connect( [weak_ouchy] {
		auto strong_ouchy = weak_ouchy.lock();

		if( strong_ouchy ) {
			strong_ouchy->DoOuch();
		}
	} );

	m_boxbuttonsv->Pack( button, false );
}

void SampleApp::OnToggleTitlebarClick() {
	m_wndmain->SetStyle( m_wndmain->GetStyle() ^ sfg::Window::TITLEBAR );
}

void SampleApp::OnHideWindowClicked() {
	m_wndmain->Show( !m_wndmain->IsLocallyVisible() );
}

void SampleApp::OnToggleOrientationClick() {
	if( m_boxorientation->GetOrientation() == sfg::Box::Orientation::HORIZONTAL ) {
		m_boxorientation->SetOrientation( sfg::Box::Orientation::VERTICAL );
	}
	else {
		m_boxorientation->SetOrientation( sfg::Box::Orientation::HORIZONTAL );
	}
}

void SampleApp::OnToggleSpaceClick() {
	if( m_scrolled_window_box->GetSpacing() > .0f ) {
		m_scrolled_window_box->SetSpacing( .0f );
	}
	else {
		m_scrolled_window_box->SetSpacing( 40.f );
	}
}


void SampleApp::OnLimitCharsToggle() {
	if( m_limit_check->IsActive() ) {
		m_entry->SetMaximumLength( 4 );
	}
	else {
		m_entry->SetMaximumLength( 0 );
	}
}

void SampleApp::OnLoadThemeClick() {
	m_desktop.LoadThemeFromFile( "data/example.theme" );
}

void SampleApp::OnAdjustmentChange() {
	m_progress->SetFraction( m_scale->GetValue() / 100.f );
	m_progress_vert->SetFraction( m_scale->GetValue() / 100.f );
}

void SampleApp::OnToggleSpinner() {
	if( !m_spinner->Started() ) {
		m_spinner->Start();
	}
	else {
		m_spinner->Stop();
	}
}

void SampleApp::OnMirrorImageClick() {
	auto image = m_image->GetImage();

	for( unsigned int height_index = 0; height_index < image.getSize().y; ++height_index ) {
		for( unsigned int width_index = 0; width_index < image.getSize().x / 2; ++width_index ) {
			auto color0 = image.getPixel( width_index, height_index );
			auto color1 = image.getPixel( image.getSize().x - width_index - 1, height_index );
			image.setPixel( width_index, height_index, color1 );
			image.setPixel( image.getSize().x - width_index - 1, height_index, color0 );
		}
	}

	m_image->SetImage( image );
}

void SampleApp::OnSwitchRendererClick() {
	if( sfg::Renderer::Get().GetName() == "Non-Legacy Renderer" ) {
		auto renderer = sfg::VertexBufferRenderer::Create();

		sfg::Renderer::Set( renderer );

		//renderer->TuneUseFBO( true );
		renderer->TuneAlphaThreshold( .2f );
		renderer->TuneCull( true );

		m_switch_renderer->SetLabel( "Renderer: VBR" );

		m_desktop.Refresh();
		return;
	}

	if( sfg::Renderer::Get().GetName() == "Vertex Buffer Renderer" ) {
		auto renderer = sfg::VertexArrayRenderer::Create();

		sfg::Renderer::Set( renderer );

		renderer->TuneAlphaThreshold( .2f );
		renderer->TuneCull( true );

		m_switch_renderer->SetLabel( "Renderer: VAR" );

		m_desktop.Refresh();
		return;
	}

	if( sfg::NonLegacyRenderer::IsAvailable() ) {
		auto renderer = sfg::NonLegacyRenderer::Create();

		sfg::Renderer::Set( renderer );

		renderer->TuneUseFBO( true );
		renderer->TuneCull( true );

		m_switch_renderer->SetLabel( "Renderer: NLR" );

		m_desktop.Refresh();
	}
	else if( sfg::VertexBufferRenderer::IsAvailable() ) {
		auto renderer = sfg::VertexBufferRenderer::Create();

		sfg::Renderer::Set( renderer );

		//renderer->TuneUseFBO( true );
		renderer->TuneAlphaThreshold( .2f );
		renderer->TuneCull( true );

		m_switch_renderer->SetLabel( "Renderer: VBR" );

		m_desktop.Refresh();
	}
}

void SampleApp::RenderCustomGL() {
	static sf::Clock clock;

	glMatrixMode( GL_MODELVIEW );
	glPushMatrix();
	glLoadIdentity();

	glTranslatef( 0.f, 0.f, -3.f );

	glRotatef( clock.getElapsedTime().asSeconds() * 50.f, 1.f, 0.f, 0.f );
	glRotatef( clock.getElapsedTime().asSeconds() * 30.f, 0.f, 1.f, 0.f );
	glRotatef( clock.getElapsedTime().asSeconds() * 90.f, 0.f, 0.f, 1.f );

	glViewport( 0, 0, static_cast<int>( std::floor( m_gl_canvas->GetAllocation().width + .5f ) ), static_cast<int>( std::floor( m_gl_canvas->GetAllocation().height + .5f ) ) );

	if( !m_custom_draw_display_list ) {
		m_custom_draw_display_list = glGenLists( 1 );

		glNewList( m_custom_draw_display_list, GL_COMPILE );

		glEnable( GL_DEPTH_TEST );
		glDepthMask( GL_TRUE );

		glDisable( GL_TEXTURE_2D );

		glMatrixMode( GL_PROJECTION );
		glPushMatrix();
		glLoadIdentity();

		static const auto pi = 3.1415926535897932384626433832795f;
		static const auto fov = 90.f;
		static const auto near_distance = 1.f;
		static const auto far_distance = 20.f;

		// We set the proper aspect ratio using the dimensions of our Canvas.
		auto aspect = m_gl_canvas->GetAllocation().width / m_gl_canvas->GetAllocation().height;
		auto frustum_height = std::tan( fov / 360 * pi ) * near_distance;
		auto frustum_width = frustum_height * aspect;

		glFrustum( -frustum_width, frustum_width, -frustum_height, frustum_height, near_distance, far_distance );

		glBegin( GL_QUADS );

		glColor3f( 0.f, 0.f, 1.f );
		glVertex3f( -1.f, -1.f, -1.f );
		glVertex3f( -1.f, 1.f, -1.f );
		glVertex3f( 1.f, 1.f, -1.f );
		glVertex3f( 1.f, -1.f, -1.f );

		glColor3f( 0.f, 1.f, 0.f );
		glVertex3f( -1.f, -1.f, 1.f );
		glVertex3f( -1.f, 1.f, 1.f );
		glVertex3f( 1.f, 1.f, 1.f );
		glVertex3f( 1.f, -1.f, 1.f );

		glColor3f( 0.f, 1.f, 1.f );
		glVertex3f( -1.f, -1.f, -1.f );
		glVertex3f( -1.f, 1.f, -1.f );
		glVertex3f( -1.f, 1.f, 1.f );
		glVertex3f( -1.f, -1.f, 1.f );

		glColor3f( 1.f, 0.f, 0.f );
		glVertex3f( 1.f, -1.f, -1.f );
		glVertex3f( 1.f, 1.f, -1.f );
		glVertex3f( 1.f, 1.f, 1.f );
		glVertex3f( 1.f, -1.f, 1.f );

		glColor3f( 1.f, 0.f, 1.f );
		glVertex3f( -1.f, -1.f, 1.f );
		glVertex3f( -1.f, -1.f, -1.f );
		glVertex3f( 1.f, -1.f, -1.f );
		glVertex3f( 1.f, -1.f, 1.f );

		glColor3f( 1.f, 1.f, 0.f );
		glVertex3f( -1.f, 1.f, 1.f );
		glVertex3f( -1.f, 1.f, -1.f );
		glVertex3f( 1.f, 1.f, -1.f );
		glVertex3f( 1.f, 1.f, 1.f );

		glEnd();

		glPopMatrix();

		glEnable( GL_TEXTURE_2D );
		glDisable( GL_DEPTH_TEST );

		glEndList();
	}

	glCallList( m_custom_draw_display_list );

	glMatrixMode( GL_MODELVIEW );
	glPopMatrix();

	glViewport( 0, 0, static_cast<int>( m_window.getSize().x ), static_cast<int>( m_window.getSize().y ) );
}

void SampleApp::RenderCustomSFML() {
	//m_sfml_canvas->Draw( m_canvas_sprite );
}

int main() {
	SampleApp app;
	app.Run();

	return 0;
}
