// bytran -|- spectral calculations for portable devices using the HITRAN database
//
// Developed by Dzianis Pliutau (Denis Plutov) and Kostya Roslyakov
// www.bytran.org | bytran@bytran.org
// Copyright (c) 2016 Dzianis Pliutau
// Distributed under the MIT license
//
// Share results methods (Android and iOS) are based on the tutorial and code Copyrighted (c) 2014 Nicolas Froment
// http://blog.lasconic.com/share-on-ios-and-android-using-qml/
//
// The MIT License (MIT)
// Copyright (c) 2016 Dzianis Pliutau
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.



// Object linking the Data save QML component with the core calculation components
// This has to use platform specific implementations (Android or iOS etc.)
#include "shareresultsqml.h"
#include <QDesktopServices>
#include <QApplication>
#include <QMessageBox>
#include <QString>
#include <QStringList>
#include <QFileDialog>

// This is needed to enable sharing of data under Windows Phone
#ifdef Q_OS_WINPHONE
    // Windows phone related inclusions to later enable data and image sharing under the Windows phone
    #include <roapi.h>
    #include <vccorlib.h>
    #include <ppltasks.h>
    using namespace Platform;
    using namespace concurrency;
    using namespace Windows::ApplicationModel;
    using namespace Windows::ApplicationModel::DataTransfer;
    using namespace Windows::UI::Popups;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Navigation;
    using namespace Windows::UI::Core;
    using namespace Windows::Foundation;
    using namespace Windows::ApplicationModel::Core;
#endif

#ifdef Q_OS_ANDROID
    #include <QAndroidJniObject>
    #include <QAndroidJniEnvironment>
#endif

ShareResultsQml::ShareResultsQml(QObject *parent, BytranCore *inCore) : QObject(parent)
{
    // Passing a reference to the core object into the internal variable
    core = inCore;

    // Setting default parameters
    m_attachImage = true;
    m_attachDataFile = false;
    m_shareResults = false;

    // Method to create actions
    createActions();

    // Object to share results of the calculations on the iOS platform (iOS only)
    #if defined(Q_OS_IOS)
        iosShareDataAndText = new iosShare();
    #endif

    // Object to share the calculation results under the Windows Phone operating system
//    #if defined(Q_OS_WINPHONE)
//        shareWinPhone = ref new ShareWindows();
//    #endif

    // Save is not in progess at the beginning
    saveInProgress = false;
}

// Method to create actions
void ShareResultsQml::createActions()
{
    connect(this->core->getResultsSaver(), SIGNAL(savingFilesToDiskComplete()), this, SLOT(fileSavedToDisk()));
    connect(this->core->getResultsSaver(), SIGNAL(saveConfigToDocuments(QString)), this, SLOT(saveConfigForSharing(QString)));
}

// Slot to save the configuration file for sharing
void ShareResultsQml::saveConfigForSharing(QString path)
{
    QString savedPath;

    savedPath = this->core->getConfigIO()->getConfigFilePath();
    this->core->getConfigIO()->setConfigFilePath(path);
    this->core->getConfigIO()->saveToConfig();
    this->core->getConfigIO()->setConfigFilePath(savedPath);
}

// Informting the QML components that file writing is complete
void ShareResultsQml::fileSavedToDisk()
{
    // This is to notify the QML that file writing has finished
    emit fileWritingCompleteChanged();
    saveInProgress = false;
}

// Invocable method to save the results of the calculations to the Documents folder
void ShareResultsQml::saveToDocuments()
{
    QString message;

    if (saveInProgress == false)
    {
        saveInProgress = true;
        if (core->getHorizSpectra()->getOptDepth()->getY()->count() > 0)
        {
            #if defined(Q_OS_ANDROID)
                message = "Saving data and image\ncurrently displayed and\nnon-zero in current scale\n( > 1 vertical axis pixel )\ndate and time stamped\nto 'Documents/bytran'";
            #else
                message = "Saving data and image\ncurrently displayed and\nnon-zero in current scale\n( > 1 vertical axis pixel )\ndate and time stamped\nto the internal app folder";
            #endif

            core->displayNotification(message, 0, true, false);
            QApplication::processEvents();

            // Getting the file names and the top string generated
            core->getResultsSaver()->generateFilePathsAndTopString();

            // Setting the top string
            m_topString = core->getResultsSaver()->getTopString();
            emit topStringChanged();

            // Changing the path where an image is to be saved to (main plot)
            m_saveImage = core->getResultsSaver()->getImageFilePath();
            emit saveImageChanged();

            // Saving of the text file is carried out through a timer and a signal / slot connection
        }
        else
        {
            core->displayOkDialog("No calculation has been performed.\nThere are no data to save.\n");
        }
    }
}

// Share results methods are based on the tutorial and code Copyrighted (c) 2014 Nicolas Froment - http://blog.lasconic.com/share-on-ios-and-android-using-qml/
// Other resources were used as well but were less helpful:
// http://stackoverflow.com/questions/27094100/how-to-use-qandroidjniobject-to-call-java-code-from-inside-qt
// https://www.calligra.org/blogs/sharing-with-qt-on-android/
// http://www.kdab.com/qt-android-episode-5/
// http://stackoverflow.com/questions/2264622/android-multiple-email-attachment-using-intent-question
// https://amin-ahmadi.com/2015/12/07/how-to-mix-c-and-java-code-in-qt-for-android/
void ShareResultsQml::shareImageAndText()
{
    // Only initiating the share if the user has chosen for the sahring to be performed
    if (m_shareResults == true) {

        #if defined(Q_OS_IOS)  // Sharing the results under the iOS

            QString imagePath;
            QString textPath;

            // Setting the image path
            imagePath = "";
            if (this->m_attachImage) { imagePath = this->m_saveImage; }

            // Setting the text path
            textPath = "";
            if (this->m_attachDataFile) { textPath = core->getResultsSaver()->getTextFilePath(); }

            // Sharing the results of the calculations (image and text on the iOS platform)
            iosShareDataAndText->share(imagePath, textPath, core->getResultsSaver()->getConfigFilePath());

        #elif defined(Q_OS_ANDROID)  // Sharing the results under Android

            // Sharing image and data
            QAndroidJniObject jImagePath;
            QAndroidJniObject jTextPath;
            QAndroidJniObject jConfigPath;

            if (this->m_attachImage) { jImagePath = QAndroidJniObject::fromString(this->m_saveImage); }
                                else { jImagePath = QAndroidJniObject::fromString(""); }

            if (this->m_attachDataFile)
            {
                jTextPath = QAndroidJniObject::fromString(core->getResultsSaver()->getTextFilePath());
                jConfigPath = QAndroidJniObject::fromString(core->getResultsSaver()->getConfigFilePath());
            }
            else
            {
                jTextPath = QAndroidJniObject::fromString("");
                jConfigPath = QAndroidJniObject::fromString("");
            }

            QAndroidJniObject::callStaticMethod<void>(
                "org/bytran/bytran/ShareIntent",            // Class
                "share",                                    // Method name
                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",  // Method signature
                jImagePath.object<jstring>(),
                jTextPath.object<jstring>(),
                jConfigPath.object<jstring>()
            );

            QAndroidJniEnvironment env;
            if (env->ExceptionCheck()) {
                // Handle exception here.
                env->ExceptionClear();
            }

        #elif defined(Q_OS_WINPHONE)  // Sharing the results under Windows Phone (WinRT)

        // Have been inserted in the description
        // https://code.msdn.microsoft.com/windowsapps/Sharing-Content-Source-App-d9bffd84
        // https://forum.qt.io/topic/49752/native-calls-to-winrt-equivalent-to-qandroidjniobject/3
        // https://forum.qt.io/topic/72964/winrt-filepicker-and-cx/4
        // http://stackoverflow.com/questions/10579027/run-code-on-ui-thread-in-winrt
        // http://blog.qt.io/blog/2013/04/19/qts-winrt-port-and-its-ccx-usage/

        // Probably insert somwhere
        // https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples

        // Need to insert somewhere if this works
        // http://www.robwirving.com/2013/07/17/guide-to-getting-to-the-ui-thread-for-any-type-of-windows-phone-8-app/
        // http://www.robwirving.com/2014/06/02/getting-to-the-ui-thread-from-a-windows-phone-winrt-component/
        // https://github.com/robwirving/CppUIThreadApp

        // Maybe add somewhere
        // http://stackoverflow.com/questions/16477190/correct-way-to-get-the-coredispatcher-in-a-windows-store-app

        // Analyze code in this example, may be useful
        // http://answers.flyppdevportal.com/MVC/Post/Thread/7d87700c-f38e-4064-bb77-7be016f5c4dd?category=winappswithnativecode



        // Not sure needs to be inserted
        // https://docs.microsoft.com/en-us/windows/uwp/get-started/create-a-basic-windows-10-app-in-cpp
        // http://doc.qt.io/qt-5/activeqt-dotnet.html
        // https://forum.qt.io/topic/68670/how-can-i-use-winrt-c-apis-in-at-qt-winrt-app/2
        // https://msdn.microsoft.com/en-us/windows/uwp/app-to-app/share-data
        // http://doc.qt.io/qt-4.8/activeqt-dotnet.html
        // http://doc.qt.io/qt-4.8/activeqt.html#activeqt
        // http://programmers.stackexchange.com/questions/162168/what-are-c-cx-and-c-cli-and-how-do-they-relate-to-c-and-winrt
        // https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh871370.aspx
        // http://www.c-sharpcorner.com/UploadFile/99bb20/share-an-image-with-windows-store-apps-using-C-Sharp/
        // https://www.codeproject.com/Articles/42319/Using-NET-Classes-Modules-from-Native-C
        // https://stackoverflow.com/questions/778590/calling-c-sharp-code-from-c
        // http://stackoverflow.com/questions/13312806/how-to-create-a-modal-messagebox-in-winrt-using-native-c
        // https://social.msdn.microsoft.com/Forums/windowsapps/en-US/e307fd6b-3367-45f0-b32f-3d99d4738724/winrt-ccx-synchronous-messagedialog?forum=wpdevelop
        // https://github.com/Microsoft/Windows-universal-samples/blob/master/Samples/MessageDialog/cpp/CompletedCallback.xaml.cpp
        // http://stackoverflow.com/questions/27844508/issue-calling-something-on-the-ui-thread-from-inside-a-callback-in-c-cx
        // http://stackoverflow.com/questions/23607096/new-apis-for-windows-phone-8-1
        // https://github.com/qt-labs/weather-app/blob/master/src/main.cpp
        // https://github.com/qt-labs/weather-app
        // https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh871370.aspx
        // https://docs.microsoft.com/en-us/uwp/api/windows.ui.core.coredispatcher
        // https://social.msdn.microsoft.com/Forums/en-US/5a6b98b5-f8f1-4b8b-9191-2f17c7d18e6e/the-threadpoolrunasync-calls-work-items-asynchronously-but-in-the-same-thread?forum=winappswithnativecode
        // http://mikaelkoskinen.net/post/fixing-coredispatcher-invoke-how-to-invoke-method-in-ui-thread-in-windows-8-release-preview



        // Not very important
        // https://forum.qt.io/topic/64229/how-to-integrate-winrt-native-code-to-the-qt-application-for-windows-phone/9
        // https://msdn.microsoft.com/library/windows/apps/windows.ui.core.coredispatcher.runasync?cs-save-lang=1&cs-lang=cpp#code-snippet-3
        // https://programmium.wordpress.com/2014/02/21/how-to-share-files-in-windows-phone-8-1-using-c/
        // http://stackoverflow.com/questions/35749107/create-a-share-button-on-windows-phone-8-8-1-and-10-mobile
        // http://stackoverflow.com/questions/34203806/porting-qt-based-qml-app-to-windows-store-app
        // http://amin-ahmadi.com/2015/06/26/how-to-use-windows-api-in-qt-a-simple-example/
        // http://www.qtcentre.org/threads/29680-use-a-COM-(-or-a-Net-)-object-in-a-c-QT-application
        // https://bugreports.qt.io/browse/QTBUG-47724
        // http://stackoverflow.com/questions/26798335/windows-phone-8-1-app-crashes-on-showshareui
        // http://www.c-sharpcorner.com/UploadFile/99bb20/sharing-text-in-windows-8-apps-using-C-Sharp/
        // http://www.cplusplus.com/forum/windows/174883/
        // http://stackoverflow.com/questions/16488571/how-to-call-c-sharp-code-in-c
        // http://rutechnet.blob.core.windows.net/files/windows8csharp.pdf
        // http://microsoft.github.io/winsdkfb/sample/
        // https://stackoverflow.com/questions/38161549/sharing-a-link-from-uwp-app-to-facebook


            // The below commented lines are just random test performed.
            // Don't know how to call the C++/Cx ShowShareUI() correctly, not much information on this online right now
            // Some variants compile but crashes when the Windows Phone UI elements are called.
            // The problem seems to be that I am not running on the UI Thread and this is why the program terminates when the message dialog window is opened

            // RoInitialize(RO_INIT_MULTITHREADED); // WRT stuff initialization
            // dispatcher = Windows::UI::Core::CoreWindow->GetForCurrentThread()->Dispatcher;
            // CoreDispatcher dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;

            // Create the message dialog and set its content
//            MessageDialog^ msg = ref new MessageDialog("Message box is being shown.");
//            msg->ShowAsync();

            // Add the commands to the dialog, use ids for the commands instead of delegates
            // msg->Commands->Append(ref new UICommand("Don't install", nullptr, PropertyValue::CreateInt32(0)));
            // msg->Commands->Append(ref new UICommand("Install updates", nullptr, PropertyValue::CreateInt32(1)));

            // Set the command that will be invoked by default
            // msg->DefaultCommandIndex = 1;

            // Create task only causes a crash under a cell phone, maybe debug and try to pull the message box under Windows Desktop operating system RT, and then move over to Phone after
            // create_task(msg->ShowAsync()).then([this](IUICommand^ command)
            // {
            //      if (command->Id != nullptr)
            //      {
            //          // rootPage->NotifyUser("The '" + command->Label + "' (" +  command->Id + ") command has been selected.", NotifyType::StatusMessage);
            //      }
            //      else
            //      {
            //          // rootPage->NotifyUser("The dialog has now been closed", NotifyType::StatusMessage);
            //      }
            // });

            // Just creating an object causes a crash already, figure out how to create an object which is a Visual studio C++ object
            winPhoneShare = ref new WinRtShareTest();
            //winPhoneShare->RegisterForShare();
            winPhoneShare->ShowShareUI();

            // Forming a data request
            // DataRequest^ request = e->Request;
            // request->Data->Properties->Title = "Testing";
            // request->Data->Properties->Description = "Email with attachment";

            // http://stackoverflow.com/questions/10579027/run-code-on-ui-thread-in-winrt
            // DataTransferManager^ dataTransferManager = ref new DataTransferManager();

            // https://docs.microsoft.com/en-us/uwp/api/windows.ui.core.coredispatcher
            // https://social.msdn.microsoft.com/Forums/en-US/5a6b98b5-f8f1-4b8b-9191-2f17c7d18e6e/the-threadpoolrunasync-calls-work-items-asynchronously-but-in-the-same-thread?forum=winappswithnativecode

            // How to execute WinRT on a UI thread.
            // http://www.robwirving.com/2013/07/17/guide-to-getting-to-the-ui-thread-for-any-type-of-windows-phone-8-app/
            // http://www.robwirving.com/2014/06/02/getting-to-the-ui-thread-from-a-windows-phone-winrt-component/
            // https://github.com/robwirving/CppUIThreadApp
            // http://mikaelkoskinen.net/post/fixing-coredispatcher-invoke-how-to-invoke-method-in-ui-thread-in-windows-8-release-preview
            // http://stackoverflow.com/questions/16477190/correct-way-to-get-the-coredispatcher-in-a-windows-store-app


            // http://answers.flyppdevportal.com/MVC/Post/Thread/7d87700c-f38e-4064-bb77-7be016f5c4dd?category=winappswithnativecode

            // This codee does not crash but doesn't do anything either
            // CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::Normal,
            // CoreWindow::GetForCurrentThread()->Dispatcher->RunAsync(CoreDispatcherPriority::Normal,
            //                                                        ref new DispatchedHandler([]()
            //                                                        {
            //     // Initializing the data transfer manager
            //     DataTransferManager^ dataTransferManager = DataTransferManager::GetForCurrentView();
            //     dataTransferManager->ShowShareUI();

                 // Your UI update code goes here!
                 // Windows::ApplicationModel::DataTransfer::DataTransferManager::ShowShareUI();

            // }));

            // Windows::ApplicationModel::DataTransfer::DataTransferManager::ShowShareUI();  // Figure out how to use this function here in Qt
            // DataTransferManager^ dataTransferManager = DataTransferManager::GetForCurrentView();
            // DataTransferManager::ShowShareUI();
            // dataTransferManager::DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.ShareTextHandler);

            // create_task(Windows::ApplicationModel::DataTransfer::DataTransferManager::ShowShareUI())
            // {
            //      try{}
            //      catch{}
            // }

        #elif defined(Q_OS_WINRT)

        #else

        #endif
    }
}

// This method handler from Windows RT needs to be accessible somehow to enable sharing
// Maybe need to write a class and call it from Qt, need to learn more about this
// private void ShareTextHandler(DataTransferManager sender, DataRequestedEventArgs e)
// {
//      DataRequest request = e.Request;
//      request.Data.Properties.Title = "Share Text Example";
//      request.Data.Properties.Description = "A demonstration that shows how to share text.";
//      request.Data.SetText(ShareText.Text);
// }

// Methods to get and set the save image path
QString ShareResultsQml::saveImage() const
{
    return this->m_saveImage;
}
void ShareResultsQml::setSaveImage(const QString &saveImage)
{
    if (saveImage != m_saveImage)
    {
        this->m_saveImage = saveImage;
        emit saveImageChanged();
    }
}

// Methods to get and set the top string with the information about the total pressure, pathlegth and temperature
QString ShareResultsQml::topString() const { return this->m_topString; }
void ShareResultsQml::setTopString(const QString &topString)
{
    if (topString != m_topString)
    {
        m_topString = topString;
        emit topStringChanged();
    }
}

// Methods to get and set values indicating if an image file is to be attached to the share message
bool ShareResultsQml::attachImage() const { return this->m_attachImage; }
void ShareResultsQml::setAttachImage(const bool attachImage)
{
    if (attachImage != m_attachImage)
    {
        m_attachImage = attachImage;
        emit attachImageChanged();
    }
}

// Methods to get and set the values indicating if a data file is to be attached to the share message
bool ShareResultsQml::attachDataFile() const { return this->m_attachDataFile; }
void ShareResultsQml::setAttachDataFile(const bool attachDataFile)
{
    if (attachDataFile != m_attachDataFile)
    {
        m_attachDataFile = attachDataFile;
        emit attachDataFileChanged();
    }
}

// Method to set a variable indicating if the results need to be shared after they have been saved to the local storage
bool ShareResultsQml::shareResults() const { return this->m_shareResults; }
void ShareResultsQml::setShareResults(const bool &shareResults)
{
    if (shareResults != m_shareResults)
    {
        m_shareResults = shareResults;
        emit shareResultsChanged();
    }
}

// Method to expose the state of the data available valriable
bool ShareResultsQml::dataAvailable() const { return (core->getHorizSpectra()->getOptDepth()->getY()->count() > 0); }

// Methods to get and set the state when file writing is complete (this could be implemented in a different was as aprameter passing is not needed)
bool ShareResultsQml::fileWritingComplete() const { return this->m_fileWritingComplete; }
void ShareResultsQml::setFileWritingComplete(const bool &fileWritingComplete)
{
    if (fileWritingComplete != m_fileWritingComplete)
    {
        m_fileWritingComplete = fileWritingComplete;
        emit fileWritingCompleteChanged();
    }
}

// Method to let the user know that no data is available for saving
void ShareResultsQml::showNoDataAlert()
{
    core->displayOkDialog("No calculation has been performed.\nThere are no data to save.");
}

// Calling a function to test the winRT funcitonality
void ShareResultsQml::testCall()
{
//    MessageDialog^ msg = ref new MessageDialog("test message boc.");
//    msg->ShowAsync();

    //DataTransferManager^ dataTransferManager = DataTransferManager::GetForCurrentView();
    // dataTransferManager->ShowShareUI();

    //DataTransferManager::ShowShareUI();

    // or

    //    Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher->RunAsync(CoreDispatcherPriority::Normal,
    //                               ref new Windows::UI::Core::DispatchedHandler([this]()
    //    {
    //        //DataTransferManager::ShowShareUI();
    //        MessageDialog^ msg = ref new MessageDialog("Test message.");
    //        msg->ShowAsync();
    //    }));

}

// WinRT
// https://msdn.microsoft.com/en-us/windows/uwp/files/file-access-permissions
// https://msdn.microsoft.com/en-us/windows/uwp/files/file-access-permissions
// https://msdn.microsoft.com/en-us/windows/uwp/get-started/create-a-basic-windows-10-app-in-cpp
// http://www.informit.com/articles/article.aspx?p=1405551&seqNum=3
// http://stackoverflow.com/questions/11744602/qt-unmanaged-c-class-to-work-with-c-sharp-net
// http://stackoverflow.com/questions/13467701/windows-phone-8-media-file-access
// http://doc.qt.io/qt-4.8/activeqt-dotnet.html
// http://www.qtcentre.org/threads/65487-howto-use-ActiveQT-Dot-Net
// http://doc.qt.io/qt-4.8/activeqt-dotnet.html
// http://stackoverflow.com/questions/23340398/how-to-access-downloads-folder-in-windows-phone-8-1

// iOS
// https://developer.apple.com/library/content/documentation/Miscellaneous/Conceptual/iPhoneOSTechOverview/CoreServicesLayer/CoreServicesLayer.html#//apple_ref/doc/uid/TP40007898-CH10-SW30
// http://stackoverflow.com/questions/2709705/read-text-file-in-document-folder-iphone-sdk
// http://stackoverflow.com/questions/15219511/theres-a-way-to-access-the-document-folder-in-iphone-ipad-real-device-no-simu
// http://stackoverflow.com/questions/6029916/how-to-enable-file-sharing-for-my-app
// http://www.android-nougat.trade/2016/12/lg-k3-8269.html

