/**************************************************************************
* 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 "MainPage.xaml.h"
#include "SmartPanel.xaml.h"
#include "MessageTextPage.xaml.h"

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

using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Documents;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml::Media::Animation;
using namespace Windows::UI::Xaml::Shapes;
using namespace Windows::UI::Popups;
using namespace Windows::ApplicationModel::DataTransfer;
using namespace Windows::System::Threading;

// refacto : the message text page should be
MessageTextPage::MessageTextPage()
{
    InitializeComponent();

    /* connect to delegates */
    RingD::instance->incomingAccountMessage += ref new IncomingAccountMessage([&](String^ accountId, String^ fromRingId, String^ payload)
    {
        scrollDown();
    });
    RingD::instance->incomingMessage += ref new RingClientUWP::IncomingMessage(this, &RingClientUWP::Views::MessageTextPage::OnincomingMessage);

    RingD::instance->messageDataLoaded += ref new MessageDataLoaded([&]() { scrollDown(); });

    RingD::instance->messageStatusUpdated += ref new MessageStatusUpdated(this, &MessageTextPage::updateMessageStatus);

    RingD::instance->windowResized +=
        ref new WindowResized([=](float width, float height)
    {
        if (RingD::instance->mainPage) {
            bool isSmartPanelOpen = RingD::instance->mainPage->isSmartPanelOpen;
            if (width <= 728 && (width > 638 && isSmartPanelOpen)) {
                _contactButtonsGrid_->SetValue(Grid::ColumnSpanProperty, 3);
                _contactButtonsGrid_->SetValue(Grid::ColumnProperty, static_cast<Object^>(0));
                _contactButtonsGrid_->SetValue(Grid::RowProperty, 1);
                _contactButtonsGrid_->Margin = Windows::UI::Xaml::Thickness(6.0, 0.0, 14.0, 6.0);
                _contactButtonsGrid_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
            }
            else {
                _contactButtonsGrid_->SetValue(Grid::ColumnSpanProperty, 1);
                _contactButtonsGrid_->SetValue(Grid::ColumnProperty, 2);
                _contactButtonsGrid_->SetValue(Grid::RowProperty, static_cast<Object^>(0));
                _contactButtonsGrid_->Margin = Windows::UI::Xaml::Thickness(0.0, 0.0, 14.0, 0.0);
                _contactButtonsGrid_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Right;
            }
        }
    });

    RingD::instance->vCardUpdated += ref new VCardUpdated([&](Contact^ contact)
    {
        Utils::runOnUIThread([this, contact]() {
            if (auto item = SmartPanelItemsViewModel::instance->findItem(contact)) {
                updatePageContent(item);
            }
        });
    });

    lastMessageText = "";

    conversation_ = ref new Vector<ConversationMessage^>();
    converstationChunk_ = ref new Platform::Array<ConversationMessage^>(20);
}

void
MessageTextPage::updateMessageStatus(String^ messageId, int status)
{
    MSG_("message status update [id: " + messageId + ", status: " + status.ToString() + "]");
    if (status < 2)
        return;

    // One does not simply begin a storyboard nested in a datatemplated item.
    // We won't check the validity of the UIElement refs so an InvalidCast exception will be thrown
    // if the xaml gets changed without adjusting this messy workaround.
    auto items = _messagesList_->Items;
    for (int i = 0; i < items->Size; ++i) {
        if (auto message = dynamic_cast<ConversationMessage^>(items->GetAt(i))) {
            if (message->MessageId == messageId) {
                auto depObj = _messagesList_->ItemContainerGenerator->ContainerFromItem(items->GetAt(i));
                auto gridElement = Utils::xaml::FindVisualChildByName(depObj, "_confirmationCheckGrid_");
                auto grid = dynamic_cast<Grid^>(gridElement);
                auto rect = dynamic_cast<Rectangle^>(grid->Children->GetAt(0));
                auto eventTrigger = dynamic_cast<EventTrigger^>(rect->Triggers->GetAt(0));
                auto beginStoryboard = dynamic_cast<BeginStoryboard^>(eventTrigger->Actions->GetAt(0));
                if (beginStoryboard) {
                    beginStoryboard->Storyboard->Begin();
                    grid->Visibility = VIS::Visible;
                }
            }
        }
    }
}

void
MessageTextPage::updatePageContent(SmartPanelItem^ item)
{
    if (auto selectedItem = (item ? item : SmartPanelItemsViewModel::instance->_selectedItem)) {
        auto contact = selectedItem->_contact;

        if (!contact) /* should never happen */
            return;

        /* show the name of contact on the page */
        auto firstChoice = contact->_bestName2;
        auto secondChoice = contact->_bestName3;
        _contactName_->Text = firstChoice;
        _tt_contactName_->Text = firstChoice;
        if (firstChoice == contact->ringID_) {
            _contactName2_->Visibility = VIS::Collapsed;
        }
        else {
            _contactName2_->Visibility = VIS::Visible;
            _contactName2_->Text = secondChoice;
            _tt_contactName2_->Text = secondChoice;
        }

        //_contactBarAvatar_->ImageSource = RingClientUWP::ResourceMananger::instance->imageFromRelativePath(contact->_avatarImage);
        if (contact->_avatarImage != " ") {
            auto avatarImageUri = ref new Windows::Foundation::Uri(contact->_avatarImage);
            _contactBarAvatar_->ImageSource = ref new BitmapImage(avatarImageUri);
            _defaultContactBarAvatarGrid_->Visibility = VIS::Collapsed;
            _contactBarAvatarGrid_->Visibility = VIS::Visible;
        }
        else {
            _defaultContactBarAvatarGrid_->Visibility = VIS::Visible;
            _contactBarAvatarGrid_->Visibility = VIS::Collapsed;
            _defaultAvatar_->Fill = contact->_avatarColorBrush;
            _defaultAvatarInitial_->Text = Utils::getUpperInitial(contact->_bestName2);
        }

        if (contact->_trustStatus == TrustStatus::INCOMING_CONTACT_REQUEST ||
            contact->_trustStatus == TrustStatus::CONTACT_REQUEST_SENT ||
            contact->_trustStatus == TrustStatus::UNKNOWN ||
            contact->_trustStatus == TrustStatus::INGNORED)
            _sendContactRequestBtn_->Visibility = VIS::Visible;
        else
            _sendContactRequestBtn_->Visibility = VIS::Collapsed;

        if (item == nullptr) {
            contact->_unreadMessages = 0;

            /* show messages */
            _messagesList_->ItemsSource = nullptr;
            _fadeInMessagesPageInfoStoryBoard_->Begin();
            Utils::runOnUIThreadDelayed(50, [this, contact]() {
                auto start = std::chrono::steady_clock::now();
                /*auto conversationSize = contact->_conversation->_messages->Size;
                if (conversationSize > 20) {
                    conversation_->Clear();
                    contact->_conversation->_messages->GetMany(conversationSize - 20, converstationChunk_);
                    for (auto message : converstationChunk_) {
                        conversation_->Append(message);
                    }
                    _messagesList_->ItemsSource = conversation_;
                }
                else */{
                    _messagesList_->ItemsSource = contact->_conversation->_messages;
                }
                _fadeInMessagesListStoryBoard_->Begin();
                _easeUpMessagesListStoryBoard_->Begin();
                scrollDown();
                auto end = std::chrono::steady_clock::now();
                auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
            });
        }
    }
}

void RingClientUWP::Views::MessageTextPage::scrollDown()
{
    _scrollView_->UpdateLayout();
    _scrollView_->ScrollToVerticalOffset(_scrollView_->ScrollableHeight);
}

void
RingClientUWP::Views::MessageTextPage::_sendBtn__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    sendMessage();
}

void
RingClientUWP::Views::MessageTextPage::sendMessage()
{
    if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
        auto contact = item->_contact;

        auto message = _messageTextBox_->Text;

        IBuffer^ buffUTF8 = CryptographicBuffer::ConvertStringToBinary(message, BinaryStringEncoding::Utf8);
        auto stdMessage = Utils::getData(buffUTF8);
        MSG_(stdMessage);

        if (!contact || message->IsEmpty())
            return;

        /* empty the textbox */
        _messageTextBox_->Text = "";

        RingD::instance->sendAccountTextMessage(message);
        scrollDown();
    }
}

Object ^ RingClientUWP::Views::BubbleBackground::Convert(Object ^ value, Windows::UI::Xaml::Interop::TypeName targetType, Object ^ parameter, String ^ language)
{
    auto c1 = Utils::ColorFromString("#ffebefef");
    auto c2 = Utils::ColorFromString("#ffcfebf5");
    return ((bool)value) ? ref new SolidColorBrush(c1) : ref new SolidColorBrush(c2);
}

// we only do OneWay so the next function is not used
Object ^ RingClientUWP::Views::BubbleBackground::ConvertBack(Object ^ value, Windows::UI::Xaml::Interop::TypeName targetType, Object ^ parameter, String ^ language)
{
    throw ref new Platform::NotImplementedException();
}

RingClientUWP::Views::BubbleBackground::BubbleBackground()
{}

Object ^ RingClientUWP::Views::BubbleHorizontalAlignement::Convert(Object ^ value, Windows::UI::Xaml::Interop::TypeName targetType, Object ^ parameter, String ^ language)
{
    return ((bool)value) ? Windows::UI::Xaml::HorizontalAlignment::Left : Windows::UI::Xaml::HorizontalAlignment::Right;
}

// we only do OneWay so the next function is not used
Object ^ RingClientUWP::Views::BubbleHorizontalAlignement::ConvertBack(Object ^ value, Windows::UI::Xaml::Interop::TypeName targetType, Object ^ parameter, String ^ language)
{
    throw ref new Platform::NotImplementedException();
}

RingClientUWP::Views::BubbleHorizontalAlignement::BubbleHorizontalAlignement()
{}


void RingClientUWP::Views::MessageTextPage::OnincomingMessage(Platform::String ^callId, Platform::String ^payload)
{
    scrollDown();
}


void RingClientUWP::Views::MessageTextPage::OnSelectionChanged(Platform::Object ^sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs ^e)
{
}

void RingClientUWP::Views::MessageTextPage::_clearConversationBtn__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
        auto contact = item->_contact;

        auto messageDialog = ref new MessageDialog("Are you sure you want to clear the conversation?", "Clear conversation");

        messageDialog->Commands->Append(ref new UICommand("Cancel", ref new UICommandInvokedHandler([this](IUICommand^ command)
        {})));
        messageDialog->Commands->Append(ref new UICommand("Clear", ref new UICommandInvokedHandler([contact](IUICommand^ command)
        {
            contact->_conversation->_messages->Clear();
            contact->saveConversationToFile();
        })));

        messageDialog->DefaultCommandIndex = 1;

        messageDialog->ShowAsync();
    }
}


void RingClientUWP::Views::MessageTextPage::_audioCallBtn__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    auto button = dynamic_cast<Button^>(e->OriginalSource);
    if (button) {
        if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
            auto contact = item->_contact;
            if (contact)
                RingD::instance->placeCall(contact);
        }
    }
}


void RingClientUWP::Views::MessageTextPage::_videoCallBtn__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    auto button = dynamic_cast<Button^>(e->OriginalSource);
    if (button) {
        if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
            auto contact = item->_contact;
            if (contact)
                RingD::instance->placeCall(contact);
        }
    }
}

void
MessageTextPage::_messageTextBox__TextChanged(Object^ sender, TextChangedEventArgs^ e)
{
    bool carriageReturnPressed = false;
    if (_messageTextBox_->Text->Length() == (lastMessageText->Length() + 1) &&
        _messageTextBox_->Text != "\r") {
        unsigned cursorPos = 0;
        auto strMessage = Utils::toString(_messageTextBox_->Text);
        auto strLastMessage = Utils::toString(lastMessageText);
        for (std::string::size_type i = 0; i < strLastMessage.size(); ++i) {
            if (strMessage[i] != strLastMessage[i]) {
                auto changed = strMessage.substr(i, 1);
                if (changed == "\r") {
                    carriageReturnPressed = true;
                }
                break;
            }
        }

        if (strMessage.substr(strMessage.length() - 1) == "\r") {
            if (lastMessageText->Length() != 0) {
                carriageReturnPressed = true;
            }
        }
    }

    if (carriageReturnPressed && !(RingD::instance->isCtrlPressed || RingD::instance->isShiftPressed)) {
        _messageTextBox_->Text = lastMessageText;
        sendMessage();
        lastMessageText = "";
    }

    DependencyObject^ child = VisualTreeHelper::GetChild(_messageTextBox_, 0);
    auto sendBtnElement = Utils::xaml::FindVisualChildByName(child, "_sendBtn_");
    auto sendBtn = dynamic_cast<Button^>(sendBtnElement);
    if (_messageTextBox_->Text != "") {
        sendBtn->IsEnabled = true;
    }
    else {
        sendBtn->IsEnabled = false;
    }

    lastMessageText = _messageTextBox_->Text;
}

void
MessageTextPage::_messageTextBox__KeyDown(Object^ sender, KeyRoutedEventArgs^ e)
{
}

void
MessageTextPage::_sendContactRequestBtn__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
        if (auto contact = item->_contact) {
            auto accountId = Utils::toString(AccountListItemsViewModel::instance->getSelectedAccountId());
            auto uri = Utils::toString(contact->ringID_);
            auto vcard = Configuration::UserPreferences::instance->getVCard();
            RingD::instance->sendContactRequest(accountId, uri, vcard->asString());
        }
    }
}