﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

#include "pch.h"
#include "common.h"
#include "Vector.h"
#include "SplitDataSourceBase.h"
#include "TopNavigationViewDataProvider.h"
#include "NavigationViewItem.h"

TopNavigationViewDataProvider::TopNavigationViewDataProvider(const ITrackerHandleManager* m_owner)
    :SplitDataSourceT()
    , m_rawDataSource(m_owner)
    , m_dataSource(m_owner)
{
    auto lambda = [this](const winrt::IInspectable& value)
    {
        return IndexOf(value);
    };

    auto primaryVector = std::make_shared<SplitVectorT>(m_owner, NavigationViewSplitVectorID::PrimaryList, lambda);
    auto overflowVector = std::make_shared<SplitVectorT>(m_owner, NavigationViewSplitVectorID::OverflowList, lambda);
    
    InitializeSplitVectors({ primaryVector, overflowVector });
}


winrt::IVector<winrt::IInspectable> TopNavigationViewDataProvider::GetPrimaryItems()
{
    return GetVector(NavigationViewSplitVectorID::PrimaryList)->GetVector();
}

winrt::IVector<winrt::IInspectable> TopNavigationViewDataProvider::GetOverflowItems()
{
    return GetVector(NavigationViewSplitVectorID::OverflowList)->GetVector();
}

// The raw data is from MenuItems or MenuItemsSource
void TopNavigationViewDataProvider::SetDataSource(const winrt::IInspectable& rawData)
{
    if (ShouldChangeDataSource(rawData)) // avoid to create multiple of datasource for the same raw data
    {        
        winrt::ItemsSourceView dataSource = nullptr;
        if (rawData)
        {
            dataSource = winrt::ItemsSourceView(rawData);
        }
        ChangeDataSource(dataSource);
        m_rawDataSource.set(rawData);
        if (dataSource)
        {
            MoveAllItemsToPrimaryList();
        }
    }
}

bool TopNavigationViewDataProvider::ShouldChangeDataSource(winrt::IInspectable const& rawData)
{
    return rawData != m_rawDataSource.get();    
}

void TopNavigationViewDataProvider::OnRawDataChanged(std::function<void(winrt::NotifyCollectionChangedEventArgs const& args)> const& dataChangeCallback)
{
    m_dataChangeCallback = dataChangeCallback;
}

int TopNavigationViewDataProvider::IndexOf(const winrt::IInspectable& value)
{
    if (auto dataSource = m_dataSource.get())
    {
        return dataSource.IndexOf(value);
    }
    return -1;
}

winrt::IInspectable TopNavigationViewDataProvider::GetAt(int index)
{
    if (auto dataSource = m_dataSource.get())
    {
        return dataSource.GetAt(index);
    }
    return nullptr;
}

int TopNavigationViewDataProvider::Size()
{
    if (auto dataSource = m_dataSource.get())
    {
        return static_cast<int>(dataSource.Count());
    }
    return 0;
}

NavigationViewSplitVectorID TopNavigationViewDataProvider::DefaultVectorIDOnInsert()
{
    return NavigationViewSplitVectorID::NotInitialized;
}

float TopNavigationViewDataProvider::DefaultAttachedData()
{
    return std::numeric_limits<float>::min();
}

void TopNavigationViewDataProvider::MoveAllItemsToPrimaryList()
{
    for (int i = 0; i < Size(); i++)
    {
        MoveItemToVector(i, NavigationViewSplitVectorID::PrimaryList);
    }
}

std::vector<int> TopNavigationViewDataProvider::ConvertPrimaryIndexToIndex(std::vector<int> const& indexesInPrimary)
{
    std::vector<int> indexes;
    if (!indexesInPrimary.empty())
    {
        auto vector = GetVector(NavigationViewSplitVectorID::PrimaryList);
        if (vector)
        {
            // transform PrimaryList index to OrignalVector index
            std::transform(indexesInPrimary.begin(), indexesInPrimary.end(), std::back_inserter(indexes),
                [&vector](int index) -> int
            {
                return vector->IndexToIndexInOriginalVector(index);
            });
        }
    }
    return indexes;
}

int TopNavigationViewDataProvider::ConvertOriginalIndexToIndex(int originalIndex)
{
    auto const vector = GetVector(IsItemInPrimaryList(originalIndex) ? NavigationViewSplitVectorID::PrimaryList : NavigationViewSplitVectorID::OverflowList);
    return vector->IndexFromIndexInOriginalVector(originalIndex);
}

void TopNavigationViewDataProvider::MoveItemsOutOfPrimaryList(std::vector<int> const& indexes)
{
    MoveItemsToList(indexes, NavigationViewSplitVectorID::OverflowList);
}

void TopNavigationViewDataProvider::MoveItemsToPrimaryList(std::vector<int> const& indexes)
{
    MoveItemsToList(indexes, NavigationViewSplitVectorID::PrimaryList);
}

void TopNavigationViewDataProvider::MoveItemsToList(std::vector<int> const& indexes, NavigationViewSplitVectorID vectorID)
{
    for (auto &index : indexes)
    {
        MoveItemToVector(index, vectorID);
    };
}

int TopNavigationViewDataProvider::GetPrimaryListSize()
{
    return GetPrimaryItems().Size();
}

int TopNavigationViewDataProvider::GetNavigationViewItemCountInPrimaryList()
{
    int count = 0;
    for (int i = 0; i < Size(); i++)
    {
        if (IsItemInPrimaryList(i) && IsContainerNavigationViewItem(i))
        {
            count++;
        }
    }
    return count;
}

int TopNavigationViewDataProvider::GetNavigationViewItemCountInTopNav()
{
    int count = 0;
    for (int i = 0; i < Size(); i++)
    {
        if (IsContainerNavigationViewItem(i))
        {
            count++;
        }
    }
    return count;
}

void TopNavigationViewDataProvider::UpdateWidthForPrimaryItem(int indexInPrimary, float width)
{
    auto vector = GetVector(NavigationViewSplitVectorID::PrimaryList);
    if (vector)
    {
        const auto index = vector->IndexToIndexInOriginalVector(indexInPrimary);
        SetWidthForItem(index, width);
    }
}

float TopNavigationViewDataProvider::WidthRequiredToRecoveryAllItemsToPrimary()
{
    auto width = 0.f;
    for (int i=0; i<Size(); i++)
    {
        if (!IsItemInPrimaryList(i))
        {
            width += GetWidthForItem(i);
        }
    }
    width -= m_overflowButtonCachedWidth;
    return std::max(0.f, width);
}

bool TopNavigationViewDataProvider::HasInvalidWidth(std::vector<int> & items)
{
    bool hasInvalidWidth = false;
    for (auto &index : items)
    {
        if (!IsValidWidthForItem(index))
        {
            hasInvalidWidth = true;
            break;
        }
    }
    return hasInvalidWidth;
}

float TopNavigationViewDataProvider::GetWidthForItem(int index)
{
    auto width = AttachedData(index);
    if (!IsValidWidth(width))
    {
        width = 0;
    }
    return width;
}

float TopNavigationViewDataProvider::CalculateWidthForItems(std::vector<int> &items)
{
    float width = 0.f;
    for (auto &index : items)
    {
        width += GetWidthForItem(index);
    }
    return width;
}

void TopNavigationViewDataProvider::InvalidWidthCache()
{
    ResetAttachedData(-1.0f);
}

float TopNavigationViewDataProvider::OverflowButtonWidth()
{
    return m_overflowButtonCachedWidth;
}

void TopNavigationViewDataProvider::OverflowButtonWidth(float width)
{
    m_overflowButtonCachedWidth = width;
}

bool TopNavigationViewDataProvider::IsItemSelectableInPrimaryList(const winrt::IInspectable& value)
{
    const int index = IndexOf(value);
    return (index != -1);
}

int TopNavigationViewDataProvider::IndexOf(const winrt::IInspectable& value, NavigationViewSplitVectorID vectorID)
{
    return IndexOfImpl(value, vectorID);
}

void TopNavigationViewDataProvider::OnDataSourceChanged(const winrt::IInspectable& sender, const winrt::NotifyCollectionChangedEventArgs& args)
{
    switch (args.Action())
    {
        case winrt::NotifyCollectionChangedAction::Add:
        {   
            OnInsertAt(args.NewStartingIndex(), args.NewItems().Size());
            break;
        }

        case winrt::NotifyCollectionChangedAction::Remove:
        {
            OnRemoveAt(args.OldStartingIndex(), args.OldItems().Size());
            break;
        }

        case winrt::NotifyCollectionChangedAction::Reset:
        {
            OnClear();
            break;
        }

        case winrt::NotifyCollectionChangedAction::Replace:
        {
            OnRemoveAt(args.OldStartingIndex(), args.OldItems().Size());
            OnInsertAt(args.NewStartingIndex(), args.NewItems().Size());
            break;
        }
    }
    if (m_dataChangeCallback)
    {
        m_dataChangeCallback(args);
    }
}

bool TopNavigationViewDataProvider::IsValidWidth(float width)
{
    return (width >= 0) && (width < std::numeric_limits<float>::max());
}

bool TopNavigationViewDataProvider::IsValidWidthForItem(int index)
{
    auto width = AttachedData(index);
    return IsValidWidth(width);
}

void TopNavigationViewDataProvider::SetWidthForItem(int index, float width)
{
    if (IsValidWidth(width))
    {
        AttachedData(index, width);
    }
}

void TopNavigationViewDataProvider::ChangeDataSource(winrt::ItemsSourceView newValue)
{
    auto oldValue = m_dataSource.get();
    if (oldValue != newValue)
    {
        // update to the new datasource.

        if (oldValue)
        {
            oldValue.CollectionChanged(m_dataSourceChanged);
        }

        Clear();

        m_dataSource.set(newValue);
        SyncAndInitVectorFlagsWithID(NavigationViewSplitVectorID::NotInitialized, DefaultAttachedData());

        if (newValue)
        {
            m_dataSourceChanged = newValue.CollectionChanged({ this, &TopNavigationViewDataProvider::OnDataSourceChanged });
        }
    }

    // Move all to primary list
    MoveItemsToVector(NavigationViewSplitVectorID::NotInitialized);
}

bool TopNavigationViewDataProvider::IsItemInPrimaryList(int index)
{
    return GetVectorIDForItem(index) == NavigationViewSplitVectorID::PrimaryList;
}

bool TopNavigationViewDataProvider::IsContainerNavigationViewItem(int index)
{
    bool isContainerNavigationViewItem = true;

    auto item = GetAt(index);
    if (item && (item.try_as<winrt::NavigationViewItemHeader>() || item.try_as<winrt::NavigationViewItemSeparator>()))
    {
        isContainerNavigationViewItem = false;
    }
    return isContainerNavigationViewItem;
}

bool TopNavigationViewDataProvider::IsContainerNavigationViewHeader(int index)
{
    bool isContainerNavigationViewHeader = false;

    auto item = GetAt(index);
    if (item && item.try_as<winrt::NavigationViewItemHeader>())
    {
        isContainerNavigationViewHeader = true;
    }
    return isContainerNavigationViewHeader;
}
