/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   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 2 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/>  *
 ***************************************************************************/
/** @file
 * This file is Skrooge plugin for unit management.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgunitplugin.h"

#include <KInputDialog>
#include <KActionCollection>
#include <KAction>
#include <KAboutData>
#include <KGenericFactory>
#include <KStandardDirs>
#include <KComponentData>

#include "skgunitpluginwidget.h"
#include "skgunitboardwidget.h"
#include "skgunit_settings.h"
#include "skgtraces.h"
#include "skgmainpanel.h"
#include "skghtmlboardwidget.h"
#include "skgtransactionmng.h"
#include "skgdocumentbank.h"
#include "skgunitvalueobject.h"

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGUnitPluginFactory, registerPlugin<SKGUnitPlugin>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGUnitPluginFactory("skrooge_unit", "skrooge_unit"))

SKGUnitPlugin::SKGUnitPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/)
    : SKGInterfacePlugin(iParent), m_currentBankDocument(NULL),
      m_splitShareAction(NULL)
{
    Q_UNUSED(iWidget);
    SKGTRACEIN(10, "SKGUnitPlugin::SKGUnitPlugin");
}

SKGUnitPlugin::~SKGUnitPlugin()
{
    SKGTRACEIN(10, "SKGUnitPlugin::~SKGUnitPlugin");
    m_currentBankDocument = NULL;
    m_splitShareAction = NULL;
}

bool SKGUnitPlugin::setupActions(SKGDocument* iDocument, const QStringList& iArgument)
{
    SKGTRACEIN(10, "SKGUnitPlugin::setupActions");
    Q_UNUSED(iArgument);
    m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
    if (m_currentBankDocument == NULL) return false;

    setComponentData(KGlobal::mainComponent());
    setXMLFile("../skrooge_unit/skrooge_unit.rc");

    // Menu
    m_splitShareAction = new KAction(KIcon("skrooge_split_stock"), i18nc("Verb", "Split share..."), this);
    connect(m_splitShareAction, SIGNAL(triggered(bool)), this, SLOT(onSplitShare()));
    m_splitShareAction->setShortcut(Qt::ALT + Qt::Key_Slash);
    registerGlobalAction("edit_split_stock", m_splitShareAction);

    // -----------
    KAction* act = new KAction(KIcon(icon()), i18nc("Verb", "Delete unused units"), this);
    connect(act, SIGNAL(triggered(bool)), this, SLOT(deleteUnusedUnits()));
    registerGlobalAction("clean_delete_unused_units", act);
    return true;
}

int SKGUnitPlugin::getNbDashboardWidgets()
{
    return 2;
}

QString SKGUnitPlugin::getDashboardWidgetTitle(int iIndex)
{
    if (iIndex == 0) {
        return i18nc("Noun, the title of a section", "Quotes");
    } else {
        return i18nc("Noun, the title of a section", "Stock portfolio");
    }
}

SKGBoardWidget* SKGUnitPlugin::getDashboardWidget(int iIndex)
{
    if (iIndex == 0) {
        return new SKGUnitBoardWidget(m_currentBankDocument);
    } else {
        return new SKGHtmlBoardWidget(m_currentBankDocument,
                                      getDashboardWidgetTitle(iIndex),
                                      KStandardDirs().findResource("data", "skrooge/html/default/portfolio.html"),
                                      QStringList() << "v_operation_display");
    }
}

void SKGUnitPlugin::refresh()
{
    SKGTRACEIN(10, "SKGUnitPlugin::refresh");
    if (SKGMainPanel::getMainPanel() && m_currentBankDocument) {
        bool test = (m_currentBankDocument->getDatabase() != NULL);
        if (m_splitShareAction) {
            SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
            if (selection.count() == 1 && selection.at(0).getRealTable() == "unit") {
                SKGUnitObject unit(selection.at(0));
                m_splitShareAction->setEnabled(unit.getType() == SKGUnitObject::SHARE);
            } else {
                m_splitShareAction->setEnabled(false);
            }
        }

        // Automatic download
        if (test) {
            QString doc_id = m_currentBankDocument->getUniqueIdentifier();
            if (m_docUniqueIdentifier != doc_id) {
                m_docUniqueIdentifier = doc_id;
                // Check if current unit is existing
                KLocale* loc = KGlobal::locale();
                bool exist = false;
                SKGError err = m_currentBankDocument->existObjects("unit", "", exist);
                IFOK(err) {
                    if (!exist) {
                        SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Create default unit"), err);
                        IFOK(err) {
                            // Create default unit
                            SKGUnitObject unit;
                            err = SKGUnitObject::createCurrencyUnit(m_currentBankDocument, loc->currencyCode(), unit);

                            // The file is considered has not modified
                            m_currentBankDocument->setFileNotModified();
                        }
                    } else if (skgunit_settings::download_on_open()) {
                        // Check frequency
                        QString lastAutomaticDownload = m_currentBankDocument->getParameter("SKG_LAST_UNIT_AUTOMATIC_DOWNLOAD");
                        if (lastAutomaticDownload.isEmpty()) lastAutomaticDownload = "1970-01-01";
                        QDate lastAutomaticDownloadDate = QDate::fromString(lastAutomaticDownload, "yyyy-MM-dd");
                        if ((lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 1 && skgunit_settings::download_frequency() == 0) ||
                                (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 7 && skgunit_settings::download_frequency() == 1) ||
                                (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 30 && skgunit_settings::download_frequency() == 2))

                        {
                            // Download all units
                            SKGObjectBase::SKGListSKGObjectBase selection;
                            err = m_currentBankDocument->getObjects("unit", "", selection);
                            int nb = selection.count();
                            SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Automatic download of units"), err, nb);
                            for (int i = 0; !err && i < nb; ++i) {
                                SKGUnitObject unit(selection.at(i));
                                err = SKGUnitPluginWidget::downloadUnitValue(unit, SKGUnitPluginWidget::getDownloadModeFromSettings());
                                IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
                            }

                            // Memorize the last download date
                            IFOKDO(err, m_currentBankDocument->setParameter("SKG_LAST_UNIT_AUTOMATIC_DOWNLOAD", QDate::currentDate().toString("yyyy-MM-dd")))
                        }
                    }
                }

                // Display error
                SKGMainPanel::displayErrorMessage(err);
            }
        }
    }
}

SKGTabPage* SKGUnitPlugin::getWidget()
{
    SKGTRACEIN(10, "SKGUnitPlugin::getWidget");
    return new SKGUnitPluginWidget(m_currentBankDocument);
}

QWidget* SKGUnitPlugin::getPreferenceWidget()
{
    SKGTRACEIN(10, "SKGUnitPlugin::getPreferenceWidget");
    QWidget* w = new QWidget();
    ui.setupUi(w);

    return w;
}

KConfigSkeleton* SKGUnitPlugin::getPreferenceSkeleton()
{
    return skgunit_settings::self();
}

QString SKGUnitPlugin::title() const
{
    return i18nc("Noun, units for operations, usually currencies or a shares", "Units");
}

QString SKGUnitPlugin::icon() const
{
    return "view-currency-list";
}

QString SKGUnitPlugin::toolTip() const
{
    return i18nc("A tool tip", "Unit management");
}

QStringList SKGUnitPlugin::tips() const
{
    QStringList output;
    output.push_back(i18nc("Description of a tips", "<p>... you can download units.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... units can be downloaded automatically when a document is opened.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... shares can be downloaded with additional information by activating the option in settings.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can split a share.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... units can be merged by drag & drop.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can download more sources of quote.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can create and share your own source of quote.</p>"));

    return output;
}

int SKGUnitPlugin::getOrder() const
{
    return 60;
}

bool SKGUnitPlugin::isInPagesChooser() const
{
    return true;
}

void SKGUnitPlugin::onSplitShare()
{
    SKGError err;
    SKGTRACEINRC(10, "SKGUnitPlugin::onSplitShare", err);

    // Get Selection
    if (SKGMainPanel::getMainPanel()) {
        SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
        int nb = selection.count();
        if (nb == 1) {
            bool ok = false;
            double ratio = KInputDialog::getDouble(i18nc("Question", "Split share"),
                                                   i18nc("Question", "Ratio (2 means 2-for-1, 0.5 means 1-for-2):"), 2.0,
                                                   0, DBL_MAX, 8, &ok, SKGMainPanel::getMainPanel());
            if (ok) {
                SKGUnitObject unit(selection.at(0));
                SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Split stock '%1' by '%2'", unit.getName(), ratio), err);
                err = unit.split(ratio);
            }
        }

        // status
        IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Stock split.")))
        else {
            err.addError(ERR_FAIL, i18nc("Error message", "Splitting stock failed."));
        }

        // Display error
        SKGMainPanel::displayErrorMessage(err);
    }
}

SKGAdviceList SKGUnitPlugin::advice(const QStringList& iIgnoredAdvice)
{
    SKGTRACEIN(10, "SKGUnitPlugin::advice");
    SKGAdviceList output;

    // Get all currencies
    SKGStringListList result;
    m_currentBankDocument->executeSelectSqliteOrder("SELECT (SELECT count(1) FROM operation WHERE operation.rc_unit_id=unit.id), unit.t_name FROM unit WHERE t_type='C' GROUP BY t_name ORDER BY count(1) DESC", result);
    int nb = result.count();

    // Check primary unit
    if (!iIgnoredAdvice.contains("skgunitplugin_primaryunit")) {
        if (m_currentBankDocument->getPrimaryUnit().Name.isEmpty() && nb > 1) {
            // Get unit
            QString unit = result.at(1).at(1);

            SKGAdvice ad;
            ad.setUUID("skgunitplugin_primaryunit|" % unit);
            ad.setPriority(8);
            ad.setShortMessage(i18nc("Advice on making the best (short)", "Define a primary currency"));
            ad.setLongMessage(i18nc("Advice on making the best (long)", "To avoid misunderstanding and conflicts between units at conversion time, you should define a primary currency. It is the currency against which all other will be converted"));
            QStringList autoCorrections;
            autoCorrections.push_back(i18nc("Advice on making the best (action)", "Set '%1' as primary currency", unit));
            autoCorrections.push_back(i18nc("Advice on making the best (action)", "Edit units"));
            ad.setAutoCorrections(autoCorrections);
            output.push_back(ad);

            --nb;
        }
    }

    // Check secondary unit
    if (!iIgnoredAdvice.contains("skgunitplugin_secondaryunit")) {
        if (m_currentBankDocument->getSecondaryUnit().Name.isEmpty() && nb > 1) {
            // Get unit
            QString unit = result.at(1).at(1);

            SKGAdvice ad;
            ad.setUUID("skgunitplugin_secondaryunit|" % unit);
            ad.setPriority(2);
            ad.setShortMessage(i18nc("Advice on making the best (short)", "Define a secondary currency"));
            ad.setLongMessage(i18nc("Advice on making the best (long)", "When a secondary unit is defined, Skrooge will display it as an additional amount information."));
            QStringList autoCorrections;
            autoCorrections.push_back(i18nc("Advice on making the best (action)", "Set '%1' as secondary currency", unit));
            autoCorrections.push_back(i18nc("Advice on making the best (action)", "Edit units"));
            ad.setAutoCorrections(autoCorrections);
            output.push_back(ad);
        }
    }

    // Shares not downloaded
    if (!iIgnoredAdvice.contains("skgunitplugin_notdownloaded")) {
        m_currentBankDocument->executeSelectSqliteOrder("SELECT t_name, t_internet_code from unit WHERE t_internet_code<>'' AND (julianday('now')-(SELECT MAX(julianday(d_date)) FROM unitvalue WHERE rd_unit_id=unit.id ))>30 OR NOT EXISTS (SELECT 1 FROM unitvalue WHERE unitvalue.rd_unit_id=unit.id)", result);
        nb = result.count();
        for (int i = 1; i < nb; ++i) {  // Ignore header
            // Get parameters
            QStringList line = result.at(i);
            QString unit = line.at(0);
            QString internet_code = line.at(1);

            SKGAdvice ad;
            ad.setUUID("skgunitplugin_notdownloaded|" % unit);
            ad.setPriority(5);
            ad.setShortMessage(i18nc("Advice on making the best (short)", "Unit '%1' has not been downloaded for more than a month", unit));
            ad.setLongMessage(i18nc("Advice on making the best (long)", "Don't forget download units to have a better view of your accounts"));
            QStringList autoCorrections;
            autoCorrections.push_back(i18nc("Advice on making the best (action)", "Edit units"));
            if (!internet_code.isEmpty()) autoCorrections.push_back(i18nc("Advice on making the best (action)", "Download '%1'", unit));
            ad.setAutoCorrections(autoCorrections);
            output.push_back(ad);
        }
    }

    // Check unused units
    if (!iIgnoredAdvice.contains("skgunitplugin_unused")) {
        bool exist = false;
        m_currentBankDocument->existObjects("unit", "t_type NOT IN ('I', '1', '2') AND NOT EXISTS (SELECT 1 FROM operation WHERE operation.rc_unit_id=unit.id) AND NOT EXISTS (SELECT 1 FROM unit as unit2 WHERE unit2.rd_unit_id=unit.id)", exist);
        if (exist) {
            SKGAdvice ad;
            ad.setUUID("skgunitplugin_unused");
            ad.setPriority(5);
            ad.setShortMessage(i18nc("Advice on making the best (short)", "Many unused units"));
            ad.setLongMessage(i18nc("Advice on making the best (long)", "You can improve performances by removing units for which no operation is registered."));
            QStringList autoCorrections;
            autoCorrections.push_back("skg://clean_delete_unused_units");
            ad.setAutoCorrections(autoCorrections);
            output.push_back(ad);
        }
    }

    // Check unit too complex
    if (!iIgnoredAdvice.contains("skgunitplugin_toocomplex")) {
        m_currentBankDocument->executeSelectSqliteOrder("SELECT A.t_name FROM unit A, unit B, unit C, unit D WHERE A.rd_unit_id=B.id AND B.rd_unit_id=C.id AND C.rd_unit_id=D.id", result);
        nb = result.count();
        for (int i = 1; i < nb; ++i) {  // Ignore header
            // Get parameters
            QStringList line = result.at(i);
            QString unit = line.at(0);

            SKGAdvice ad;
            ad.setUUID("skgunitplugin_toocomplex");
            ad.setPriority(9);
            ad.setShortMessage(i18nc("Advice on making the best (short)", "The definition of the unit '%1' is too complex", unit));
            ad.setLongMessage(i18nc("Advice on making the best (long)", "'%1' is defined relatively to another unit defined relatively to a third one. This is too complex and not supported by Skrooge.", unit));
            QStringList autoCorrections;
            autoCorrections.push_back(i18nc("Advice on making the best (action)", "Edit units"));
            ad.setAutoCorrections(autoCorrections);
            output.push_back(ad);
        }
    }

    // Unit with very old values
    if (!iIgnoredAdvice.contains("skgunitplugin_veryold")) {
        m_currentBankDocument->executeSelectSqliteOrder("SELECT t_name from unit WHERE (SELECT COUNT(*) FROM unitvalue WHERE unitvalue.rd_unit_id=unit.id)>1 AND EXISTS (SELECT 1 FROM unitvalue WHERE unitvalue.rd_unit_id=unit.id AND unitvalue.d_date<=(SELECT date('now', '-50 year')))", result);
        nb = result.count();
        for (int i = 1; i < nb; ++i) {  // Ignore header
            // Get parameters
            QStringList line = result.at(i);
            QString unit = line.at(0);

            SKGAdvice ad;
            ad.setUUID("skgunitplugin_veryold|" % unit);
            ad.setPriority(3);
            ad.setShortMessage(i18nc("Advice on making the best (short)", "Unit '%1' has very old values", unit));
            ad.setLongMessage(i18nc("Advice on making the best (long)", "Unit '%1' has very old values. Check if this is normal.", unit));
            QStringList autoCorrections;
            autoCorrections.push_back(i18nc("Advice on making the best (action)", "Edit units"));
            ad.setAutoCorrections(autoCorrections);
            output.push_back(ad);
        }
    }

    return output;
}

SKGError SKGUnitPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
{
    if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_primaryunit|"))) {
        if (iSolution == 1) {
            SKGMainPanel::getMainPanel()->openPage("skg://skrooge_unit_plugin");
        } else {
            // Get parameters
            QString unit = iAdviceIdentifier.right(iAdviceIdentifier.length() - 26);

            SKGError err;
            {
                SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Define primary currency"), err);
                SKGUnitObject unitObj(m_currentBankDocument);
                err = unitObj.setName(unit);
                IFOKDO(err, unitObj.load())
                IFOKDO(err, unitObj.setType(SKGUnitObject::PRIMARY))
                IFOKDO(err, unitObj.save(true, false))
            }

            // status bar
            IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Primary currency defined.")))
            else {
                err.addError(ERR_FAIL, i18nc("Error message", "Primary currency definition failed"));
            }

            // Display error
            SKGMainPanel::displayErrorMessage(err);
        }

        return SKGError();
    } else if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_secondaryunit|"))) {
        if (iSolution == 1) {
            SKGMainPanel::getMainPanel()->openPage("skg://skrooge_unit_plugin");
        } else {
            // Get parameters
            QString unit = iAdviceIdentifier.right(iAdviceIdentifier.length() - 28);

            SKGError err;
            {
                SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Define secondary currency"), err);
                SKGUnitObject unitObj(m_currentBankDocument);
                err = unitObj.setName(unit);
                IFOKDO(err, unitObj.load())
                IFOKDO(err, unitObj.setType(SKGUnitObject::SECONDARY))
                IFOKDO(err, unitObj.save(true, false))
            }

            // status bar
            IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Secondary currency defined.")))
            else {
                err.addError(ERR_FAIL, i18nc("Error message", "Secondary currency definition failed"));
            }

            // Display error
            SKGMainPanel::displayErrorMessage(err);
        }

        return SKGError();
    } else if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_notdownloaded|"))) {
        if (iSolution == 0) {
            SKGMainPanel::getMainPanel()->openPage("skg://skrooge_unit_plugin");
        } else {
            // Get parameters
            QString unit = iAdviceIdentifier.right(iAdviceIdentifier.length() - 28);

            SKGError err;
            SKGUnitObject unitObj(m_currentBankDocument);
            err = unitObj.setName(unit);
            IFOKDO(err, unitObj.load())
            IFOKDO(err, SKGUnitPluginWidget::downloadUnitValue(unitObj, SKGUnitPluginWidget::getDownloadModeFromSettings()))

            // Display error
            SKGMainPanel::displayErrorMessage(err);
        }

        return SKGError();
    } else if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_veryold|"))) {
        SKGMainPanel::getMainPanel()->openPage("skg://skrooge_unit_plugin");
        return SKGError();
    } else if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgunitplugin_toocomplex"))) {
        SKGMainPanel::getMainPanel()->openPage("skg://skrooge_unit_plugin");
        return SKGError();
    }
    return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
}

void SKGUnitPlugin::deleteUnusedUnits() const
{
    SKGError err;
    _SKGTRACEINRC(10, "SKGUnitPlugin::deleteUnusedUnits", err);
    if (m_currentBankDocument) {
        SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Delete unused units")  , err);

        // Modification of payee object
        QString sql = "DELETE FROM unit WHERE t_type NOT IN ('I', '1', '2') AND NOT EXISTS (SELECT 1 FROM operation WHERE operation.rc_unit_id=unit.id) AND NOT EXISTS (SELECT 1 FROM unit as unit2 WHERE unit2.rd_unit_id=unit.id)";
        err = m_currentBankDocument->executeSqliteOrder(sql);
    }

    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Unused units deleted")))
    else {
        err.addError(ERR_FAIL, i18nc("Error message", "Unused units deletion failed"));
    }

    // Display error
    SKGMainPanel::displayErrorMessage(err);
}

#include "skgunitplugin.moc"
