/*
 *   This file is part of Dianara
 *   Copyright 2012-2019  JanKusanagi JRR <jancoding@gmx.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 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, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .
 */

#include "globalobject.h"


GlobalObject::GlobalObject(QObject *parent) : QObject(parent)
{
    QSettings settings;
    settings.beginGroup("Configuration");

    /////////////////////////////////////////////////////////////////// GENERAL


    ///////////////////////////////////////////////////////////////////// FONTS
    QFont defaultTitleFont;     // 1 point larger
    defaultTitleFont.setPointSize(defaultTitleFont.pointSize() + 1);
    defaultTitleFont.setBold(true);

    QFont defaultContentFont;   // Just the default text size

    QFont defaultCommentsFont;  //  1 point smaller
    defaultCommentsFont.setPointSize(defaultCommentsFont.pointSize() - 1);

    QFont defaultMinorFeedFont; // 2 points smaller
    defaultMinorFeedFont.setPointSize(defaultMinorFeedFont.pointSize() - 2);

    this->syncFontSettings(settings.value("font1",
                                          defaultTitleFont).toString(),
                           settings.value("font2",
                                          defaultContentFont).toString(),
                           settings.value("font3",
                                          defaultCommentsFont).toString(),
                           settings.value("font4",
                                          defaultMinorFeedFont).toString());


    //////////////////////////////////////////////////////////////////// COLORS
    m_colorsList.clear();                                 // Defaults:
    m_colorsList << settings.value("color1", "#CC2030").toString() // Red
                 << settings.value("color2", "#5599CC").toString() // Blue
                 << settings.value("color3", "#DDCC10").toString() // Yellow
                 << settings.value("color4", "#10BB10").toString() // Green
                 << settings.value("color5", "#AA77EE").toString() // Purple
                 << settings.value("color6").toString();
    // No need to call this->syncColorSettings() here...


    ///////////////////////////////////////////////////////////////// TIMELINES
    const int pppMain = qBound(5,
                               settings.value("postsPerPageMain", 20).toInt(),
                               50);
    const int pppOther = qBound(1,
                                settings.value("postsPerPageOther", 5).toInt(),
                                30);
    this->syncTimelinesSettings(pppMain,
                                pppOther,
                                settings.value("showDeletedPosts",     false).toBool(),
                                settings.value("hideDuplicatedPosts",  false).toBool(),
                                settings.value("jumpToNewOnUpdate",    false).toBool(),
                                settings.value("mfSnippetsType",       2).toInt(), // Default: Always
                                settings.value("snippetCharLimit",     30).toInt(),
                                settings.value("snippetCharLimitHl",   200).toInt(),
                                settings.value("minorFeedAvatarIndex", 1).toInt(),  // Def: 2nd (32x32)
                                settings.value("minorFeedIconType",    0).toInt()); // Def: No


    ///////////////////////////////////////////////////////////////////// POSTS
    this->syncPostSettings(settings.value("postAvatarIndex",       3).toInt(), // Def: 4th (64x64)
                           settings.value("commentAvatarIndex",    1).toInt(), // Def: 2nd (32x32)
                           settings.value("postExtendedShares",    true).toBool(),
                           settings.value("postShowExtraInfo",     false).toBool(),
                           settings.value("postHLAuthorComments",  true).toBool(),
                           settings.value("postHLOwnComments",     true).toBool(),
                           settings.value("postIgnoreSslInImages", false).toBool(),
                           settings.value("postFullImages",        false).toBool());


    ////////////////////////////////////////////////////////////////// COMPOSER
    this->syncComposerSettings(settings.value("publicPosts", false).toBool(),
                               settings.value("useFilenameAsTitle", false).toBool(),
                               settings.value("showCharacterCounter", false).toBool());


    /////////////////////////////////////////////////////////////////// PRIVACY
    this->syncPrivacySettings(settings.value("silentFollows", false).toBool(),
                              settings.value("silentLists", true).toBool(),
                              settings.value("silentLikes", false).toBool());


    ///////////////////////////////////////////////////////////// NOTIFICATIONS
    this->syncNotificationSettings(settings.value("notificationTaskbar",
                                                  true).toBool());
                                          // TODO: load the other nofification options


    ////////////////////////////////////////////////////////////////////// TRAY
    this->syncTrayOptions(settings.value("systrayIconType", 0).toInt(),
                          settings.value("systrayCustomIconFN").toString(),
                          settings.value("systrayHideInTray", false).toBool());


    settings.endGroup();

    ///////////////////////////////////////////////////////////////////////////

    // Model for nick completion used by Composer
    m_nickCompletionModel = new QStandardItemModel(this);
    m_nickCompletionModel->setColumnCount(2);

    m_filterCompletionModel = new QSortFilterProxyModel(this);
    m_filterCompletionModel->setSortCaseSensitivity(Qt::CaseInsensitive);
    m_filterCompletionModel->setSourceModel(m_nickCompletionModel);


    // Timeline height used to calculate maximum optimal height for post contents
    m_timelineHeight = 400; // Some initial value


    m_programIsClosing = false;

    qDebug() << "GlobalObject created";
}

GlobalObject::~GlobalObject()
{
    qDebug() << "GlobalObject destroyed";
}




// General options
void GlobalObject::syncGeneralSettings()
{
    // TODO
}


// Font options
void GlobalObject::syncFontSettings(QString postTitleFont,
                                    QString postContentsFont,
                                    QString commentsFont,
                                    QString minorFeedFont)
{
    m_postTitleFontInfo = postTitleFont;
    m_postContentsFontInfo = postContentsFont;
    m_commentsFontInfo = commentsFont;
    m_minorFeedFontInfo = minorFeedFont;

    qDebug() << "GlobalObject::syncFontSettings() - font info sync'd";
}

QString GlobalObject::getPostTitleFont()
{
    return m_postTitleFontInfo;
}

QString GlobalObject::getPostContentsFont()
{
    return m_postContentsFontInfo;
}

QString GlobalObject::getCommentsFont()
{
    return m_commentsFontInfo;
}

QString GlobalObject::getMinorFeedFont()
{
    return m_minorFeedFontInfo;
}


// Color options
void GlobalObject::syncColorSettings(QStringList newColorList)
{
    m_colorsList = newColorList;

    qDebug() << "GlobalObject::syncColorSettings() - color list sync'd";
}

QStringList GlobalObject::getColorsList()
{
    return m_colorsList;
}

QString GlobalObject::getColor(int colorIndex)
{
    QString color;

    if (colorIndex >= 0 && colorIndex < m_colorsList.length())
    {
        color = m_colorsList.at(colorIndex);
        if (!QColor::isValidColor(color)) // If invalid, clear it
        {
            color.clear();
        }
    }

    return color;
}


// Timeline options
void GlobalObject::syncTimelinesSettings(int pppMain, int pppOther,
                                         bool showDeleted, bool hideDuplicates,
                                         bool jumpToNew, int minorFeedSnippets,
                                         int snippetsChars, int snippetsCharsHl,
                                         int mfAvatarIndex, int mfVerbIconType)
{
    m_postsPerPageMain = pppMain;
    m_postsPerPageOther = pppOther;

    m_showDeletedPosts = showDeleted;
    m_hideDuplicatedPosts = hideDuplicates;
    m_jumpToNewOnUpdate = jumpToNew;

    m_minorFeedSnippetsType = minorFeedSnippets;
    m_snippetsCharLimit = snippetsChars;
    m_snippetsCharLimitHl = snippetsCharsHl;
    m_mfAvatarIndex = mfAvatarIndex;
    m_mfAvatarSize = this->getAvatarSizeForIndex(mfAvatarIndex);

    m_mfIconType = mfVerbIconType;

    // TODO: more...
}

int GlobalObject::getPostsPerPageMain()
{
    return m_postsPerPageMain;
}

int GlobalObject::getPostsPerPageOther()
{
    return m_postsPerPageOther;
}

bool GlobalObject::getShowDeleted()
{
    return m_showDeletedPosts;
}

bool GlobalObject::getHideDuplicates()
{
    return m_hideDuplicatedPosts;
}

bool GlobalObject::getJumpToNewOnUpdate()
{
    return m_jumpToNewOnUpdate;
}

int GlobalObject::getMinorFeedSnippetsType()
{
    return m_minorFeedSnippetsType;
}

int GlobalObject::getSnippetsCharLimit()
{
    return m_snippetsCharLimit;
}

int GlobalObject::getSnippetsCharLimitHl()
{
    return m_snippetsCharLimitHl;
}

int GlobalObject::getMfAvatarIndex()
{
    return m_mfAvatarIndex;
}

QSize GlobalObject::getMfAvatarSize()
{
    return m_mfAvatarSize;
}

int GlobalObject::getMfIconType()
{
    return m_mfIconType;
}



// Post options
void GlobalObject::syncPostSettings(int postAvatarIndex, int commentAvatarIndex,
                                    bool extendedShares, bool showExtraInfo,
                                    bool hlAuthorComments, bool hlOwnComments,
                                    bool ignoreSslInImages, bool fullImages)
{
    m_postAvatarIndex = postAvatarIndex;
    m_postAvatarSize = this->getAvatarSizeForIndex(postAvatarIndex);
    m_commentAvatarIndex = commentAvatarIndex;
    m_commentAvatarSize = this->getAvatarSizeForIndex(commentAvatarIndex);

    m_postExtendedShares = extendedShares;
    m_postShowExtraInfo = showExtraInfo;
    m_postHLAuthorComments = hlAuthorComments;
    m_postHLOwnComments = hlOwnComments;
    m_postIgnoreSslInImages = ignoreSslInImages;
    m_postFullImages = fullImages;
}

int GlobalObject::getPostAvatarIndex()
{
    return m_postAvatarIndex;
}

QSize GlobalObject::getPostAvatarSize()
{
    return m_postAvatarSize;
}

int GlobalObject::getCommentAvatarIndex()
{
    return m_commentAvatarIndex;
}

QSize GlobalObject::getCommentAvatarSize()
{
    return m_commentAvatarSize;
}

bool GlobalObject::getPostExtendedShares()
{
    return m_postExtendedShares;
}

bool GlobalObject::getPostShowExtraInfo()
{
    return m_postShowExtraInfo;
}

bool GlobalObject::getPostHLAuthorComments()
{
    return m_postHLAuthorComments;
}

bool GlobalObject::getPostHLOwnComments()
{
    return m_postHLOwnComments;
}

bool GlobalObject::getPostIgnoreSslInImages()
{
    return m_postIgnoreSslInImages;
}

bool GlobalObject::getPostFullImages()
{
    return m_postFullImages;
}


// Composer options
void GlobalObject::syncComposerSettings(bool publicPosts,
                                        bool filenameAsTitle,
                                        bool showCharCounter)
{
    m_publicPostsByDefault = publicPosts;
    m_useFilenameAsTitle = filenameAsTitle;
    m_showCharacterCounter = showCharCounter;
}

bool GlobalObject::getPublicPostsByDefault()
{
    return m_publicPostsByDefault;
}

bool GlobalObject::getUseFilenameAsTitle()
{
    return m_useFilenameAsTitle;
}

bool GlobalObject::getShowCharacterCounter()
{
    return m_showCharacterCounter;
}



// Privacy options
void GlobalObject::syncPrivacySettings(bool silentFollows, bool silentLists,
                                       bool silentLikes)
{
    m_silentFollowing = silentFollows;
    m_silentListsHandling = silentLists;
    m_silentLiking = silentLikes;
}

bool GlobalObject::getSilentFollows()
{
    return m_silentFollowing;
}

bool GlobalObject::getSilentLists()
{
    return m_silentListsHandling;
}

bool GlobalObject::getSilentLikes()
{
    return m_silentLiking;
}



// Notification options
void GlobalObject::syncNotificationSettings(bool notifyInTaskbar)
{
    m_notifyInTaskbar = notifyInTaskbar;

    // TODO, most still handled elsewhere
}

bool GlobalObject::getNotifyInTaskbar()
{
    return m_notifyInTaskbar;
}



// Tray options
void GlobalObject::syncTrayOptions(int trayIconType, QString trayIconFN,
                                   bool trayHideStartup)
{
    m_systrayIconType = trayIconType;
    m_systrayIconFN = trayIconFN;
    m_systrayHideStartup = trayHideStartup;
}


int GlobalObject::getTrayIconType()
{
    return m_systrayIconType;
}

QString GlobalObject::getTrayIconFilename()
{
    return m_systrayIconFN;
}

bool GlobalObject::getTrayHideOnStartup()
{
    return m_systrayHideStartup;
}

///////////////////////////////////////////////////////////////////////////////


void GlobalObject::setDataDirectory(QString dataDir)
{
    m_dataDirectory = dataDir;
}

QString GlobalObject::getDataDirectory()
{
    return m_dataDirectory;
}


void GlobalObject::createMessageForContact(QString id, QString name,
                                           QString url)
{
    // Send signal to be caught by Publisher()
    emit messagingModeRequested(id, name, url);

    qDebug() << "GlobalObject; asking for Messaging mode for "
             << name << id << url;
}



void GlobalObject::browseUserMessages(QString userId, QString userName,
                                      QIcon userAvatar, QString userOutbox)
{
    // Signal to be caught by MainWindow
    emit userTimelineRequested(userId, userName,
                               userAvatar, userOutbox);
}



void GlobalObject::editPost(QString originalPostId,
                            QString type,
                            QString title,
                            QString contents)
{
    // Signal to be caught by Publisher
    emit postEditRequested(originalPostId,
                           type,
                           title,
                           contents);

    qDebug() << "GlobalObject; asking to edit post: "
             << originalPostId << title << type;
}



QSortFilterProxyModel *GlobalObject::getNickCompletionModel()
{
    return m_filterCompletionModel;
}


void GlobalObject::addToNickCompletionModel(QString id, QString name,
                                            QString url)
{
    QStandardItem *itemName = new QStandardItem(name);
    itemName->setData(id,  Qt::UserRole + 1);
    itemName->setData(url, Qt::UserRole + 2);

    QStandardItem *itemId = new QStandardItem(id);

    QList<QStandardItem*> itemLine;
    itemLine.append(itemName);
    itemLine.append(itemId);

    m_nickCompletionModel->appendRow(itemLine);
}

void GlobalObject::removeFromNickCompletionModel(QString id)
{
    qDebug() << "GlobalObject::removeFromNickCompletionModel()" << id;

    QList<QStandardItem *> allNicks;
    allNicks = m_nickCompletionModel->findItems(QString(), Qt::MatchContains);

    foreach (QStandardItem *item, allNicks)
    {
        //qDebug() << item->data(Qt::UserRole + 1).toString();

        if (item->data(Qt::UserRole + 1).toString() == id)
        {
            const int rowNum = item->row();
            /*
             * FIXME: Ensure deleting the item is not needed.
             * Deleting it before removeRow doesn't seem to do any harm.
             * removeRow seems to delete it anyway; trying to delete it
             * afterwards results in segfault.
             *
             * delete item;
             */
            m_nickCompletionModel->removeRow(rowNum);
        }
    }
}

void GlobalObject::clearNickCompletionModel()
{
    // clear() seems to delete the items properly
    m_nickCompletionModel->clear();
}

void GlobalObject::sortNickCompletionModel()
{
    m_filterCompletionModel->sort(0);
}

/*
 * Return a Display Name + URL pair for a given user ID,
 * to be used when restoring audience from a draft
 *
 */
QPair<QString, QString> GlobalObject::getDataForNick(QString id)
{
    QList<QStandardItem *> matchingNicks;
    matchingNicks = m_nickCompletionModel->findItems(id, Qt::MatchExactly, 1);
    if (matchingNicks.isEmpty())
    {
        // Return basic data for invalid case
        return QPair<QString,QString>(id, QString());
    }

    QStandardItem *item = m_nickCompletionModel->item(matchingNicks.first()->row(),
                                                      0);
    // FIXME: add some error control

    return QPair<QString,QString>(item->text(),
                                  item->data(Qt::UserRole + 2).toString());
}


/*
 * Change status bar message in main window
 *
 */
void GlobalObject::setStatusMessage(QString message)
{
    emit messageForStatusBar(message);
}

/*
 * Add a log message to the log viewer
 *
 */
void GlobalObject::logMessage(QString message, QString url)
{
    emit messageForLog(message, url);
}

void GlobalObject::storeTimelineHeight(int height)
{
    // Substract some pixels to account for the row of buttons, etc.
    m_timelineHeight = qMax(height - 80,
                            50); // Never less than 50, but window should never be that small
}

int GlobalObject::getTimelineHeight()
{
    return m_timelineHeight;
}

/*
 * Get corresponding QSize for specified index from combo box
 *
 */
QSize GlobalObject::getAvatarSizeForIndex(int index)
{
    int pixelSize;

    switch (index)
    {
    case 0:
        pixelSize = 16;
        break;
    case 1:
        pixelSize = 32;
        break;
    case 2:
        pixelSize = 48;
        break;
    // case 3 = default = 64
    case 4:
        pixelSize = 96;
        break;
    case 5:
        pixelSize = 128;
        break;
    case 6:
        pixelSize = 256;
        break;

    default: // index = 3 or invalid option
        pixelSize = 64;
    }

    return QSize(pixelSize, pixelSize);
}



void GlobalObject::notifyProgramShutdown()
{
    m_programIsClosing = true;
    emit programShuttingDown();
}

bool GlobalObject::isProgramShuttingDown()
{
    return m_programIsClosing;
}


void GlobalObject::showSpellConfigDialog()
{
    emit spellConfigRequested();
}
