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

/* The NavCategory group of classes and enumerations is intended to serve
 * as a single location for storing metadata about a navigation mode.
 *
 * These .h and .cpp files:
 *     - Define the ViewMode enumeration which is used for setting the mode of the app.
 *     - Define a list of metadata associated with each ViewMode.
 *     - Define the order of groups and items in the navigation menu.
 *     - Provide a static helper function for creating the navigation menu options.
 *     - Provide static helper functions for querying information about a given ViewMode.
 */

#pragma once

#include "Utils.h"
#include "MyVirtualKey.h"

namespace CalculatorApp
{
    namespace Common
    {
        // Don't change the order of these enums
        // and definitely don't use int arithmetic
        // to change modes.
    public
        enum class ViewMode
        {
            None = -1,
            Standard = 0,
            Scientific = 1,
            Programmer = 2,
            Date = 3,
            Volume = 4,
            Length = 5,
            Weight = 6,
            Temperature = 7,
            Energy = 8,
            Area = 9,
            Speed = 10,
            Time = 11,
            Power = 12,
            Data = 13,
            Pressure = 14,
            Angle = 15,
            Currency = 16,
            Graphing = 17
        };

    public
        enum class CategoryGroupType
        {
            None = -1,
            Calculator = 0,
            Converter = 1
        };

    private
        struct NavCategoryInitializer
        {
            constexpr NavCategoryInitializer(
                ViewMode mode,
                int id,
                wchar_t const* name,
                wchar_t const* nameKey,
                wchar_t const* glyph,
                CategoryGroupType group,
                MyVirtualKey vKey,
                wchar_t const* aKey,
                bool categorySupportsNegative,
                bool enabled,
                bool isPreview)
                : viewMode(mode)
                , serializationId(id)
                , friendlyName(name)
                , nameResourceKey(nameKey)
                , glyph(glyph)
                , groupType(group)
                , virtualKey(vKey)
                , accessKey(aKey)
                , supportsNegative(categorySupportsNegative)
                , isEnabled(enabled)
                , isPreview(isPreview)
            {
            }

            const ViewMode viewMode;
            const int serializationId;
            const wchar_t* const friendlyName;
            const wchar_t* const nameResourceKey;
            const wchar_t* const glyph;
            const CategoryGroupType groupType;
            const MyVirtualKey virtualKey;
            const wchar_t* const accessKey;
            const bool supportsNegative;
            const bool isEnabled;
            const bool isPreview;
        };

    private
        struct NavCategoryGroupInitializer
        {
            constexpr NavCategoryGroupInitializer(CategoryGroupType t, wchar_t const* h, wchar_t const* n, wchar_t const* a)
                : type(t)
                , headerResourceKey(h)
                , modeResourceKey(n)
                , automationResourceKey(a)
            {
            }

            const CategoryGroupType type;
            const wchar_t* headerResourceKey;
            const wchar_t* modeResourceKey;
            const wchar_t* automationResourceKey;
        };

        [Windows::UI::Xaml::Data::Bindable] public ref class NavCategory sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
        {
        public:
            OBSERVABLE_OBJECT();
            PROPERTY_R(Platform::String ^, Name);
            PROPERTY_R(Platform::String ^, AutomationName);
            PROPERTY_R(Platform::String ^, Glyph);
            PROPERTY_R(ViewMode, Mode);
            PROPERTY_R(Platform::String ^, AccessKey);
            PROPERTY_R(bool, SupportsNegative);
            PROPERTY_R(bool, IsEnabled);
            PROPERTY_R(bool, IsPreview);

            property Platform::String
                ^ AutomationId { Platform::String ^ get() { return m_Mode.ToString(); } }


            // For saving/restoring last mode used.
            static int Serialize(ViewMode mode);
            static ViewMode Deserialize(Platform::Object ^ obj);
            static ViewMode GetViewModeForFriendlyName(Platform::String ^ name);

            static bool IsValidViewMode(ViewMode mode);
            static bool IsCalculatorViewMode(ViewMode mode);
            static bool IsGraphingCalculatorViewMode(ViewMode mode);
            static bool IsDateCalculatorViewMode(ViewMode mode);
            static bool IsConverterViewMode(ViewMode mode);
            static bool IsViewModePreview(ViewMode mode);
            static Platform::String ^ GetFriendlyName(ViewMode mode);
            static Platform::String ^ GetNameResourceKey(ViewMode mode);
            static CategoryGroupType GetGroupType(ViewMode mode);

            // GetIndex is 0-based, GetPosition is 1-based
            static int GetIndex(ViewMode mode);
            static int GetFlatIndex(ViewMode mode);
            static int GetIndexInGroup(ViewMode mode, CategoryGroupType type);
            static int GetPosition(ViewMode mode);

            static ViewMode GetViewModeForVirtualKey(MyVirtualKey virtualKey);

            internal : NavCategory(
                           Platform::String ^ name,
                           Platform::String ^ automationName,
                           Platform::String ^ glyph,
                           Platform::String ^ accessKey,
                           Platform::String ^ mode,
                           ViewMode viewMode,
                           bool supportsNegative,
                           bool isEnabled,
                           bool isPreview)
                : m_Name(name)
                , m_AutomationName(automationName)
                , m_Glyph(glyph)
                , m_AccessKey(accessKey)
                , m_modeString(mode)
                , m_Mode(viewMode)
                , m_SupportsNegative(supportsNegative)
                , m_IsEnabled(isEnabled)
                , m_IsPreview(isPreview)
            {
            }

            static std::vector<MyVirtualKey> GetCategoryAcceleratorKeys();

        private:
            static bool IsModeInCategoryGroup(ViewMode mode, CategoryGroupType groupType);

            Platform::String ^ m_modeString;
        };

        [Windows::UI::Xaml::Data::Bindable] public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
        {
        public:
            OBSERVABLE_OBJECT();
            OBSERVABLE_PROPERTY_R(Platform::String ^, Name);
            OBSERVABLE_PROPERTY_R(Platform::String ^, AutomationName);
            OBSERVABLE_PROPERTY_R(CategoryGroupType, GroupType);
            OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<NavCategory ^> ^, Categories);

            static Windows::Foundation::Collections::IObservableVector<NavCategoryGroup ^> ^ CreateMenuOptions();

            internal : static NavCategoryGroup ^ CreateCalculatorCategory();
            static NavCategoryGroup ^ CreateConverterCategory();

        private:
            NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer);
        };
    }
}
