/*
 * Copyright (c) 2014-2016 Alex Spataru <alex_spataru@outlook.com>
 *
 * This file is part of the QSimpleUpdater library, which is released under
 * the DBAD license, you can read a copy of it below:
 *
 * DON'T BE A DICK PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING,
 * DISTRIBUTION AND MODIFICATION:
 *
 * Do whatever you like with the original work, just don't be a dick.
 * Being a dick includes - but is not limited to - the following instances:
 *
 * 1a. Outright copyright infringement - Don't just copy this and change the
 *     name.
 * 1b. Selling the unmodified original with no work done what-so-ever, that's
 *     REALLY being a dick.
 * 1c. Modifying the original work to contain hidden harmful content.
 *     That would make you a PROPER dick.
 *
 * If you become rich through modifications, related works/services, or
 * supporting the original work, share the love.
 * Only a dick would make loads off this work and not buy the original works
 * creator(s) a pint.
 *
 * Code is provided with no warranty. Using somebody else's code and bitching
 * when it goes wrong makes you a DONKEY dick.
 * Fix the problem yourself. A non-dick would submit the fix back.
 */

#include <QJsonValue>
#include <QJsonObject>
#include <QMessageBox>
#include <QApplication>
#include <QJsonDocument>
#include <QDesktopServices>

#include "Updater.h"
#include "Downloader.h"
#include "loappconfig.h"
#include "qsettings.h"
#include "LoUIClass/x_uimsgboxokcancel.h"
Updater::Updater()
{
    m_url = "";
    m_openUrl = "";
    m_changelog = "";
    m_downloadUrl = "";
    m_latestVersion = "";
    m_customAppcast = false;
    m_notifyOnUpdate = true;
    m_notifyOnFinish = false;
    m_updateAvailable = true;
    m_sameVersion=false;
    m_downloaderEnabled = true;
    m_noTipDownload=false;
    m_bSameStringCompare=false;
    m_moduleName = qApp->applicationName();
    m_moduleVersion = qApp->applicationVersion();
    m_mandatoryUpdate = false;

    m_downloader = new Downloader();
    m_manager = new QNetworkAccessManager();
    QNetworkReply *pReply = m_manager->get(QNetworkRequest(QUrl("https://www.ledok.cn/download/definitions/updates.json")));
    //QNetworkReply *pReply = m_manager->get(QNetworkRequest(QUrl("https://www.ledok.cn")));
    QReplyTimeout *pTimeout = new QReplyTimeout(pReply, 10000);
    // 超时进一步处理
    connect(pTimeout, &QReplyTimeout::timeout, [=]() {
        qDebug() << "Timeout";
    });
#if defined Q_OS_WIN
    m_platform = "windows";
#elif defined Q_OS_MAC
    m_platform = "osx";
#elif defined Q_OS_LINUX
    m_platform = "linux";
#elif defined Q_OS_ANDROID
    m_platform = "android";
#elif defined Q_OS_IOS
    m_platform = "ios";
#endif

    setUserAgentString (QString ("%1/%2 (Qt; QSimpleUpdater)").arg (qApp->applicationName(),
                        qApp->applicationVersion()));

    connect (m_downloader, SIGNAL (downloadFinished (QString, QString)),
             this,         SIGNAL (downloadFinished (QString, QString)));
    connect (m_manager,    SIGNAL (finished (QNetworkReply*)),
             this,           SLOT (onReply  (QNetworkReply*)));
}

Updater::~Updater()
{
    delete m_downloader;
}

/**
 * Returns the URL of the update definitions file
 */
QString Updater::url() const
{
    return m_url;
}

/**
 * Returns the URL that the update definitions file wants us to open in
 * a web browser.
 *
 * \warning You should call \c checkForUpdates() before using this functio
 */
QString Updater::openUrl() const
{
    return m_openUrl;
}

/**
 * Returns the changelog defined by the update definitions file.
 * \warning You should call \c checkForUpdates() before using this function
 */
QString Updater::changelog() const
{
    return m_changelog;
}

/**
 * Returns the name of the module (if defined)
 */
QString Updater::moduleName() const
{
    return m_moduleName;
}

/**
 * Returns the platform key (be it system-set or user-set).
 * If you do not define a platform key, the system will assign the following
 * platform key:
 *    - On iOS: \c ios
 *    - On Mac OSX: \c osx
 *    - On Android: \c android
 *    - On GNU/Linux: \c linux
 *    - On Microsoft Windows: \c windows
 */
QString Updater::platformKey() const
{
    return m_platform;
}

/**
 * Returns the download URL defined by the update definitions file.
 * \warning You should call \c checkForUpdates() before using this function
 */
QString Updater::downloadUrl() const
{
    return m_downloadUrl;
}

/**
 * Returns the latest version defined by the update definitions file.
 * \warning You should call \c checkForUpdates() before using this function
 */
QString Updater::latestVersion() const
{
    return m_latestVersion;
}

/**
 * Returns the user-agent header used by the client when communicating
 * with the server through HTTP
 */
QString Updater::userAgentString() const
{
    return m_userAgentString;
}

/**
 * Returns the "local" version of the installed module
 */
QString Updater::moduleVersion() const
{
    return m_moduleVersion;
}

/**
 * Returns \c true if the updater should NOT interpret the downloaded appcast.
 * This is useful if you need to store more variables (or information) in the
 * JSON file or use another appcast format (e.g. XML)
 */
bool Updater::customAppcast() const
{
    return m_customAppcast;
}

/**
 * Returns \c true if the updater should notify the user when an update is
 * available.
 */
bool Updater::notifyOnUpdate() const
{
    return m_notifyOnUpdate;
}

/**
 * Returns \c true if the updater should notify the user when it finishes
 * checking for updates.
 *
 * \note If set to \c true, the \c Updater will notify the user even when there
 *       are no updates available (by congratulating him/her about being smart)
 */
bool Updater::notifyOnFinish() const
{
    return m_notifyOnFinish;
}

/**
 * Returns \c true if there the current update is mandatory.
 * \warning You should call \c checkForUpdates() before using this function
*/
bool Updater::mandatoryUpdate() const
{
    return m_mandatoryUpdate;
}

/**
 * Returns \c true if there is an update available.
 * \warning You should call \c checkForUpdates() before using this function
 */
bool Updater::updateAvailable() const
{
    qDebug()<<"updateAvailable="<<m_updateAvailable;
    return m_updateAvailable;
}
bool Updater::updateSameVersionAvailable() const
{
    qDebug()<<"m_sameVersion="<<m_sameVersion;
    return m_sameVersion;
}

/**
 * Returns \c true if the integrated downloader is enabled.
 * \note If set to \c true, the \c Updater will open the downloader dialog if
 *       the user agrees to download the update.
 */
bool Updater::downloaderEnabled() const
{
    return m_downloaderEnabled;
}

/**
 * Returns \c true if the updater shall not intervene when the download has
 * finished (you can use the \c QSimpleUpdater signals to know when the
 * download is completed).
 */
bool Updater::useCustomInstallProcedures() const
{
    return m_downloader->useCustomInstallProcedures();
}

/**
 * Downloads and interpets the update definitions file referenced by the
 * \c url() function.
 */
void Updater::checkForUpdates()
{
    QNetworkRequest request (url());
    if (!userAgentString().isEmpty())
        request.setRawHeader ("User-Agent", userAgentString().toUtf8());

    //判断一下网络状态， 如果为NotAccessible 重新设置一下
    if(m_manager->networkAccessible() == QNetworkAccessManager::NotAccessible){
        qDebug()<<"setNetworkAccessible(QNetworkAccessManager::Accessible";
        m_manager->setNetworkAccessible(QNetworkAccessManager::Accessible);
    }
    m_manager->get (request);
    qDebug()<<request.url();
}

/**
 * Changes the \c url in which the \c Updater can find the update definitions
 * file.
 */
void Updater::setUrl (const QString& url)
{
    m_url = url;
}

/**
 * Changes the module \a name.
 * \note The module name is used on the user prompts. If the module name is
 *       empty, then the prompts will show the name of the application.
 */
void Updater::setModuleName (const QString& name)
{
    m_moduleName = name;
}

/**
 * If \a notify is set to \c true, then the \c Updater will notify the user
 * when an update is available.
 */
void Updater::setNotifyOnUpdate (const bool notify)
{
    m_notifyOnUpdate = notify;
}

/**
 * If \a notify is set to \c true, then the \c Updater will notify the user
 * when it has finished interpreting the update definitions file.
 */
void Updater::setNotifyOnFinish (const bool notify)
{
    m_notifyOnFinish = notify;
}
void Updater::setNoNotifyDownload(const bool notify)
{
    m_noTipDownload = notify;
}
void Updater::setCompareBySameString(const bool notify)
{
    m_bSameStringCompare = notify;
}
/**
 * Changes the user agent string used to identify the client application
 * from the server in a HTTP session.
 *
 * By default, the user agent will co
 */
void Updater::setUserAgentString (const QString& agent)
{
    m_userAgentString = agent;
    m_downloader->setUserAgentString (agent);
}

/**
 * Changes the module \a version
 * \note The module version is used to compare the local and remote versions.
 *       If the \a version parameter is empty, then the \c Updater will use the
 *       application version (referenced by \c qApp)
 */
void Updater::setModuleVersion (const QString& version)
{
    m_moduleVersion = version;
}

/**
 * If the \a enabled parameter is set to \c true, the \c Updater will open the
 * integrated downloader if the user agrees to install the update (if any)
 */
void Updater::setDownloaderEnabled (const bool enabled)
{
    m_downloaderEnabled = enabled;
}

/**
 * Changes the platform key.
 * If the platform key is empty, then the system will use the following keys:
 *    - On iOS: \c ios
 *    - On Mac OSX: \c osx
 *    - On Android: \c android
 *    - On GNU/Linux: \c linux
 *    - On Microsoft Windows: \c windows
 */
void Updater::setPlatformKey (const QString& platformKey)
{
    m_platform = platformKey;
}

/**
 * If the \a customAppcast parameter is set to \c true, then the \c Updater
 * will not try to read the network reply from the server, instead, it will
 * emit the \c appcastDownloaded() signal, which allows the application to
 * read and interpret the appcast file by itself
 */
void Updater::setUseCustomAppcast (const bool customAppcast)
{
    m_customAppcast = customAppcast;
}

/**
 * If the \a custom parameter is set to \c true, the \c Updater will not try
 * to open the downloaded file. Use the signals fired by the \c QSimpleUpdater
 * to install the update from the downloaded file by yourself.
 */
void Updater::setUseCustomInstallProcedures (const bool custom)
{
    m_downloader->setUseCustomInstallProcedures (custom);
}

/**
 * If the \a mandatory_update is set to \c true, the \c Updater has to download and install the
 * update. If the user cancels or exits, the application will close
 */
void Updater::setMandatoryUpdate(const bool mandatory_update)
{
    m_mandatoryUpdate = mandatory_update;
}
/**
 * Called when the download of the update definitions file is finished.
 */

void Updater::onReply (QNetworkReply* reply)
{
    /* Check if we need to redirect */
    QUrl redirect = reply->attribute (
                        QNetworkRequest::RedirectionTargetAttribute).toUrl();
    if (!redirect.isEmpty()) {
        setUrl (redirect.toString());
        checkForUpdates();
        return;
    }
reply->ignoreSslErrors();
    /* There was a network error */
    int aa=reply->error();
    if (aa != QNetworkReply::NoError) {
        qDebug()<<"QNetworkReply::Error="<<aa;
        qDebug()<<reply->url()<<aa;




        setUpdateAvailable (false);
        emit checkingFinished (url());
        return;
    }

    /* The application wants to interpret the appcast by itself */
    if (customAppcast()) {
        emit appcastDownloaded (url(), reply->readAll());
        emit checkingFinished (url());
        return;
    }

    /* Try to create a JSON document from downloaded data */
    QJsonDocument document = QJsonDocument::fromJson (reply->readAll());

    /* JSON is invalid */
    if (document.isNull()) {
        qDebug()<<"document.isNull()";
       setUpdateAvailable (false);
        emit checkingFinished (url());
        return;
    }

    /* Get the platform information */
    QJsonObject updates = document.object().value ("updates").toObject();
    QJsonObject platform = updates.value (platformKey()).toObject();

    /* Get update information */
    m_openUrl = platform.value ("open-url").toString();

    LoAppConfig *cfg = LoAppConfig::getInstance();

    QSettings *settings = new QSettings(cfg->OrganizationName(), cfg->ApplicationName());
    if(settings->value("MainSetting/Language").isValid())
    {
        QString lang = settings->value("MainSetting/Language").toString();
        if(lang == "zhCN") {
            m_changelog = platform.value ("changelog_zhCN").toString();
        }
        else if(lang == "zhTH")
        {
            m_changelog = platform.value ("changelog_zhTH").toString();
        }
        else if(lang == "jaJP")
        {
            m_changelog = platform.value ("changelog_jaJP").toString();
        }
        else {
            m_changelog = platform.value ("changelog_enUS").toString();
        }
    }
    else
    {
        m_changelog = platform.value ("changelog_enUS").toString();
    }

    m_downloadUrl = platform.value ("download-url").toString();
    m_latestVersion = platform.value ("latest-version").toString();
    if (platform.contains("mandatory-update"))
        m_mandatoryUpdate = platform.value ("mandatory-update").toBool();

    /* Compare latest and current version */
    if(m_bSameStringCompare)
    {
        qDebug()<<latestVersion()<<"............"+moduleVersion();

        m_sameVersion=false;
        if(latestVersion()==moduleVersion())
            m_sameVersion=true;
        setUpdateAvailable(!m_sameVersion);
    }
    else {
        qDebug()<<"eeeeeeeeeee";
        if(latestVersion()==moduleVersion())
        {
            setUpdateAvailable (true);
            m_sameVersion=true;

        }
        else {

            setUpdateAvailable (compare (latestVersion(), moduleVersion()));
            m_sameVersion=false;
        }

    }
    emit checkingFinished (url());
}

/**
 * Prompts the user based on the value of the \a available parameter and the
 * settings of this instance of the \c Updater class.
 */
void Updater::setUpdateAvailable (const bool available)
{
    qDebug()<<"setUpdateAvailable="<<available;
    m_updateAvailable = available;

    QMessageBox box;
    box.setTextFormat (Qt::RichText);
    box.setIcon (QMessageBox::Information);

    if (updateAvailable() && (notifyOnUpdate() || notifyOnFinish()))
    {
        QString text = tr("Would you like to download the update now?");
        if (m_mandatoryUpdate)
        {
            text = tr ("Would you like to download the update now? This is a mandatory update, exiting now will close the application");
        }
        if(m_noTipDownload)
        {
            m_downloader->setUrlId (url());
            m_downloader->setFileName (downloadUrl().split ("/").last());
            m_downloader->setMandatoryUpdate(m_mandatoryUpdate);
            m_downloader->startDownload (QUrl (downloadUrl()));
            m_downloader->m_strVersion=latestVersion();

        }
        else {
//            QString title = "<h3>"
//                            + tr ("Version %1 of %2 has been released!")
//                            .arg (latestVersion()).arg (moduleName())
//                            + "</h3>";

//            X_UIMsgBoxOkCancel *dlg = new X_UIMsgBoxOkCancel(tr("Tip Info"),title);
//            if(dlg->exec() == QDialog::Accepted)
            {
                if (!openUrl().isEmpty())
                    QDesktopServices::openUrl (QUrl (openUrl()));
                else if (downloaderEnabled())
                {
                    m_downloader->setUrlId (url());
                    m_downloader->setFileName (downloadUrl().split ("/").last());
                    m_downloader->setMandatoryUpdate(m_mandatoryUpdate);
                    m_downloader->startDownload (QUrl (downloadUrl()));
                    m_downloader->m_strVersion=latestVersion();
                }
                else
                    QDesktopServices::openUrl (QUrl (downloadUrl()));
            }
//            else {
//                if(m_mandatoryUpdate)
//                {
//                    QApplication::quit();
//                }
//            }


//            box.setText (title);
//            box.setInformativeText (text);
//            box.setStandardButtons (QMessageBox::No | QMessageBox::Yes);
//            box.setDefaultButton   (QMessageBox::Yes);

//            if (box.exec() == QMessageBox::Yes)
//            {
//                if (!openUrl().isEmpty())
//                    QDesktopServices::openUrl (QUrl (openUrl()));
//                else if (downloaderEnabled())
//                {
//                    m_downloader->setUrlId (url());
//                    m_downloader->setFileName (downloadUrl().split ("/").last());
//                    m_downloader->setMandatoryUpdate(m_mandatoryUpdate);
//                    m_downloader->startDownload (QUrl (downloadUrl()));
//                    m_downloader->m_strVersion=latestVersion();
//                }
//                else
//                    QDesktopServices::openUrl (QUrl (downloadUrl()));
//            }
//            else
//            {
//                if(m_mandatoryUpdate)
//                {
//                    QApplication::quit();
//                }
//            }
        }

    }

    else if (notifyOnFinish()) {
        box.setStandardButtons (QMessageBox::Close);
        box.setInformativeText (tr ("No updates are available for the moment"));
        box.setText ("<h3>"
                     + tr ("Congratulations! You are running the "
                           "latest version of %1").arg (moduleName())
                     + "</h3>");

        box.exec();
    }
}

/**
 * Compares the two version strings (\a x and \a y).
 *     - If \a x is greater than \y, this function returns \c true.
 *     - If \a y is greater than \x, this function returns \c false.
 *     - If both versions are the same, this function returns \c false.
 */
bool Updater::compare (const QString& x, const QString& y)
{
    QStringList versionsX = x.split (".");
    QStringList versionsY = y.split (".");

    int count = qMin (versionsX.count(), versionsY.count());

    for (int i = 0; i < count; ++i) {
        int a = QString (versionsX.at (i)).toInt();
        int b = QString (versionsY.at (i)).toInt();

        if (a >= b)
            return true;

        else if (b > a)
            return false;
    }

    return versionsY.count() < versionsX.count();
}
