/**************************************************************************
* Copyright (C) 2016 by Savoir-faire Linux                                *
* Author: Jäger Nicolas <nicolas.jager@savoirfairelinux.com>              *
* Author: Traczyk Andreas <andreas.traczyk@savoirfairelinux.com>          *
*                                                                         *
* This program is free software; you can redistribute it and/or modify    *
* it under the terms of the GNU General Public License as published by    *
* the Free Software Foundation; either version 3 of the License, or       *
* (at your option) any later version.                                     *
*                                                                         *
* This program is distributed in the hope that it will be useful,         *
* but WITHOUT ANY WARRANTY; without even the implied warranty of          *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
* GNU General Public License for more details.                            *
*                                                                         *
* You should have received a copy of the GNU General Public License       *
* along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
**************************************************************************/
#include "pch.h"

#include "ContactListModel.h"
#include "MessageTextPage.xaml.h"
#include "SmartPanel.xaml.h"
#include "VideoPage.xaml.h"
#include "PreviewPage.xaml.h"
#include "WelcomePage.xaml.h"
#include "AboutPage.xaml.h"

#include "MainPage.xaml.h"

using namespace RingClientUWP;
using namespace RingClientUWP::Views;
using namespace RingClientUWP::ViewModel;

using namespace Platform;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::ViewManagement;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::Graphics::Display;
using namespace Windows::System;
using namespace Concurrency;
using namespace Windows::System::Threading;

MainPage::MainPage()
{
    InitializeComponent();

    UserModel::instance->getUserData();

    Window::Current->SizeChanged += ref new WindowSizeChangedEventHandler(this, &MainPage::OnResize);

    _welcomeFrame_->Navigate(TypeName(RingClientUWP::Views::WelcomePage::typeid));
    _smartPanel_->Navigate(TypeName(RingClientUWP::Views::SmartPanel::typeid));
    _videoFrame_->Navigate(TypeName(RingClientUWP::Views::VideoPage::typeid));
    _previewFrame_->Navigate(TypeName(RingClientUWP::Views::PreviewPage::typeid));
    _messageTextFrame_->Navigate(TypeName(RingClientUWP::Views::MessageTextPage::typeid));

    isSmartPanelOpen = true;

    /* connect to delegates */
    RingD::instance->stateChange += ref new RingClientUWP::StateChange(this, &RingClientUWP::MainPage::OnstateChange);
    auto smartPanel = dynamic_cast<SmartPanel^>(_smartPanel_->Content);
    smartPanel->summonMessageTextPage += ref new RingClientUWP::SummonMessageTextPage(this, &RingClientUWP::MainPage::OnsummonMessageTextPage);
    smartPanel->summonWelcomePage += ref new RingClientUWP::SummonWelcomePage(this, &RingClientUWP::MainPage::OnsummonWelcomePage);
    smartPanel->summonPreviewPage += ref new RingClientUWP::SummonPreviewPage(this, &RingClientUWP::MainPage::OnsummonPreviewPage);
    smartPanel->hidePreviewPage += ref new RingClientUWP::HidePreviewPage(this, &RingClientUWP::MainPage::OnhidePreviewPage);
    smartPanel->summonVideoPage += ref new RingClientUWP::SummonVideoPage(this, &RingClientUWP::MainPage::OnsummonVideoPage);

    auto videoPage = dynamic_cast<VideoPage^>(_videoFrame_->Content);
    videoPage->pressHangUpCall += ref new RingClientUWP::PressHangUpCall(this, &RingClientUWP::MainPage::OnpressHangUpCall);

    auto messageTextFrame = dynamic_cast<MessageTextPage^>(_messageTextFrame_->Content);
    messageTextFrame->closeMessageTextPage += ref new RingClientUWP::CloseMessageTextPage(this, &RingClientUWP::MainPage::OncloseMessageTextPage);

    dpiChangedtoken = (DisplayInformation::GetForCurrentView()->DpiChanged += ref new TypedEventHandler<DisplayInformation^,
                       Platform::Object^>(this, &MainPage::DisplayProperties_DpiChanged));

    visibilityChangedEventToken = Window::Current->VisibilityChanged +=
                                      ref new WindowVisibilityChangedEventHandler(this, &MainPage::Application_VisibilityChanged);

    applicationSuspendingEventToken = Application::Current->Suspending +=
                                          ref new SuspendingEventHandler(this, &MainPage::Application_Suspending);
    applicationResumingEventToken = Application::Current->Resuming +=
                                        ref new EventHandler<Object^>(this, &MainPage::Application_Resuming);

    RingD::instance->registrationStateErrorGeneric += ref new RingClientUWP::RegistrationStateErrorGeneric(this, &RingClientUWP::MainPage::OnregistrationStateErrorGeneric);
    RingD::instance->registrationStateRegistered += ref new RingClientUWP::RegistrationStateRegistered(this, &RingClientUWP::MainPage::OnregistrationStateRegistered);
    RingD::instance->registrationStateUnregistered += ref new RingClientUWP::RegistrationStateUnregistered(this, &RingClientUWP::MainPage::OnregistrationStateUnregistered);
    RingD::instance->callPlaced += ref new RingClientUWP::CallPlaced(this, &RingClientUWP::MainPage::OncallPlaced);

    RingD::instance->setOverlayStatusText += ref new SetOverlayStatusText(this, &MainPage::OnsetOverlayStatusText);

    RingD::instance->fullScreenToggled += ref new RingClientUWP::FullScreenToggled(this, &RingClientUWP::MainPage::OnFullScreenToggled);

    Window::Current->CoreWindow->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(
        [this](CoreWindow^ c, KeyEventArgs^ e)
    {
        auto ctrlState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::Control);
        RingD::instance->isCtrlPressed = (ctrlState & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down;
        auto shiftState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::LeftShift);
        RingD::instance->isShiftPressed = (shiftState & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down;
        //MSG_("KEYDOWN -- isCtrlDown: " + isCtrlPressed.ToString() + " isShiftDown: " + isShiftPressed.ToString());
    });

    Window::Current->CoreWindow->KeyUp += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(
        [this](CoreWindow^ c, KeyEventArgs^ e)
    {
        auto ctrlState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::Control);
        RingD::instance->isCtrlPressed = (ctrlState & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down;
        auto shiftState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::LeftShift);
        RingD::instance->isShiftPressed = (shiftState & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down;
        //MSG_("KEYUP -- isCtrlDown: " + isCtrlPressed.ToString() + " isShiftDown: " + isShiftPressed.ToString());
    });

}

void
MainPage::OnsetOverlayStatusText(String^ statusText, String^ color)
{
    _loadingStatus_->Text = statusText;
    auto col = Utils::ColorFromString(color);
    auto brush = ref new Windows::UI::Xaml::Media::SolidColorBrush(col);
    _loadingStatus_->Foreground = brush;
}

void
MainPage::focusOnMessagingTextbox()
{
    auto messageTextPage = dynamic_cast<MessageTextPage^>(_messageTextFrame_->Content);
    auto messageTextBox = dynamic_cast<TextBox^>(messageTextPage->FindName("_messageTextBox_"));
    messageTextBox->Focus(Windows::UI::Xaml::FocusState::Programmatic);
}

void
MainPage::OnKeyDown(KeyRoutedEventArgs^ e)
{
    if (DEBUG_ON)
        if (e->Key == VirtualKey::F5) {
            _outerSplitView_->OpenPaneLength = Window::Current->Bounds.Width;
            _outerSplitView_->IsPaneOpen = !_outerSplitView_->IsPaneOpen;
        }
}

void
MainPage::_toggleSmartBoxButton__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    if (Window::Current->Bounds.Width <= 638)
        return;

    isSmartPanelOpen = !isSmartPanelOpen;
    setSmartPanelState(isSmartPanelOpen);

    RingD::instance->raiseWindowResized(Window::Current->Bounds.Width, Window::Current->Bounds.Height);
}

void
MainPage::setSmartPanelState(bool open)
{
    if (_innerSplitView_->IsPaneOpen != open) {
        _innerSplitView_->IsPaneOpen = open;
        auto newMode = open ? Views::SmartPanel::Mode::Normal : Views::SmartPanel::Mode::Minimized;
        dynamic_cast<SmartPanel^>(_smartPanel_->Content)->setMode(newMode);
    }
}

void
RingClientUWP::MainPage::showFrame(Windows::UI::Xaml::Controls::Frame^ frame)
{
    _navGrid_->SetRow(_welcomeFrame_, 0);
    _navGrid_->SetRow(_messageTextFrame_, 0);
    _navGrid_->SetRow(_videoFrame_, 0);

    if (frame == _welcomeFrame_) {
        _currentFrame = FrameOpen::WELCOME;
        _navGrid_->SetRow(_welcomeFrame_, 1);
    } else if (frame == _videoFrame_) {
        _currentFrame = FrameOpen::VIDEO;
        _navGrid_->SetRow(_videoFrame_, 1);
    } else if (frame == _messageTextFrame_) {
        _currentFrame = FrameOpen::MESSAGE;
        _navGrid_->SetRow(_messageTextFrame_, 1);
    }

}

void
RingClientUWP::MainPage::OnNavigatedTo(NavigationEventArgs ^ e)
{
    bool fromAboutPage = (e->Parameter != nullptr) ? safe_cast<bool>(e->Parameter) : false;
    if (!fromAboutPage) {
        RingD::instance->init();
        showLoadingOverlay(true, false);
    }
}

void
RingClientUWP::MainPage::showLoadingOverlay(bool load, bool modal)
{
    if (!isLoading && load) {
        isLoading = true;
        _loadingOverlay_->Visibility = Windows::UI::Xaml::Visibility::Visible;
        if (modal) {
            _fadeInModalStoryboard_->Begin();
            auto blackBrush = ref new Windows::UI::Xaml::Media::SolidColorBrush(Windows::UI::Colors::Black);
            _loadingOverlayRect_->Fill = blackBrush;
        }
        else {
            auto whiteBrush = ref new Windows::UI::Xaml::Media::SolidColorBrush(Windows::UI::Colors::White);
            _loadingOverlayRect_->Fill = whiteBrush;
            _loadingOverlayRect_->Opacity = 1.0;
            _loadingOverlay_->Opacity = 1.0;
        }
    }
    else if (!load && isLoading) {
        isLoading = false;
        _fadeOutStoryboard_->Begin();
    }
}

void
MainPage::OnResize(Platform::Object^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ e)
{
    if (e) {
        auto windowWidth = e->Size.Width;
        auto windowHeight = e->Size.Height;
        RingD::instance->raiseWindowResized(windowWidth, windowHeight);

        if (windowWidth <= 638) {
            setSmartPanelState(false);
            return;
        }

        if (isSmartPanelOpen && !RingD::instance->isFullScreen) {
            setSmartPanelState(true);
        }
    }
}

void
RingClientUWP::MainPage::DisplayProperties_DpiChanged(DisplayInformation^ sender, Platform::Object^ args)
{
    OnResize(nullptr, nullptr);
    SmartPanelItemsViewModel::instance->update({ "" });
    ContactRequestItemsViewModel::instance->update({ "" });
    AccountListItemsViewModel::instance->update({ "" });
}

void
RingClientUWP::MainPage::hideLoadingOverlay()
{
    _loadingOverlay_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}

void
RingClientUWP::MainPage::OnsummonMessageTextPage()
{
    updateMessageTextPage(nullptr);
    showFrame(_messageTextFrame_);
}

void
RingClientUWP::MainPage::updateMessageTextPage(SmartPanelItem^ item)
{
    auto messageTextPage = dynamic_cast<MessageTextPage^>(_messageTextFrame_->Content);
    messageTextPage->updatePageContent(item);
}

void RingClientUWP::MainPage::OnsummonWelcomePage()
{
    showFrame(_welcomeFrame_);
}

void RingClientUWP::MainPage::OnsummonPreviewPage()
{
    MSG_("Show Settings Preview");
    _previewFrame_->Visibility = VIS::Visible;
}

void RingClientUWP::MainPage::OnhidePreviewPage()
{
    MSG_("Hide Settings Preview");
    _previewFrame_->Visibility = VIS::Collapsed;
}

void RingClientUWP::MainPage::OnsummonVideoPage()
{
    auto item = SmartPanelItemsViewModel::instance->_selectedItem;
    auto videoPage = dynamic_cast<VideoPage^>(_videoFrame_->Content);

    if (item) {
        switch (item->_callStatus) {
        case CallStatus::IN_PROGRESS:
            videoPage->screenVideo(true);
            break;
        case CallStatus::PEER_PAUSED:
        case CallStatus::PAUSED:
            videoPage->screenVideo(false);
            break;
        }
    }

    videoPage->updatePageContent();
    showFrame(_videoFrame_);
}


void RingClientUWP::MainPage::OnpressHangUpCall()
{
}

void
MainPage::OnstateChange(Platform::String ^callId, RingClientUWP::CallStatus state, int code)
{
    auto item = SmartPanelItemsViewModel::instance->_selectedItem;

    switch (state) {
    /* send the user to the peer's message text page */
    case CallStatus::ENDED:
    {
        auto selectedItem = SmartPanelItemsViewModel::instance->_selectedItem;

        if (!selectedItem) {
            return;
        }

        if (item &&
            selectedItem->_callId == callId &&
            _currentFrame == FrameOpen::VIDEO) {
            OnsummonMessageTextPage();
            //showFrame(_messageTextFrame_);
        }
        break;
    }
    default:
        break;
    }
}

void
MainPage::Application_Suspending(Object^, Windows::ApplicationModel::SuspendingEventArgs^ e)
{
    /*MSG_("Application_Suspending");
    auto deferral = e->SuspendingOperation->GetDeferral();
    Video::VideoManager::instance->captureManager()->CleanupCameraAsync();
    MSG_("Hang up calls...");
    RingD::instance->deinit();
    deferral->Complete();*/
}

void
MainPage::Application_VisibilityChanged(Object^ sender, VisibilityChangedEventArgs^ e)
{
    auto vcm = Video::VideoManager::instance->captureManager();
    if (e->Visible) {
        MSG_("->Visible");
        RingD::instance->isInvisible = false;
        bool isInCall = false;
        for (auto item : SmartPanelItemsViewModel::instance->itemsList) {
            if (item->_callId && item->_callStatus == CallStatus::IN_PROGRESS) {
                isInCall = true;
                break;
            }
        }
        if (isInCall) {
            vcm->InitializeCameraAsync(false);
            vcm->videoFrameCopyInvoker->Start();
        }
        else if (vcm->isSettingsPreviewing) {
            vcm->CleanupCameraAsync()
            .then([=](task<void> cleanupCameraTask) {
                try {
                    cleanupCameraTask.get();
                    CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(
                        CoreDispatcherPriority::High, ref new DispatchedHandler([=]()
                    {
                        vcm->InitializeCameraAsync(true);
                    }));
                }
                catch (Exception^ e) {
                    EXC_(e);
                }
            });
        }
    }
    else {
        MSG_("->Invisible");
        RingD::instance->isInvisible = true;
        bool isInCall = false;
        for (auto item : SmartPanelItemsViewModel::instance->itemsList) {
            if (item->_callId && item->_callStatus == CallStatus::IN_PROGRESS) {
                isInCall = true;
                RingD::instance->currentCallId = item->_callId;
                break;
            }
        }
        if (isInCall) {
            if (vcm->isPreviewing) {
                vcm->StopPreviewAsync();
                if (vcm->captureTaskTokenSource)
                    vcm->captureTaskTokenSource->cancel();
                vcm->videoFrameCopyInvoker->Stop();
            }
        }
        else if (vcm->isSettingsPreviewing) {
            vcm->StopPreviewAsync();
        }
    }
}

void MainPage::Application_Resuming(Object^, Object^)
{
    MSG_("Application_Resuming");
}

void RingClientUWP::MainPage::OncloseMessageTextPage()
{
    SmartPanelItemsViewModel::instance->_selectedItem = nullptr;
    showFrame(_welcomeFrame_);
}


void RingClientUWP::MainPage::OnregistrationStateErrorGeneric(const std::string& accountId)
{
}

void RingClientUWP::MainPage::OnregistrationStateUnregistered(const std::string& accountId)
{
    RingD::instance->volatileDetailsChanged += ref new RingClientUWP::VolatileDetailsChanged(this, &MainPage::OnvolatileDetailsChanged);
}

void RingClientUWP::MainPage::OnregistrationStateRegistered(const std::string& accountId)
{
    /* do not connect those delegates before initial registration on dht is fine.
       Otherwise your going to mess with the wizard */
    RingD::instance->nameRegistered += ref new RingClientUWP::NameRegistered(this, &RingClientUWP::MainPage::OnnameRegistred);
    RingD::instance->volatileDetailsChanged += ref new RingClientUWP::VolatileDetailsChanged(this, &MainPage::OnvolatileDetailsChanged);
}

void RingClientUWP::MainPage::OncallPlaced(Platform::String ^callId)
{
}

void
MainPage::OnnameRegistred(bool status, String ^accountId)
{
    showLoadingOverlay(false, false);
}


void RingClientUWP::MainPage::OnvolatileDetailsChanged(const std::string &accountId, const std::map<std::string, std::string>& details)
{
    showLoadingOverlay(false, false);
}

void RingClientUWP::MainPage::OnFullScreenToggled(bool state)
{
    if (state == true) {
        _innerSplitView_->IsPaneOpen = false;
        _innerSplitView_->CompactPaneLength = 0;
    }
    else {
        _innerSplitView_->IsPaneOpen = isSmartPanelOpen;
        _innerSplitView_->CompactPaneLength = 60;
    }
}