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

using namespace std;

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

//------------------------------------------------------------------------------
wxPGSpinCtrlEditor* wxPGSpinCtrlEditor::Instance()
//------------------------------------------------------------------------------
{
	if( !m_pInstance )
	{
		m_pInstance = new wxPGSpinCtrlEditor();
		wxPropertyGrid::RegisterEditorClass( m_pInstance );
	}
	return m_pInstance;
}

//------------------------------------------------------------------------------
/// Create controls and initialize event handling.
wxWindow* wxPGSpinCtrlEditor::CreateControls ( wxPropertyGrid* propgrid, wxPGProperty* property, const wxPoint& pos, const wxSize& sz, wxWindow** ) const
//------------------------------------------------------------------------------
{

	// Get initial value (may be none if value is 'unspecified')
	wxString text;
	if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
	{
		text = property->GetValueAsString(0);
	}

	// Determine minimum and maximum
	double min = numeric_limits<double>::min();
	double max = numeric_limits<double>::max();
	double step = 1.0;
	
	PropertyObject* pPropData = static_cast<PropertyObject*>(property->GetClientData());
	wxString format( Property(pPropData->Component()).stringFormatString().c_str() );
	if( pPropData )
	{
		switch( pPropData->Component().type() )
		{
		case ctPropInt:
			{
				PropertyI intProp( pPropData->Component() );
				min		= intProp.isConstDefined( plMinValue ) ? intProp.read( plMinValue ) : numeric_limits<int>::min();
				max		= intProp.isConstDefined( plMaxValue ) ? intProp.read( plMaxValue ) : numeric_limits<int>::max();
				step	= intProp.isConstDefined( plStepWidth ) ? intProp.read( plStepWidth ) : 1.0;
				format.Replace( "%d", "%.0f", true ); // Otherwise the int -> double conversion will fail as some int properties specify %d style format strings, which don't work with this control.
			}
			break;
		case ctPropFloat:
			{
				PropertyF floatProp( pPropData->Component() );
				min = floatProp.isConstDefined( plMinValue ) ? floatProp.read( plMinValue ) : numeric_limits<double>::min();
				max = floatProp.isConstDefined( plMaxValue ) ? floatProp.read( plMaxValue ) : numeric_limits<double>::max();
				step = floatProp.isConstDefined( plStepWidth ) ? floatProp.read( plStepWidth ) : 1.0;
				break;
			}
		default:
			{
				// unrecognized type for this editor class.
				wxASSERT(0);
			}
		}
	}
	
	wxString valStr = propgrid->GetPropertyValueAsString(property);
	double value;
	valStr.ToDouble(&value);

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

	wxSize size(sz);
	/// \todo find out why 'sz' is too small...
	size.SetHeight( sz.GetHeight() + 6 );
	pEditor->Create( propgrid, wxPG_SUBID1, text, pos, size,wxSP_ARROW_KEYS, min, max, value, step );
	if( !format.IsEmpty() )
	{
		pEditor->SetFormat( format );
	}
	// 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 wxPGSpinCtrlEditor::UpdateControl ( wxPGProperty* property, wxWindow* wnd ) const
//------------------------------------------------------------------------------
{
	wxSpinCtrlDbl* pEditor = (wxSpinCtrlDbl*) wnd;
	wxASSERT( pEditor && pEditor->IsKindOf(CLASSINFO(wxSpinCtrlDbl)) );

	double value;
	property->GetValueAsString().ToDouble( &value );
	pEditor->SetValue( value );
}

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

//------------------------------------------------------------------------------
/// Copies value from control to property
bool wxPGSpinCtrlEditor::CopyValueFromControl ( wxPGProperty* property, wxWindow* wnd ) const
//------------------------------------------------------------------------------
{
	wxSpinCtrlDbl* pEditor = (wxSpinCtrlDbl*) wnd;
	wxASSERT( pEditor && pEditor->IsKindOf(CLASSINFO(wxSpinCtrlDbl)) );
	return property->SetValueFromString( wxString::Format( pEditor->GetFormat(), pEditor->GetValue() ), wxPG_FULL_VALUE );
}

//------------------------------------------------------------------------------
/// Makes control look like it has unspecified value
void wxPGSpinCtrlEditor::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 wxPGSpinCtrlEditor::SetControlStringValue ( wxWindow* wnd, const wxString& txt ) const
//------------------------------------------------------------------------------
{
	wxSpinCtrlDbl* pEditor = (wxSpinCtrlDbl*) wnd;
	wxASSERT( pEditor && pEditor->IsKindOf(CLASSINFO(wxSpinCtrlDbl)) );
	pEditor->SetValue(txt, false);
}
