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

#pragma once

#include "Equation.h"

namespace GraphControl
{
    delegate void EquationChangedEventHandler(Equation ^ sender);
    delegate void VisibilityChangedEventHandler(Equation ^ sender);

public
    ref class EquationCollection sealed : public Windows::Foundation::Collections::IObservableVector<GraphControl::Equation ^>
    {
    public:
        virtual ~EquationCollection()
        {
        }

#pragma region IIterable
        virtual Windows::Foundation::Collections::IIterator< GraphControl::Equation^ >^ First()
        {
            return m_vector->First();
        }
#pragma endregion

#pragma region IVector
        virtual property unsigned int Size
        {
            unsigned int get()
            {
                return m_vector->Size;
            }
        }

        virtual void Append(GraphControl::Equation ^ value)
        {
            m_vector->Append(value);
            m_tokens.emplace_back(
                value->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged));
        }

        virtual void Clear()
        {
            auto numEqs = m_vector->Size;
            for (auto i = 0u; i < numEqs; i++)
            {
                m_vector->GetAt(i)->PropertyChanged -= m_tokens[i];
            }

            m_vector->Clear();
            m_tokens.clear();
        }

        virtual GraphControl::Equation
            ^ GetAt(unsigned int index) { return m_vector->GetAt(index); }

            virtual unsigned int GetMany(unsigned int startIndex, Platform::WriteOnlyArray<GraphControl::Equation ^> ^ items)
        {
            return m_vector->GetMany(startIndex, items);
        }

        virtual Windows::Foundation::Collections::IVectorView< GraphControl::Equation^ >^ GetView()
        {
            return m_vector->GetView();
        }

        virtual Platform::Boolean IndexOf(GraphControl::Equation^ value, unsigned int *index)
        {
            return m_vector->IndexOf(value, index);
        }

        virtual void InsertAt(unsigned int index, GraphControl::Equation ^ value)
        {
            m_vector->InsertAt(index, value);
            m_tokens.insert(
                m_tokens.begin() + index,
                value->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged));
        }

        virtual void RemoveAt(unsigned int index)
        {
            m_vector->GetAt(index)->PropertyChanged -= m_tokens[index];

            m_vector->RemoveAt(index);
            m_tokens.erase(m_tokens.begin() + index);
        }

        virtual void RemoveAtEnd()
        {
            auto size = m_vector->Size;
            if (size > 0)
            {
                m_vector->GetAt(size - 1)->PropertyChanged -= *m_tokens.rbegin();
                m_tokens.erase(m_tokens.end() - 1);
            }

            m_vector->RemoveAtEnd();
        }

        virtual void ReplaceAll(const Platform::Array<GraphControl::Equation ^> ^ items)
        {
            auto size = m_vector->Size;
            for (auto i = 0u; i < size; i++)
            {
                m_vector->GetAt(i)->PropertyChanged -= m_tokens[i];
            }

            size = items->Length;
            m_tokens.resize(size);
            for (auto i = 0u; i < size; i++)
            {
                m_tokens[i] = items[i]->PropertyChanged +=
                    ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged);
            }

            m_vector->ReplaceAll(items);
        }

        virtual void SetAt(unsigned int index, GraphControl::Equation ^ value)
        {
            m_vector->GetAt(index)->PropertyChanged -= m_tokens[index];

            m_vector->SetAt(index, value);
            m_tokens[index] = value->PropertyChanged +=
                ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler(this, &EquationCollection::OnEquationPropertyChanged);
        }
#pragma endregion

#pragma region IObservableVector
        virtual event Windows::Foundation::Collections::VectorChangedEventHandler< GraphControl::Equation^ >^ VectorChanged
        {
            Windows::Foundation::EventRegistrationToken add(Windows::Foundation::Collections::VectorChangedEventHandler< GraphControl::Equation^ >^ handler)
            {
                return m_vector->VectorChanged += handler;
            }

            void remove(Windows::Foundation::EventRegistrationToken token)
            {
                m_vector->VectorChanged -= token;
            }
        }
#pragma endregion

    internal:
        EquationCollection() :
            m_vector(ref new Platform::Collections::Vector< GraphControl::Equation^ >())
        {
        }

        event EquationChangedEventHandler ^ EquationChanged;
        event EquationChangedEventHandler ^ EquationStyleChanged;
        event EquationChangedEventHandler ^ EquationLineEnabledChanged;

    private:
        void OnEquationPropertyChanged(Object^ sender, Windows::UI::Xaml::Data::PropertyChangedEventArgs ^ args)
        {
            auto equation = static_cast<Equation ^>(sender);
            auto propertyName = args->PropertyName;
            if (propertyName == GraphControl::Equation::LineColorPropertyName)
            {
                EquationStyleChanged(equation);
            }
            else if (propertyName == GraphControl::Equation::ExpressionPropertyName)
            {
                EquationChanged(equation);
            }
            else if (propertyName == GraphControl::Equation::IsLineEnabledPropertyName)
            {
                EquationLineEnabledChanged(equation);
            }
        }

    private:
        Platform::Collections::Vector<GraphControl::Equation ^> ^ m_vector;
        std::vector<Windows::Foundation::EventRegistrationToken> m_tokens;
    };
}
