// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

namespace CalculatorApp
{
    namespace Common
    {
        ref class AlwaysSelectedCollectionView sealed : public Windows::UI::Xaml::DependencyObject, public Windows::UI::Xaml::Data::ICollectionView
        {
            internal : AlwaysSelectedCollectionView(Windows::UI::Xaml::Interop::IBindableVector ^ source)
                : m_currentPosition(-1)
            {
                m_source = source;

                Windows::UI::Xaml::Interop::IBindableObservableVector ^ observable =
                    dynamic_cast<Windows::UI::Xaml::Interop::IBindableObservableVector ^>(source);
                if (observable)
                {
                    observable->VectorChanged += ref new Windows::UI::Xaml::Interop::BindableVectorChangedEventHandler(
                        this, &AlwaysSelectedCollectionView::OnSourceBindableVectorChanged);
                }
            }

        private:
            // ICollectionView
            // Not implemented methods
            virtual WF::IAsyncOperation<
                Windows::UI::Xaml::Data::LoadMoreItemsResult> ^ LoadMoreItemsAsync(unsigned int) = Windows::UI::Xaml::Data::ICollectionView::LoadMoreItemsAsync
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual bool MoveCurrentToFirst() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToFirst
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual bool MoveCurrentToLast() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToLast
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual bool MoveCurrentToNext() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToNext
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual bool MoveCurrentToPrevious() = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPrevious
            {
                throw ref new Platform::NotImplementedException();
            }
            property Windows::Foundation::Collections::IObservableVector<Platform::Object ^> ^ CollectionGroups {
                virtual Windows::Foundation::Collections::IObservableVector<
                    Platform::Object ^> ^ get() = Windows::UI::Xaml::Data::ICollectionView::CollectionGroups::get
                {
                    return ref new Platform::Collections::Vector<Platform::Object ^>();
                }
            } property bool HasMoreItems
            {
                virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::HasMoreItems::get
                {
                    return false;
                }
            }

            // Implemented methods
            virtual bool MoveCurrentTo(Platform::Object ^ item) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentTo
            {
                if (item)
                {
                    unsigned int newCurrentPosition = 0;
                    bool result = m_source->IndexOf(item, &newCurrentPosition);
                    if (result)
                    {
                        m_currentPosition = newCurrentPosition;
                        m_currentChanged(this, nullptr);
                        return true;
                    }
                }

                // The item is not in the collection
                // We're going to schedule a call back later so we
                // restore the selection to the way we wanted it to begin with
                if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
                {
                    this->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]() {
                                                   m_currentChanged(this, nullptr);
                                               }));
                }
                return false;
            }

            virtual bool MoveCurrentToPosition(int index) = Windows::UI::Xaml::Data::ICollectionView::MoveCurrentToPosition
            {
                if (index < 0 || index >= static_cast<int>(m_source->Size))
                {
                    return false;
                }

                m_currentPosition = index;
                m_currentChanged(this, nullptr);
                return true;
            }

            property Platform::Object^ CurrentItem
        {
            virtual Platform::Object^ get() = Windows::UI::Xaml::Data::ICollectionView::CurrentItem::get
            {
                if (m_currentPosition >= 0 && m_currentPosition < static_cast<int>(m_source->Size))
                {
                    return m_source->GetAt(m_currentPosition);
                }
                return nullptr;
            }
        }

        property int CurrentPosition
            {
                virtual int get() = Windows::UI::Xaml::Data::ICollectionView::CurrentPosition::get
                {
                    return m_currentPosition;
                }
            }

            property bool IsCurrentAfterLast
            {
                virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentAfterLast::get
                {
                    return m_currentPosition >= static_cast<int>(m_source->Size);
                }
            }

            property bool IsCurrentBeforeFirst
            {
                virtual bool get() = Windows::UI::Xaml::Data::ICollectionView::IsCurrentBeforeFirst::get
                {
                    return m_currentPosition < 0;
                }
            }

            event WF::EventHandler<Platform::Object^>^ CurrentChanged
        {
            virtual WF::EventRegistrationToken add(WF::EventHandler<Platform::Object^>^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::add
            {
                return m_currentChanged += handler;
            }
            virtual void remove(WF::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanged::remove
            {
                m_currentChanged -= token;
            }
        }
        event Windows::UI::Xaml::Data::CurrentChangingEventHandler^ CurrentChanging
        {
            virtual WF::EventRegistrationToken add(Windows::UI::Xaml::Data::CurrentChangingEventHandler^ handler) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::add
            {
                return m_currentChanging += handler;
            }
            virtual void remove(WF::EventRegistrationToken token) = Windows::UI::Xaml::Data::ICollectionView::CurrentChanging::remove
            {
                m_currentChanging -= token;
            }
        }

        // IVector<Object^>
          // Not implemented methods
        virtual void Append(Platform::Object^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object^>::Append
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual void Clear() = Windows::Foundation::Collections::IVector<Platform::Object ^>::Clear
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual unsigned int GetMany(
                unsigned int /*startIndex*/,
                Platform::WriteOnlyArray<Platform::Object ^> ^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object ^>::GetMany
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual Windows::Foundation::Collections::IVectorView<Platform::Object ^> ^ GetView() = Windows::Foundation::Collections::IVector<
                Platform::Object ^>::GetView
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual void InsertAt(unsigned int /*index*/, Platform::Object ^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object ^>::InsertAt
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual void RemoveAt(unsigned int /*index*/) = Windows::Foundation::Collections::IVector<Platform::Object ^>::RemoveAt
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual void RemoveAtEnd() = Windows::Foundation::Collections::IVector<Platform::Object ^>::RemoveAtEnd
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual void
            ReplaceAll(const Platform::Array<Platform::Object ^> ^ /*items*/) = Windows::Foundation::Collections::IVector<Platform::Object ^>::ReplaceAll
            {
                throw ref new Platform::NotImplementedException();
            }
            virtual void SetAt(unsigned int /*index*/, Platform::Object ^ /*item*/) = Windows::Foundation::Collections::IVector<Platform::Object ^>::SetAt
            {
                throw ref new Platform::NotImplementedException();
            }

            // Implemented methods
            virtual Platform::Object ^ GetAt(unsigned int index) = Windows::Foundation::Collections::IVector<Platform::Object ^>::GetAt
            {
                return m_source->GetAt(index);
            }

            virtual bool IndexOf(Platform::Object ^ item, unsigned int* index) = Windows::Foundation::Collections::IVector<Platform::Object ^>::IndexOf
            {
                return m_source->IndexOf(item, index);
            }

            property unsigned int Size
            {
                virtual unsigned int get() = Windows::Foundation::Collections::IVector<Platform::Object ^>::Size::get
                {
                    return m_source->Size;
                }
            }

            // IObservableVector<Object^>
            event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ VectorChanged
        {
            virtual WF::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object^>^ handler) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::add
            {
                return m_vectorChanged += handler;
            }
            virtual void remove(WF::EventRegistrationToken token) = Windows::Foundation::Collections::IObservableVector<Platform::Object^>::VectorChanged::remove
            {
                m_vectorChanged -= token;
            }
        }

        // IIterable<Object^>
         // Not implemented
        virtual Windows::Foundation::Collections::IIterator<Platform::Object^>^ First() = Windows::Foundation::Collections::IIterable<Platform::Object^>::First
            {
                throw ref new Platform::NotImplementedException();
            }

            // Event handlers
            void OnSourceBindableVectorChanged(Windows::UI::Xaml::Interop::IBindableObservableVector ^ source, Platform::Object ^ e)
            {
                Windows::Foundation::Collections::IVectorChangedEventArgs ^ args = safe_cast<Windows::Foundation::Collections::IVectorChangedEventArgs ^>(e);
                m_vectorChanged(this, args);
            }

            Windows::UI::Xaml::Interop::IBindableVector ^ m_source;
            int m_currentPosition;
            event WF::EventHandler<Platform::Object ^> ^ m_currentChanged;
            event Windows::UI::Xaml::Data::CurrentChangingEventHandler ^ m_currentChanging;
            event Windows::Foundation::Collections::VectorChangedEventHandler<Platform::Object ^> ^ m_vectorChanged;
        };

    public
        ref class AlwaysSelectedCollectionViewConverter sealed : public Windows::UI::Xaml::Data::IValueConverter
        {
        public:
            AlwaysSelectedCollectionViewConverter()
            {
            }

        private:
            virtual Platform::Object
                ^ Convert(
                    Platform::Object ^ value,
                    Windows::UI::Xaml::Interop::TypeName /*targetType*/,
                    Platform::Object ^ /*parameter*/,
                    Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::Convert
            {
                auto result = dynamic_cast<Windows::UI::Xaml::Interop::IBindableVector ^>(value);
                if (result)
                {
                    return ref new AlwaysSelectedCollectionView(result);
                }
                return Windows::UI::Xaml::DependencyProperty::UnsetValue; // Can't convert
            }

            virtual Platform::Object
                ^ ConvertBack(
                    Platform::Object ^ /*value*/,
                    Windows::UI::Xaml::Interop::TypeName /*targetType*/,
                    Platform::Object ^ /*parameter*/,
                    Platform::String ^ /*language*/) = Windows::UI::Xaml::Data::IValueConverter::ConvertBack
            {
                return Windows::UI::Xaml::DependencyProperty::UnsetValue;
            }
        };
    }
}
