//------------------------------------------------------------------------------
#include "PropData.h"
#include "SpinEditorDouble.h"
#include "spinctld.h"
#include <limits>
//------------------------------------------------------------------------------

using namespace std;
using namespace mvIMPACT::acquire;

//------------------------------------------------------------------------------
wxPGCustomSpinCtrlEditor* wxPGCustomSpinCtrlEditor::m_pInstance = 0;
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
wxPGCustomSpinCtrlEditor* wxPGCustomSpinCtrlEditor::Instance()
//------------------------------------------------------------------------------
{
	if( m_pInstance ) return m_pInstance;

	m_pInstance = new wxPGCustomSpinCtrlEditor();
	wxPropertyGrid::RegisterEditorClass( m_pInstance, wxEmptyString );
	return m_pInstance;
}

//------------------------------------------------------------------------------
/// Create controls and initialize event handling.
#ifndef __WXPYTHON__
	wxWindow* wxPGCustomSpinCtrlEditor::CreateControls ( wxPropertyGrid* propgrid, wxPGProperty* pProperty, const wxPoint& pos, const wxSize& sz, wxWindow** ) const
#else
# if wxPROPGRID_MINOR > 2
	wxPGWindowList wxPGCustomSpinCtrlEditor::CreateControls ( wxPropertyGrid* propgrid, wxPGProperty* pProperty, const wxPoint& pos, const wxSize& sz ) const
# else
	wxPGWindowPair wxPGCustomSpinCtrlEditor::CreateControls ( wxPropertyGrid* propgrid, wxPGProperty* pProperty, const wxPoint& pos, const wxSize& sz ) const
# endif
#endif
//------------------------------------------------------------------------------
{
	// Get initial value (may be none if value is 'unspecified')
	const wxString text = pProperty->IsValueUnspecified() ? wxGetEmptyString() : pProperty->GetValueAsString(0);

	// Determine minimum and maximum
	double min = numeric_limits<double>::min();
	double max = numeric_limits<double>::max();
	double step = 1.0;

#if wxPROPGRID_MINOR > 2
	PropertyObject* const pPropData = reinterpret_cast<PropertyObject*>(pProperty->GetClientData());
#else
	PropertyObject* const pPropData = static_cast<PropertyObject*>(pProperty->GetClientData());
#endif
	wxString format = wxEmptyString;
	TMode mode = mDouble;
	if( pPropData )
	{
		const Component comp = pPropData->Component();
		switch( comp.type() )
		{
		case ctPropInt:
			{
				PropertyI prop( comp );
				format = ConvertedString( prop.stringFormatString() );
				min = prop.isConstDefined( plMinValue ) ? prop.read( plMinValue ) : numeric_limits<int>::min();
				max = prop.isConstDefined( plMaxValue ) ? prop.read( plMaxValue ) : numeric_limits<int>::max();
				step = prop.isConstDefined( plStepWidth ) ? prop.read( plStepWidth ) : 1.0;
				mode = mInt;
			}
			break;
		case ctPropInt64:
			{
				PropertyI64 prop( comp );
				format = ConvertedString( prop.stringFormatString() );
				min = prop.isConstDefined( plMinValue ) ? prop.read( plMinValue ) : numeric_limits<int64_type>::min();
				max = prop.isConstDefined( plMaxValue ) ? prop.read( plMaxValue ) : numeric_limits<int64_type>::max();
				step = prop.isConstDefined( plStepWidth ) ? prop.read( plStepWidth ) : 1.0;
				mode = mInt64;
			}
			break;
		case ctPropFloat:
			{
				PropertyF prop( comp );
				format = ConvertedString( prop.stringFormatString() );
				min = prop.isConstDefined( plMinValue ) ? prop.read( plMinValue ) : numeric_limits<double>::min();
				max = prop.isConstDefined( plMaxValue ) ? prop.read( plMaxValue ) : numeric_limits<double>::max();
				step = prop.isConstDefined( plStepWidth ) ? prop.read( plStepWidth ) : 1.0;
			}
			break;
		default:
			{
				wxASSERT( !"unrecognized type for this editor class" );
				exit(0);
			}
		}
	}

	wxString valStr = propgrid->GetPropertyValueAsString(pProperty);
	double value = ToDouble( mode, valStr, format );

	// Use two stage creation to allow cleaner display on wxMSW
	wxSpinCtrlDbl* pEditor = new wxSpinCtrlDbl();
	pEditor->SetMode( mode );
#ifdef __WXMSW__
	pEditor->Hide();
#endif

	wxSize size(sz);
	/// \todo find out why 'sz' is too small...
	size.SetHeight( sz.GetHeight() + 6 );
	if( !pEditor->Create( propgrid, wxPG_SUBID1, text, pos, size,wxSP_ARROW_KEYS, min, max, value, step, 0xff, format.IsEmpty() ? wxString() : format ) )
	{
		wxASSERT( !"Failed wxControl::Create" );
	}
	// Connect all required events to grid's OnCustomEditorEvent
	// (all relevenat wxTextCtrl, wxComboBox and wxButton events are
	// already connected)
	propgrid->Connect( wxPG_SUBID1, wxEVT_COMMAND_SPINCTRL_UPDATED, (wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction)&wxPropertyGrid::OnCustomEditorEvent );

	// This centers the control in a platform dependent manner
	propgrid->FixPosForTextCtrl( pEditor );

#ifdef __WXMSW__
	pEditor->Show();
#endif

	return pEditor;
}

//------------------------------------------------------------------------------
/// Copies value from property to control
void wxPGCustomSpinCtrlEditor::UpdateControl( wxPGProperty* pProperty, wxWindow* wnd ) const
//------------------------------------------------------------------------------
{
	wxSpinCtrlDbl* pEditor = (wxSpinCtrlDbl*) wnd;
	wxASSERT( pEditor && pEditor->IsKindOf(CLASSINFO(wxSpinCtrlDbl)) );

	wxString valS(pProperty->GetValueAsString());
	double value = ToDouble( pEditor->GetMode(), valS, pEditor->GetFormat() );
	pEditor->SetValue( value );
}

//------------------------------------------------------------------------------
/// Control's events are redirected here
bool wxPGCustomSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* pProperty, wxWindow* wnd, wxEvent& e ) const
//------------------------------------------------------------------------------
{
	if( e.GetEventType() == wxEVT_COMMAND_SPINCTRL_UPDATED )
	{
		if( CopyValueFromControl( pProperty, wnd ) )
		{
			return true;
		}
		propgrid->EditorsValueWasNotModified();
	}
	else
	{
		return wxPGTextCtrlEditor::OnEvent( propgrid, pProperty, wnd, e );
	}
	return false;
}

//------------------------------------------------------------------------------
/// Copies value from control to property
bool wxPGCustomSpinCtrlEditor::CopyValueFromControl( wxPGProperty* pProperty, wxWindow* wnd ) const
//------------------------------------------------------------------------------
{
	wxSpinCtrlDbl* pEditor = (wxSpinCtrlDbl*) wnd;
	wxASSERT( pEditor && pEditor->IsKindOf(CLASSINFO(wxSpinCtrlDbl)) );
	double value = pEditor->GetValue();
	TMode mode = pEditor->GetMode();
	if( mode == mInt )
	{
		pProperty->SetValueFromString( wxString::Format( pEditor->GetFormat(), static_cast<int>(value) ), wxPG_FULL_VALUE );
	}
	else if( mode == mInt64 )
	{
		pProperty->SetValueFromString( wxString::Format( pEditor->GetFormat(), static_cast<wxLongLong_t>(value) ), wxPG_FULL_VALUE );
	}
	else
	{
		pProperty->SetValueFromString( wxString::Format( pEditor->GetFormat(), value ), wxPG_FULL_VALUE );
	}
	return true;
}

//------------------------------------------------------------------------------
/// Makes control look like it has unspecified value
void wxPGCustomSpinCtrlEditor::SetValueToUnspecified ( wxWindow* wnd ) const
//------------------------------------------------------------------------------
{
	wxSpinCtrlDbl* pEditor = (wxSpinCtrlDbl*) wnd;
	wxASSERT( pEditor && pEditor->IsKindOf(CLASSINFO(wxSpinCtrlDbl)) );
	pEditor->SetValue(wxEmptyString, false);
}

//------------------------------------------------------------------------------
/// Used when control's value is wanted to set from string source
/// (obviously, not all controls can implement this properly,
///  but wxSpinCtrl can)
void wxPGCustomSpinCtrlEditor::SetControlStringValue ( wxWindow* wnd, const wxString& txt ) const
//------------------------------------------------------------------------------
{
	wxSpinCtrlDbl* pEditor = (wxSpinCtrlDbl*) wnd;
	wxASSERT( pEditor && pEditor->IsKindOf(CLASSINFO(wxSpinCtrlDbl)) );
	pEditor->SetValue(txt, false);
}
