#pragma once

using namespace System::ComponentModel;

namespace Mix{ namespace Tool{ namespace Utility{

	generic<typename DataType>
	public ref class BindingItem : public System::ComponentModel::INotifyPropertyChanged
	{
	protected:
		System::String^ m_StrName;
		System::String^ m_StrValue;

	public:
        virtual event PropertyChangedEventHandler^ PropertyChanged;

		BindingItem( void ) :
		  m_StrName( gcnew System::String( L"" ) ),
		  m_StrValue( gcnew System::String( L"" ) )
		{
		}

		BindingItem( System::String^ _name, DataType _value )
        {
			m_StrName = ( _name != nullptr )? _name : L"";
			m_StrValue = ( _value != nullptr )? _value->ToString() : "";
		}

		property System::String^ Name
		{
			System::String^ get( void )
			{
				return m_StrName;
			}

			void set( System::String^ value )
			{
				if( m_StrName->Equals( value ) == false )
				{
					m_StrName = ( value != nullptr )? value : gcnew System::String( L"" );
					NotifyPropertyChanged( gcnew System::String( L"Name" ) );
				}
			}
		}

		property System::String^ Value
		{
			System::String^ get( void )
			{
				return m_StrValue;
			}

			void set( System::String^ value )
			{
				if( m_StrValue->Equals( value ) == false )
				{
					m_StrValue = ( value != nullptr )? value : gcnew System::String( L"" );
					NotifyPropertyChanged( gcnew System::String( L"Value" ) );
				}
			}
		}

		[Browsable( false )]
		property bool Available
		{
			bool get( void )
			{
				if( ( m_StrName == nullptr ) ||
					( m_StrName->Length == 0 ) ||
					( m_StrValue == nullptr ) ||
					( m_StrValue->Length == 0 ) )
				{
					return false;
				}

				return true;
			}
		}

	private:
		void NotifyPropertyChanged( System::String^ propertyName )
		{
			PropertyChanged( this, gcnew PropertyChangedEventArgs( propertyName ) );
		}
	};

	generic<typename DataType>
	public ref class BindingItemCollection : public System::ComponentModel::BindingList<BindingItem<DataType>^>
	{
	private:
		generic<class T>
		ref class PropertyComparer : public System::Collections::Generic::IComparer<T>
		{
		private:
			System::Reflection::PropertyInfo^ m_Property;
			System::ComponentModel::ListSortDirection m_SortDirection;

		public:
			PropertyComparer( System::ComponentModel::PropertyDescriptor^ prop, System::ComponentModel::ListSortDirection direction )
			{
				m_Property = T::typeid->GetProperty( prop->DisplayName );
				m_SortDirection = direction;
			}

		private:
			virtual int Compare( T x, T y ) sealed = System::Collections::Generic::IComparer<T>::Compare
			{
				System::Object^ valueX = m_Property->GetValue( x, nullptr );
				System::Object^ valueY = m_Property->GetValue( y, nullptr );

				if( m_SortDirection == System::ComponentModel::ListSortDirection::Ascending )
				{
					return System::Collections::Comparer::Default->Compare( valueX, valueY );
				}
				else
				{
					return System::Collections::Comparer::Default->Compare( valueY, valueX );
				}
			}
		};

	private:
		bool m_IsSorted;

	public:
		BindingItemCollection( void ) :
		  m_IsSorted( false )
		{
		}

		int FindByName( System::String^ name )
		{
			for( int i = 0; i < Count; i++ )
			{
				if( Items[i]->Name->Equals( name ) == true )
				{
					return i;
				}
			}

			return -1;
		}

		int FindByData( DataType data )
		{
			for( int i = 0; i < Count; i++ )
			{
				if( Items[i]->Value->Equals( data ) == true )
				{
					return i;
				}
			}

			return -1;
		}

	protected:
		virtual void ApplySortCore( System::ComponentModel::PropertyDescriptor^ prop, System::ComponentModel::ListSortDirection direction ) override
        {
			if( prop != nullptr )
			{
				try
				{
					System::Collections::Generic::List<BindingItem<DataType>^>^ items = safe_cast<System::Collections::Generic::List<BindingItem<DataType>^>^>( Items );

					if( items != nullptr )
					{
						items->Sort( gcnew PropertyComparer<Mix::Tool::Utility::BindingItem<DataType>^>( prop, direction ) );
						m_IsSorted = !m_IsSorted;
					}
					else
					{
						m_IsSorted = false;
					}
				}
				catch( System::Exception^ )
				{
					m_IsSorted = false;
				}
			}
			else
			{
				m_IsSorted = false;
			}

			OnListChanged( gcnew ListChangedEventArgs( ListChangedType::Reset, -1 ) );
		}

		virtual void RemoveSortCore() override
		{
			m_IsSorted = false;
		}

		property bool IsSortedCore
		{
			virtual bool get( void ) override
			{
				return m_IsSorted;
			}
		}

		property bool SupportsSortingCore
		{
			virtual bool get( void ) override
			{
				return true;
			}
		}
	};

}}}
