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

//
// DateCalculator.xaml.cpp
// Implementation of the DateCalculator class
//

#include "pch.h"
#include "DateCalculator.xaml.h"
#include "CalcViewModel/Common/TraceLogger.h"
#include "CalcViewModel/Common/CopyPasteManager.h"
#include "CalcViewModel/Common/LocalizationSettings.h"
#include "CalcViewModel/DateCalculatorViewModel.h"

using namespace CalculatorApp;
using namespace CalculatorApp::Common;
using namespace CalculatorApp::ViewModel;

using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Globalization;
using namespace Windows::Globalization::DateTimeFormatting;
using namespace Windows::System::UserProfile;
using namespace Windows::UI::Core;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Automation;
using namespace Windows::UI::Xaml::Automation::Peers;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;

// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236

DateCalculator::DateCalculator()
{
    InitializeComponent();

    const auto& localizationSettings = LocalizationSettings::GetInstance();

    // Set Calendar Identifier
    DateDiff_FromDate->CalendarIdentifier = localizationSettings.GetCalendarIdentifier();
    DateDiff_ToDate->CalendarIdentifier = localizationSettings.GetCalendarIdentifier();

    // Setting the FirstDayofWeek
    DateDiff_FromDate->FirstDayOfWeek = localizationSettings.GetFirstDayOfWeek();
    DateDiff_ToDate->FirstDayOfWeek = localizationSettings.GetFirstDayOfWeek();

    // Setting the Language explicitly is not required,
    // this is a workaround for the bug in the control due to which
    // the displayed date is incorrect for non Gregorian Calendar Systems
    // The displayed date doesn't honor the shortdate format, on setting the Language the format is refreshed
    DateDiff_FromDate->Language = localizationSettings.GetLocaleName();
    DateDiff_ToDate->Language = localizationSettings.GetLocaleName();

    // Set Min and Max Dates according to the Gregorian Calendar(1601 & 9999)
    auto calendar = ref new Calendar();
    auto today = calendar->GetDateTime();

    calendar->ChangeCalendarSystem(CalendarIdentifiers::Gregorian);
    calendar->Day = 1;
    calendar->Month = 1;
    calendar->Year = c_minYear;
    auto minYear = calendar->GetDateTime(); // 1st January, 1601
    DateDiff_FromDate->MinDate = minYear;
    DateDiff_ToDate->MinDate = minYear;

    calendar->Day = 31;
    calendar->Month = 12;
    calendar->Year = c_maxYear;
    auto maxYear = calendar->GetDateTime(); // 31st December, 9878
    DateDiff_FromDate->MaxDate = maxYear;
    DateDiff_ToDate->MaxDate = maxYear;

    // Set the PlaceHolderText for CalendarDatePicker
    DateTimeFormatter^ dateTimeFormatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDateTimeFormatter(
        L"day month year",
        localizationSettings.GetCalendarIdentifier(),
        ClockIdentifiers::TwentyFourHour); // Clock Identifier is not used

    DateDiff_FromDate->DateFormat = L"day month year";
    DateDiff_ToDate->DateFormat = L"day month year";

    auto placeholderText = dateTimeFormatter->Format(today);

    DateDiff_FromDate->PlaceholderText = placeholderText;
    DateDiff_ToDate->PlaceholderText = placeholderText;

    CopyMenuItem->Text = AppResourceProvider::GetInstance().GetResourceString(L"copyMenuItem");
    m_dateCalcOptionChangedEventToken = DateCalculationOption->SelectionChanged +=
        ref new SelectionChangedEventHandler(this, &DateCalculator::DateCalcOption_Changed);
}

void DateCalculator::FromDate_DateChanged(_In_ CalendarDatePicker ^ sender, _In_ CalendarDatePickerDateChangedEventArgs ^ e)
{
    if (e->NewDate != nullptr)
    {
        auto dateCalcViewModel = safe_cast<DateCalculatorViewModel ^>(this->DataContext);
        dateCalcViewModel->FromDate = e->NewDate->Value;
        TraceLogger::GetInstance().LogDateCalculationModeUsed(false /* AddSubtractMode */);
    }
    else
    {
        ReselectCalendarDate(sender, e->OldDate->Value);
    }
}

void DateCalculator::ToDate_DateChanged(_In_ CalendarDatePicker ^ sender, _In_ CalendarDatePickerDateChangedEventArgs ^ e)
{
    if (e->NewDate != nullptr)
    {
        auto dateCalcViewModel = safe_cast<DateCalculatorViewModel ^>(this->DataContext);
        dateCalcViewModel->ToDate = e->NewDate->Value;
        TraceLogger::GetInstance().LogDateCalculationModeUsed(false /* AddSubtractMode */);
    }
    else
    {
        ReselectCalendarDate(sender, e->OldDate->Value);
    }
}

void DateCalculator::AddSubtract_DateChanged(_In_ CalendarDatePicker ^ sender, _In_ CalendarDatePickerDateChangedEventArgs ^ e)
{
    if (e->NewDate != nullptr)
    {
        auto dateCalcViewModel = safe_cast<DateCalculatorViewModel ^>(this->DataContext);
        dateCalcViewModel->StartDate = e->NewDate->Value;
        TraceLogger::GetInstance().LogDateCalculationModeUsed(true /* AddSubtractMode */);
    }
    else
    {
        ReselectCalendarDate(sender, e->OldDate->Value);
    }
}

void CalculatorApp::DateCalculator::OffsetValue_Changed(_In_ Platform::Object ^ sender, _In_ SelectionChangedEventArgs ^ e)
{
    auto dateCalcViewModel = safe_cast<DateCalculatorViewModel ^>(this->DataContext);
    // do not log diagnostics for no-ops and initialization of combo boxes
    if (dateCalcViewModel->DaysOffset != 0 || dateCalcViewModel->MonthsOffset != 0 || dateCalcViewModel->YearsOffset != 0)
    {
        TraceLogger::GetInstance().LogDateCalculationModeUsed(true /* AddSubtractMode */);
    }
}

void DateCalculator::OnCopyMenuItemClicked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e)
{
    auto calcResult = safe_cast<TextBlock ^>(ResultsContextMenu->Target);

    CopyPasteManager::CopyToClipboard(calcResult->Text);
}

void CalculatorApp::DateCalculator::OnLoaded(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e)
{
}

void DateCalculator::CloseCalendarFlyout()
{
    if (DateDiff_FromDate->IsCalendarOpen)
    {
        DateDiff_FromDate->IsCalendarOpen = false;
    }

    if (DateDiff_ToDate->IsCalendarOpen)
    {
        DateDiff_ToDate->IsCalendarOpen = false;
    }

    if ((AddSubtract_FromDate != nullptr) && (AddSubtract_FromDate->IsCalendarOpen))
    {
        AddSubtract_FromDate->IsCalendarOpen = false;
    }
}

void DateCalculator::SetDefaultFocus()
{
    DateCalculationOption->Focus(::FocusState::Programmatic);
}

void DateCalculator::DateCalcOption_Changed(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::Controls::SelectionChangedEventArgs ^ e)
{
    FindName("AddSubtractDateGrid");
    DateCalculationOption->SelectionChanged -= m_dateCalcOptionChangedEventToken;
}

void CalculatorApp::DateCalculator::AddSubtractDateGrid_Loaded(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e)
{
    const auto& localizationSettings = LocalizationSettings::GetInstance();

    AddSubtract_FromDate->PlaceholderText = DateDiff_FromDate->PlaceholderText;
    AddSubtract_FromDate->CalendarIdentifier = localizationSettings.GetCalendarIdentifier();
    AddSubtract_FromDate->FirstDayOfWeek = localizationSettings.GetFirstDayOfWeek();
    AddSubtract_FromDate->Language = localizationSettings.GetLocaleName();

    AddSubtract_FromDate->MinDate = DateDiff_FromDate->MinDate;
    AddSubtract_FromDate->MaxDate = DateDiff_FromDate->MaxDate;
    AddSubtract_FromDate->DateFormat = L"day month year";
}

void DateCalculator::ReselectCalendarDate(_In_ Windows::UI::Xaml::Controls::CalendarDatePicker ^ calendarDatePicker, Windows::Foundation::DateTime dateTime)
{
    // Reselect the unselected Date
    calendarDatePicker->Date = ref new Box<DateTime>(dateTime);

    // Dismiss the Calendar flyout
    calendarDatePicker->IsCalendarOpen = false;
}

void DateCalculator::OffsetDropDownClosed(_In_ Object ^ sender, _In_ Object ^ e)
{
    RaiseLiveRegionChangedAutomationEvent(/* DateDiff mode */ false);
}

void DateCalculator::CalendarFlyoutClosed(_In_ Object ^ sender, _In_ Object ^ e)
{
    auto dateCalcViewModel = safe_cast<DateCalculatorViewModel ^>(this->DataContext);
    RaiseLiveRegionChangedAutomationEvent(dateCalcViewModel->IsDateDiffMode);
}

void DateCalculator::RaiseLiveRegionChangedAutomationEvent(_In_ bool isDateDiffMode)
{
    TextBlock ^ resultTextBlock = (isDateDiffMode ? DateDiffAllUnitsResultLabel : DateResultLabel);
    String ^ automationName = AutomationProperties::GetName(resultTextBlock);
    TextBlockAutomationPeer::FromElement(resultTextBlock)->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged);
}

void DateCalculator::AddSubtractOption_Checked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e)
{
    RaiseLiveRegionChangedAutomationEvent(/* DateDiff mode */ false);
}

void CalculatorApp::DateCalculator::OnVisualStateChanged(Platform::Object ^ sender, Windows::UI::Xaml::VisualStateChangedEventArgs ^ e)
{
    auto state = std::wstring(e->NewState->Name->Begin());
    TraceLogger::GetInstance().LogVisualStateChanged(ViewMode::Date, state);
}
