//-----------------------------------------------------------------------------
#include "DataConversion.h"
#include "PropTree.h"
#include "PropData.h"
#include "SpinEditorDouble.h"
#include <wxPropGrid/Include/propgrid.h>
#include <wxPropGrid/Include/advprops.h>
#include <wx/settings.h>
#include <wx/msgdlg.h>
#include <limits>
#include <vector>

//------------------------------------------------------------------------------
using namespace std;
using namespace mvIMPACT::acquire;

//=============================================================================
//========================= PropData ==========================================
//=============================================================================

const wxColour PropData::m_IS_DEFAULT_VALUE_COLOUR(5, 165, 5);

//-----------------------------------------------------------------------------
PropData::PropData( HOBJ hObj ) : m_Component( hObj ),
	m_lastChangedCounter(numeric_limits<unsigned int>::max()),
	m_lastChangedCounterAttr(numeric_limits<unsigned int>::max()), m_GridItemId(wxNullProperty), m_pParentGrid(0) {}
//-----------------------------------------------------------------------------

//------------------------------------------------------------------------------
void PropData::AppendComponentInfo( wxString &info, unsigned int actChangedCount, unsigned int actAttrChangedCount ) const
//------------------------------------------------------------------------------
{
	ostringstream oss;
	oss << ", hObj: ";
	oss.setf( ios::hex, ios::basefield );
	oss.setf( ios::uppercase );
	oss << m_Component.hObj();
	oss.unsetf( ios::hex | ios::uppercase );
	oss << ", cc: " << actChangedCount
		<< ", cca: " << actAttrChangedCount
		<< ", type: " << m_Component.typeAsString()
		<< ", flags: " << m_Component.flagsAsString();

	if( m_Component.isProp() )
	{
		const Property prop(m_Component);
		if( prop.isConstDefined( plMinValue ) )
		{
			oss << ", min: " << prop.readS( plMinValue );
		}
		if( prop.isConstDefined( plMaxValue ) )
		{
			oss << ", max: " << prop.readS( plMaxValue );
		}
		if( prop.isConstDefined( plStepWidth ) )
		{
			oss << ", step width: " << prop.readS( plStepWidth );
		}
	}
	info.Append( ConvertedString(oss.str()) );
}

//------------------------------------------------------------------------------
void PropData::UpdateGridItem( const PropTree *pPropTree, EDisplayFlags flags, bool *boModified )
//------------------------------------------------------------------------------
{
	const unsigned int actChangedCount = m_Component.changedCounter();
	const unsigned int actAttrChangedCount = m_Component.changedCounterAttr();

	if( ( m_lastChangedCounter != actChangedCount ) || ( m_lastChangedCounterAttr != actAttrChangedCount ) )
	{
		Update( pPropTree, flags, actChangedCount, actAttrChangedCount );
		m_lastChangedCounter = actChangedCount;
		m_lastChangedCounterAttr = actAttrChangedCount;
		if( boModified )
		{
			*boModified = true;
		}
	}
	else if( boModified )
	{
		*boModified = false;
	}

#if wxPROPGRID_MINOR > 2
	if( m_pParentGrid->GetSelectedProperty() != m_GridItemId )
#else
	if( !m_pParentGrid->IsPropertySelected( m_GridItemId ) )
#endif
	{
		if( flags & dfDisplayInvisibleComponents )
		{
#if wxPROPGRID_MINOR > 2
			m_pParentGrid->SetPropertyBackgroundColour( m_GridItemId, m_Component.isVisible() ? ( m_Component.isList() ? ListObject::BACKGROUND_COLOR : *wxWHITE ): *wxGREEN );
#else
			m_pParentGrid->SetPropertyPriority( m_GridItemId, wxPG_HIGH );
			m_pParentGrid->SetPropertyColour( m_GridItemId, m_Component.isVisible() ? ( m_Component.isList() ? ListObject::BACKGROUND_COLOR : *wxWHITE ): *wxGREEN );
		}
		else
		{
			m_pParentGrid->SetPropertyPriority( m_GridItemId, m_Component.isVisible() ? wxPG_HIGH : wxPG_LOW );
#endif
		}
	}
}

#if wxPROPGRID_MINOR > 2
# define NOW_NEW new
#else
# define NOW_NEW
#endif

//=============================================================================
//========================= MethodObject ======================================
//=============================================================================
//-----------------------------------------------------------------------------
MethodObject::MethodObject( HOBJ hObj ) : PropData(hObj), m_Params(wxT(""))
//-----------------------------------------------------------------------------
{
	string::size_type end = 0;
	string name(m_Component.name());

	if( ( end = name.find_first_of( "@" ) ) != string::npos )
	{
		name = name.substr( 0, end );
	}

	const Method meth(m_Component);
	const string para_type_str = meth.paramList();
	wxString comma(wxT(" "));

	string::const_iterator  it = para_type_str.begin();
	m_FriendlyName = ConvertedString(charToType( *it++ ));
	m_FriendlyName += comma;
	m_FriendlyName += ConvertedString(name);
	m_FriendlyName += wxT("(");

	for( ; it<para_type_str.end(); it++, comma = wxT(", ") )
	{
		m_FriendlyName += comma;
		m_FriendlyName += ConvertedString(charToType( *it ));
	}
	m_FriendlyName += wxT(" )");
}

//------------------------------------------------------------------------------
wxString MethodObject::Call() const
//------------------------------------------------------------------------------
{
	if( wxPGIdIsOk(m_GridItemId) && m_Component.isMeth() )
	{
		const wxString params = wxPGIdToPtr(m_GridItemId)->GetValueAsString();
		const Method meth(m_Component);
		int callResult;

		try
		{
			string paramsANSI(params.mb_str());
			callResult = meth.call( paramsANSI, " " );
		}
		catch( const ImpactAcquireException& e )
		{
			callResult = e.getErrorCode();
			wxString errorString(wxString::Format( wxT("An error occurred while executing function %s : %s (numerical error representation: %d (%s))."), m_FriendlyName.c_str(), e.getErrorString().c_str(), e.getErrorCode(), e.getErrorCodeAsString().c_str() ));
			errorString.append( wxString::Format( wxT("Error origin: %s"), e.getErrorOrigin().c_str() ) );
			wxMessageDialog errorDlg( NULL, errorString, wxT("Error"), wxOK | wxICON_INFORMATION );
			errorDlg.ShowModal();
		}

		wxString resultMsg(wxString::Format( wxT("[ Last call info: %s( %s )"), m_FriendlyName.c_str(), params.c_str() ));
		const char retType = meth.paramList()[0];
		if( retType != 'v' )
		{
			resultMsg.append( wxT(" = ") );
			resultMsg.append( ConvertedString(ImpactAcquireException::getErrorCodeAsString( callResult )) );
		}
		resultMsg.append( wxT(" ]") );
		return resultMsg;
	}
	return wxString(wxT("Invalid method object"));
}

//------------------------------------------------------------------------------
void MethodObject::UpdatePropData( void )
//------------------------------------------------------------------------------
{
	if( wxPGIdIsOk(m_GridItemId) )
	{
		m_Params = wxPGIdToPtr(m_GridItemId)->GetValueAsString();
	}
}

//------------------------------------------------------------------------------
void MethodObject::Update( const PropTree* /*pPropTree*/, EDisplayFlags flags, unsigned int actChangedCount, unsigned int actAttrChangedCount ) const
//------------------------------------------------------------------------------
{
	ConvertedString label(m_FriendlyName);
	if( flags & dfDisplayDebugInfo )
	{
		AppendComponentInfo( label, actChangedCount, actAttrChangedCount );
	}
	if( wxPGIdIsOk(m_GridItemId) )
	{
		wxPGIdToPtr(m_GridItemId)->SetValueFromString( m_Params );
		wxPGIdToPtr(m_GridItemId)->SetLabel( label );
		wxPGIdToPtr(m_GridItemId)->SetHelpString( ConvertedString(m_Component.docString()) );
	}
}

//------------------------------------------------------------------------------
void MethodObject::EnsureValidGridItem( const PropTree *pPropTree, wxPGId parentItem, bool* boModified /* = 0 */ )
//------------------------------------------------------------------------------
{
	if( !wxPGIdIsOk( m_GridItemId ) )
	{
		m_pParentGrid = pPropTree->GetPropGrid();
		m_GridItemId = m_pParentGrid->AppendIn( parentItem, NOW_NEW wxStringProperty(ConvertedString(m_FriendlyName), wxPG_LABEL, wxT("")) );
		m_pParentGrid->SetPropertyClientData( m_GridItemId, this );
		m_Type = _ctrlEdit;

		if( boModified )
		{
			*boModified = true;
		}
	}
}

//=============================================================================
//========================= ListObject ========================================
//=============================================================================
const wxColour	ListObject::BACKGROUND_COLOR(230, 230, 230);
//string			ListObject::m_colourKey = string("<color>");

//-----------------------------------------------------------------------------
ListObject::ListObject( HOBJ hObj, const char *pTitle /* = 0 */ )
: PropData( hObj ), m_boExpanded(FALSE), m_Title( ConvertedString( pTitle ? pTitle : string() ) )
//-----------------------------------------------------------------------------
{
}

//-----------------------------------------------------------------------------
void ListObject::OnExpand()
//------------------------------------------------------------------------------
{
	if( wxPGIdIsOk(m_GridItemId) && m_pParentGrid )
	{
		m_boExpanded = m_pParentGrid->IsPropertyExpanded( m_GridItemId );
	}

	//UpdateLabel();
}

//------------------------------------------------------------------------------
void ListObject::EnsureValidGridItem( const PropTree *pPropTree, wxPGId parentItem, bool* boModified/* = 0 */ )
//------------------------------------------------------------------------------
{
	if( !wxPGIdIsOk(m_GridItemId) )
	{
		m_pParentGrid = pPropTree->GetPropGrid();
#if wxPROPGRID_MINOR > 2
		// wxPGPropertyWithChildren is now abstract   
		wxPropertyCategory * const prop = new wxPropertyCategory(ConvertedString(m_Component.name()));
#else
		wxPGPropertyWithChildren * const prop = new wxParentPropertyClass(ConvertedString(m_Component.name()), wxPG_LABEL);
#endif
		// strictly speaking, prop could be NULL
		m_GridItemId = m_pParentGrid->AppendIn( parentItem, prop );
		if( m_GridItemId != prop )
		{
			wxASSERT( !"Invalid parenting" ); // no need to delete prop, wxPropertyGridState::PrepareToAddItem already did that
		}
		m_Type = _ctrlStatic;
		m_pParentGrid->SetPropertyClientData( m_GridItemId, this );
		const ComponentList list(m_Component);
		ConvertedString contDesc(list.contentDescriptor());
		if( !contDesc.empty() )
		{
			wxPGIdToPtr(m_GridItemId)->SetValueFromString( contDesc );
			m_pParentGrid->SetPropertyTextColour( m_GridItemId, *wxBLUE );
		}
#if wxPROPGRID_MINOR > 2
		m_pParentGrid->SetPropertyBackgroundColour( m_GridItemId, BACKGROUND_COLOR );
#else
		m_pParentGrid->SetPropertyColour( m_GridItemId, BACKGROUND_COLOR );
#endif
		m_pParentGrid->DisableProperty( m_GridItemId );
		if( boModified )
		{
			*boModified = true;
		}
	}
}

////------------------------------------------------------------------------------
//bool ListObject::GetColour( string& text, wxColour& colour )
////------------------------------------------------------------------------------
//{
//	string::size_type end = 0;
//	if ( ( end = text.find( m_colourKey, 0 ) ) != string::npos )
//	{
//		string tempstring = text.substr( end + m_colourKey.size() );
//		text = text.substr( end + m_colourKey.size() + 1 );
//
//		switch( tempstring.at(0) )
//		{
//		case 'r':
//			colour = wxColour( *wxRED );
//			break;
//		case 'g':
//			colour = wxColour( *wxGREEN );
//			break;
//		case 'b':
//			colour = wxColour( *wxBLUE );
//			break;
//		default:
//			colour = wxColour( *wxBLACK );
//			break;
//		}
//		return true;
//	}
//	return false;
//}

//------------------------------------------------------------------------------
void ListObject::Update( const PropTree* /*pPropTree*/, EDisplayFlags flags, unsigned int actChangedCount, unsigned int actAttrChangedCount ) const
//------------------------------------------------------------------------------
{
	if( actAttrChangedCount != m_lastChangedCounterAttr )
	{
		wxString label;
		if( m_Title.empty() )
		{
			label = ConvertedString(m_Component.name());
		}
		else
		{
			label = m_Title;
		}
		if( flags & dfDisplayDebugInfo )
		{
			label.Append( wxString::Format( wxT("[%d]"), ComponentList(m_Component).size() ) );
			AppendComponentInfo( label, actChangedCount, actAttrChangedCount );
		}
		wxPGIdToPtr(m_GridItemId)->SetLabel( label );
		wxPGIdToPtr(m_GridItemId)->SetHelpString( ConvertedString(m_Component.docString()) );
		const ComponentList list(m_Component);
		ConvertedString contDesc(list.contentDescriptor());
		if( !contDesc.empty() )
		{
			wxPGIdToPtr(m_GridItemId)->SetValueFromString( contDesc );
			m_pParentGrid->SetPropertyTextColour( m_GridItemId, *wxBLUE );
		}
	}
}

//=============================================================================
//========================= PropertyObject ====================================
//=============================================================================
//-----------------------------------------------------------------------------
PropertyObject::PropertyObject( HOBJ hObj, int index /* = 0 */ )
: PropData( hObj ), m_Index( index < 0 ? 0 : index ), m_boVectorAsList( index >= 0 )
//-----------------------------------------------------------------------------
{
}

//-----------------------------------------------------------------------------
wxString PropertyObject::GetCurrentValueAsString( void ) const
//-----------------------------------------------------------------------------
{
	return ConvertedString(Property(m_Component).readS( m_Index, string(( m_Type == _ctrlMultiChoiceSelector ) ? "\"%s\" " : "") ));
}

//-----------------------------------------------------------------------------
void PropertyObject::WritePropVal( const string& value ) const
//-----------------------------------------------------------------------------
{
	const Property prop( m_Component );
	if( !prop.isWriteable() )
	{
		wxASSERT( !"Trying to write to a non-writeable property" );
	}
	if( !m_boVectorAsList )
	{
		prop.writeS( value );
	}
	else
	{
		// only write one value to the property!
		const string::size_type end = value.find_first_of( " " );
		prop.writeS( ( end != string::npos ) ? value.substr( 0, end ) : value, m_Index );
	}
}

//------------------------------------------------------------------------------
void PropertyObject::UpdatePropData( void )
//------------------------------------------------------------------------------
{
	if( wxPGIdIsOk(m_GridItemId) )
	{
		wxString errorString;
		const Property prop(m_Component);
		try
		{
			string valueANSI(wxPGIdToPtr(m_GridItemId)->GetValueAsString().mb_str());
			WritePropVal( valueANSI );
		}
		catch( const EValTooSmall& )
		{
			errorString = wxT("Value too small! Clipping to minimum!");
			WritePropVal( prop.readS( plMinValue ) );
		}
		catch( const EValTooLarge& )
		{
			errorString = wxT("Value too large! Clipping to maximum!");
			WritePropVal( prop.readS( plMaxValue ) );
		}
		catch( const ENoModifySizeRights& )
		{
			errorString = wxString::Format( wxT("This property can store %d value(s) only!"), prop.valCount() );
		}
		catch( const EValidationFailed& )
		{
			errorString = wxT("Value didn't pass validation check. The device log-file will contain additional information!");
		}
		catch( const ImpactAcquireException& e )
		{
			errorString = wxString::Format( wxT("Can't set value( Error: %s(%s))!"), e.getErrorString().c_str(), e.getErrorCodeAsString().c_str() );
		}

		wxPGIdToPtr(m_GridItemId)->SetValueFromString( GetCurrentValueAsString() );
		if( !errorString.IsEmpty() )
		{
			wxMessageDialog errorDlg( NULL, errorString, wxT("Error"), wxOK | wxICON_INFORMATION );
			errorDlg.ShowModal();
		}
	}
}

//-----------------------------------------------------------------------------
void PropertyObject::GetTransformedDict( wxPGChoices& soc, wxString* pEmptyString /* = 0 */ ) const
//-----------------------------------------------------------------------------
{
	const TComponentType type = m_Component.type();
	const TComponentFlag flags = m_Component.flags();
	if( type == ctPropInt )
	{
		vector<pair<string, int> > dict;
		const PropertyI propI(m_Component);
		propI.getTranslationDict( dict );
		vector<pair<string, int> >::size_type vSize = dict.size();
		for( vector<pair<string, int> >::size_type i=0; i<vSize; i++ )
		{
			if( ( flags & cfAllowValueCombinations ) && ( dict[i].second == 0 ) )
			{
				if( pEmptyString )
				{
					*pEmptyString = ConvertedString(dict[i].first);
				}
			}
			else
			{
				soc.Add( ConvertedString(dict[i].first), dict[i].second );
			}
		}
	}
	else if( type == ctPropInt64 )
	{
		// this just works as long as we always use the string values for setting properties as there are no controls
		// for 64 bit integer values so far
		vector<pair<string, int64_type> > dict;
		const PropertyI64 propI64(m_Component);
		propI64.getTranslationDict( dict );
		vector<pair<string, int64_type> >::size_type vSize = dict.size();
		for( vector<pair<string, int64_type> >::size_type i=0; i<vSize; i++ )
		{
			if( ( flags & cfAllowValueCombinations ) && ( dict[i].second == 0 ) )
			{
				if( pEmptyString )
				{
					*pEmptyString = ConvertedString(dict[i].first);
				}
			}
			else
			{
				soc.Add( ConvertedString(dict[i].first), dict[i].second );
			}
		}
	}
	else if( type == ctPropFloat )
	{
		// this just works as long as we always use the string values for setting properties as there are no controls
		// for double values so far
		vector<pair<string, double> > dict;
		const PropertyF propF(m_Component);
		propF.getTranslationDict( dict );
		vector<pair<string, double> >::size_type vSize = dict.size();
		for( vector<pair<string, double> >::size_type i=0; i<vSize; i++ )
		{
			soc.Add( ConvertedString(dict[i].first), static_cast<int>(i) );
		}
	}
	else
	{
		wxASSERT( !"Invalid component type for a combo box control" );
	}
}

//-----------------------------------------------------------------------------
bool PropertyObject::ShouldBeSpinControl( const Property& prop )
//-----------------------------------------------------------------------------
{
	switch( prop.type() )
	{
	case ctPropInt:
	case ctPropInt64:
		return ( ( prop.isConstDefined( plMinValue ) || prop.isConstDefined( plMaxValue ) ) && !prop.hasDict() && prop.isWriteable() );
	case ctPropFloat:
		// only create float property as a spin control if it defines a step size as otherwise the spin control might not allow to select every possible value
		return ( ( ( prop.isConstDefined( plMinValue ) || prop.isConstDefined( plMaxValue ) ) && prop.isConstDefined( plStepWidth ) ) && !prop.hasDict() && prop.isWriteable() );
	default:
		break;
	}
	return false;
}

//------------------------------------------------------------------------------
void PropertyObject::EnsureValidGridItem( const PropTree *pPropTree, wxPGId parentItem, bool* boModified /* = 0 */ )
//------------------------------------------------------------------------------
{
	m_pParentGrid = pPropTree->GetPropGrid();
	const ConvertedString elementName(m_Component.name());
	if( !wxPGIdIsOk(m_GridItemId) )
	{
		const TComponentType type = m_Component.type();
		switch( type )
		{
		case ctPropInt:
		case ctPropInt64:
		case ctPropFloat:
			{
				const Property prop( m_Component );
				if( ShouldBeSpinControl( prop ) )
				{
					m_Type = _ctrlSpinner;
				}
				else if( prop.hasDict() )
				{
					if( prop.flags() & cfAllowValueCombinations )
					{
						m_Type = _ctrlMultiChoiceSelector;
					}
					else
					{
						m_Type = _ctrlCombo;
					}
				}
				else
				{
					m_Type = _ctrlEdit;
				}
			}
			break;
		case ctPropString:
			{
				wxString lowerCaseName(elementName.Lower());
				if( lowerCaseName.Contains( wxString(wxT("directory")) ) )
				{
					m_Type = _ctrlDirSelector;
				}
				else if( lowerCaseName.Contains( wxString(wxT("filename")) ) )
				{
					m_Type = _ctrlFileSelector;
				}
				else
				{
					m_Type = _ctrlEdit;
				}
			}
			break;
		case ctPropPtr:
			m_Type = _ctrlEdit;
			break;
		default:
			break;
		}

		switch( m_Type )
		{
		case _ctrlSpinner:
			if( ( type == ctPropInt ) || ( type == ctPropInt64 ) || ( type == ctPropFloat ) )
			{
				m_GridItemId = m_pParentGrid->AppendIn( parentItem, NOW_NEW wxStringProperty(elementName, wxPG_LABEL) );
			}
			else
			{
				wxASSERT( !"invalid component type for spinner control" );
			}
			m_pParentGrid->SetPropertyEditor( m_GridItemId, wxPGCustomSpinCtrlEditor::Instance() );
			break;
		case _ctrlEdit:
			m_GridItemId = m_pParentGrid->AppendIn( parentItem, NOW_NEW wxStringProperty(elementName, wxPG_LABEL) );
			break;
		case _ctrlCombo:
			{
				wxPGChoices soc;
				GetTransformedDict( soc );
				m_GridItemId = m_pParentGrid->AppendIn( parentItem, NOW_NEW wxEnumProperty(elementName, wxPG_LABEL, soc, 240) );
			}
			break;
		case _ctrlMultiChoiceSelector:
			{
				wxPGChoices soc;
				wxString emptyString;
				GetTransformedDict( soc, &emptyString );
				wxPGProperty* p = wxMultiChoiceProperty(elementName, wxPG_LABEL, soc.GetLabels());
				dynamic_cast<wxMultiChoicePropertyClass*>(p)->SetEmptySelectionString( emptyString );
				m_GridItemId = m_pParentGrid->AppendIn( parentItem, p );
			}
			break;
		case _ctrlFileSelector:
			m_GridItemId = m_pParentGrid->AppendIn( parentItem, NOW_NEW wxFileProperty(elementName, wxPG_LABEL) );
			break;
		case _ctrlDirSelector:
			m_GridItemId = m_pParentGrid->AppendIn( parentItem, NOW_NEW wxDirProperty(elementName , wxPG_LABEL, ::wxGetUserHome()) );
			break;
		default:
			break;
		}

		m_pParentGrid->SetPropertyClientData( m_GridItemId, this );
		if( boModified )
		{
			*boModified = true;
		}
	}
}

//------------------------------------------------------------------------------
void PropertyObject::UpdateLabel( EDisplayFlags flags, unsigned int actChangedCount, unsigned int actAttrChangedCount ) const
//------------------------------------------------------------------------------
{
	ConvertedString label(m_Component.name());
	if( m_boVectorAsList )
	{
		label.Append( wxString::Format( ( ( flags & dfHexIndices ) ? wxT("[0x%x]") : wxT("[%d]") ), m_Index ) );
	}
	else
	{
		const int valCount = Property( m_Component ).valCount();
		if( valCount > 1 )
		{
			label.Append( wxString::Format( wxT("[%d]"), valCount ) );
		}
	}
	if( flags & dfDisplayDebugInfo )
	{
		AppendComponentInfo( label, actChangedCount, actAttrChangedCount );
	}
	m_pParentGrid->SetPropertyLabel( m_GridItemId, label );
	wxPGIdToPtr(m_GridItemId)->SetHelpString( ConvertedString(m_Component.docString()) );
	m_pParentGrid->SetPropertyTextColour( m_GridItemId, m_Component.isWriteable() ? ( m_Component.isDefault() ? m_IS_DEFAULT_VALUE_COLOUR : wxSystemSettings::GetColour ( wxSYS_COLOUR_WINDOWTEXT ) ) : wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
}

//------------------------------------------------------------------------------
void PropertyObject::Update( const PropTree* /*pPropTree*/, EDisplayFlags flags, unsigned int actChangedCount, unsigned int actAttrChangedCount ) const
//------------------------------------------------------------------------------
{
	const Property prop( m_Component );

	switch( Type() )
	{
	case _ctrlEdit:
		{
			string str;
			if( ( prop.valCount() > 1 ) && !m_boVectorAsList )
			{
				const unsigned int valCnt = prop.valCount();
				for( unsigned int i=0; i<valCnt; i++ )
				{
					str.append( prop.readS( i ) );
					str.append( " " );
				}
			}
			else
			{
				str.append( prop.readS( m_Index ) );
			}
			m_pParentGrid->SetPropertyValueString( m_GridItemId, ConvertedString(str) );
		}
		break;
	case _ctrlSpinner:
	case _ctrlDirSelector:
	case _ctrlFileSelector:
	case _ctrlMultiChoiceSelector:
		m_pParentGrid->SetPropertyValueString( m_GridItemId, GetCurrentValueAsString() );
		break;
	case _ctrlCombo:
		if( actAttrChangedCount != m_lastChangedCounterAttr )
		{
			PropertyI prop(m_Component);
			vector<pair<string, int> > dict;
			prop.getTranslationDict( dict );
			wxPGChoices soc;

			vector<pair<string, int> >::iterator it = dict.begin(), itEnd = dict.end();
			while( it != itEnd )
			{
				soc.Add( ConvertedString(it->first), it->second );
				++it;
			}
			m_pParentGrid->SetPropertyChoices( m_GridItemId, soc );
			m_pParentGrid->SetPropertyClientData( m_GridItemId, const_cast<PropertyObject *>(this) );
		}
		m_pParentGrid->SetPropertyValueString( m_GridItemId, GetCurrentValueAsString() );
		break;
	default:
		break;
	}

	// read-only / read-write state
	if( !m_Component.isWriteable() )
	{
		m_pParentGrid->DisableProperty( m_GridItemId );
	}
	else if( !m_pParentGrid->IsPropertyEnabled( m_GridItemId ) )
	{
		m_pParentGrid->EnableProperty( m_GridItemId );
	}
	UpdateLabel( flags, actChangedCount, actAttrChangedCount );
}

//=============================================================================
//========================= VectorPropertyObject ==============================
//=============================================================================
//-----------------------------------------------------------------------------
VectorPropertyObject::VectorPropertyObject( HOBJ hObj )
	: PropData( hObj ), m_boExpanded(false) {}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
VectorPropertyObject::~VectorPropertyObject()
//-----------------------------------------------------------------------------
{
	size_t propCnt = static_cast<size_t>(m_VectorItems.size());
	m_pParentGrid->Freeze();
	for( size_t i=0; i<propCnt; i++ )
	{
		DeleteGridProperty( i );
	}
	m_pParentGrid->Thaw();
}

//-----------------------------------------------------------------------------
void VectorPropertyObject::DeleteGridProperty( size_t index )
//-----------------------------------------------------------------------------
{
	if( m_VectorItems.at( index ) )
	{
		wxPGId item = m_VectorItems[index]->GetGridItem();
		if( wxPGIdIsOk(item) )
		{
#if wxPROPGRID_MINOR > 2
			m_pParentGrid->DeleteProperty( item );
#else
			m_pParentGrid->Delete( item );
#endif
		}
		delete m_VectorItems[index];
		m_VectorItems[index] = 0;
	}
}

//-----------------------------------------------------------------------------
void VectorPropertyObject::OnExpand()
//------------------------------------------------------------------------------
{
	if( wxPGIdIsOk(m_GridItemId) && m_pParentGrid )
	{
		m_boExpanded = m_pParentGrid->IsPropertyExpanded( m_GridItemId );
	}
}

//------------------------------------------------------------------------------
PropertyObject* VectorPropertyObject::GetVectorItem( int index )
//------------------------------------------------------------------------------
{
	const int vSize = static_cast<int>(m_VectorItems.size());
	for( int i=vSize; i<=index; i++ )
	{
		m_VectorItems.push_back( new PropertyObject( m_Component, i ) );
	}
	return m_VectorItems.at(index);
}

//------------------------------------------------------------------------------
void VectorPropertyObject::EnsureValidGridItem( const PropTree* pPropTree, wxPGId parentItem, bool* boModified /* = 0 */ )
//------------------------------------------------------------------------------
{
	if( !wxPGIdIsOk(m_GridItemId) )
	{
		m_pParentGrid = pPropTree->GetPropGrid();
#if wxPROPGRID_MINOR > 2
		wxPropertyCategory * const prop = new wxPropertyCategory(ConvertedString(m_Component.name()));
#else
		wxPGPropertyWithChildren * const prop = new wxParentPropertyClass(ConvertedString(m_Component.name()), wxPG_LABEL);
#endif
		// strictly speaking, prop could be NULL
		m_GridItemId = m_pParentGrid->AppendIn( parentItem, prop );
		if( m_GridItemId != prop )
		{
			wxASSERT( !"Invalid parenting" ); // no need to delete prop, wxPropertyGridState::PrepareToAddItem already did that
		}
		m_Type = _ctrlStatic;
		m_pParentGrid->SetPropertyClientData( m_GridItemId, this );
		m_pParentGrid->DisableProperty( m_GridItemId );
		if( boModified )
		{
			*boModified = true;
		}
	}

	const unsigned int actChangedCount = m_Component.changedCounter();
	const unsigned int actAttrChangedCount = m_Component.changedCounterAttr();
	if( ( m_lastChangedCounter != actChangedCount ) || ( m_lastChangedCounterAttr != actAttrChangedCount ) )
	{
		const Property prop(m_Component);
		m_pParentGrid->Freeze();
		for( unsigned int i=0; i<prop.valCount(); i++ )
		{
			pPropTree->CreateGridProperty( m_Component, m_GridItemId, i );
		}
		m_pParentGrid->Thaw();
	}
}

//-----------------------------------------------------------------------------
void VectorPropertyObject::RemoveValue( unsigned int index )
//-----------------------------------------------------------------------------
{
	const Property prop(m_Component);
	const unsigned int propValCount = prop.valCount();
	const unsigned int vSize = static_cast<unsigned int>(m_VectorItems.size());
	if( ( propValCount > index ) && ( vSize > index ) && ( vSize > 1 ) )
	{
		prop.removeValue( index );
		DeleteGridProperty( index );
		m_VectorItems.erase( m_VectorItems.begin() + index );
	}
}

//------------------------------------------------------------------------------
void VectorPropertyObject::Resize( void )
//------------------------------------------------------------------------------
{
	const Property prop(m_Component);
	const unsigned int propValCount = prop.valCount();
	const unsigned int vSize = static_cast<unsigned int>(m_VectorItems.size());
	if( propValCount < vSize )
	{
		m_pParentGrid->Freeze();
		for( unsigned int i=propValCount; i<vSize; i++ )
		{
			wxPGId item = m_VectorItems[i]->GetGridItem();
			if( wxPGIdIsOk(item) )
			{
#if wxPROPGRID_MINOR > 2
				m_pParentGrid->DeleteProperty( item );
#else
				m_pParentGrid->Delete( item );
#endif
			}
			delete m_VectorItems[i];
		}
		m_VectorItems.resize( propValCount );
		m_pParentGrid->Thaw();
	}
	else if( propValCount > vSize )
	{
		m_pParentGrid->Freeze();
		for( unsigned int i=vSize; i<propValCount; i++ )
		{
			m_VectorItems.push_back( new PropertyObject( m_Component, static_cast<int>(i) ) );
		}
		m_pParentGrid->Thaw();
	}
}

//-----------------------------------------------------------------------------
void VectorPropertyObject::Update( const PropTree* pPropTree, EDisplayFlags flags, unsigned int actChangedCount, unsigned int actAttrChangedCount ) const
//-----------------------------------------------------------------------------
{
	const Property prop(m_Component);
	const unsigned int valCnt = prop.valCount();
	unsigned int vSize = static_cast<unsigned int>(m_VectorItems.size());
	ConvertedString label(prop.name());
	label.Append(wxString::Format( wxT("[%d]"), valCnt ));
	if( flags & dfDisplayDebugInfo )
	{
		AppendComponentInfo( label, actChangedCount, actAttrChangedCount );
	}
	m_pParentGrid->SetPropertyLabel( m_GridItemId, label );
	wxString val(wxT("["));
	val.Append( ConvertedString(prop.readSArray( "", ", " )) );
	val.Append(wxT("]"));
	wxPGIdToPtr(m_GridItemId)->SetValueFromString( val );
	while( valCnt > vSize )
	{
		pPropTree->CreateGridProperty( m_Component, m_GridItemId, vSize++ );
	}
	m_pParentGrid->SetPropertyTextColour( m_GridItemId, prop.isDefault() ? m_IS_DEFAULT_VALUE_COLOUR : wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
}
