#include "datadisplaydelegate.hpp"

#include "../../model/prefs/state.hpp"

#include <QApplication>
#include <QIcon>
#include <QPainter>
#include <QRect>
#include <QSize>
#include <QString>
#include <QStyle>

#include <memory>

#include <apps/opencs/model/prefs/category.hpp>
#include <apps/opencs/model/prefs/setting.hpp>
#include <apps/opencs/view/world/enumdelegate.hpp>
#include <apps/opencs/view/world/util.hpp>

#include <components/misc/scalableicon.hpp>

class QModelIndex;
class QObject;

namespace CSMDoc
{
    class Document;
}

namespace CSMWorld
{
    class CommandDispatcher;
}

CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList& values, const IconList& icons,
    CSMWorld::CommandDispatcher* dispatcher, CSMDoc::Document& document, const std::string& pageName,
    const std::string& settingName, QObject* parent)
    : EnumDelegate(values, dispatcher, document, parent)
    , mDisplayMode(Mode_TextOnly)
    , mIcons(icons)
    , mIconSize(QSize(16, 16))
    , mHorizontalMargin(QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1)
    , mTextLeftOffset(8)
    , mPixmapsColor(QApplication::palette().text().color())
    , mUiScale(static_cast<QGuiApplication*>(QGuiApplication::instance())->devicePixelRatio())
    , mSettingKey(pageName + '/' + settingName)
{
    if (parent)
        parent->installEventFilter(this);

    buildPixmaps();

    if (!pageName.empty())
        updateDisplayMode(CSMPrefs::get()[pageName][settingName].toString());
}

bool CSVWorld::DataDisplayDelegate::eventFilter(QObject* target, QEvent* event)
{
    if (event->type() == QEvent::Resize)
    {
        auto uiScale = static_cast<QGuiApplication*>(QGuiApplication::instance())->devicePixelRatio();
        if (mUiScale != uiScale)
        {
            mUiScale = uiScale;

            buildPixmaps();
        }
    }
    else if (event->type() == QEvent::PaletteChange)
    {
        QColor themeColor = QApplication::palette().text().color();
        if (themeColor != mPixmapsColor)
        {
            mPixmapsColor = std::move(themeColor);

            buildPixmaps();
        }
    }

    return false;
}

void CSVWorld::DataDisplayDelegate::buildPixmaps()
{
    if (!mPixmaps.empty())
        mPixmaps.clear();

    IconList::iterator it = mIcons.begin();

    while (it != mIcons.end())
    {
        mPixmaps.emplace_back(it->mValue, it->mIcon.pixmap(mIconSize));
        ++it;
    }
}

void CSVWorld::DataDisplayDelegate::setIconSize(const QSize& size)
{
    mIconSize = size;
    buildPixmaps();
}

void CSVWorld::DataDisplayDelegate::setTextLeftOffset(int offset)
{
    mTextLeftOffset = offset;
}

QSize CSVWorld::DataDisplayDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QSize size = EnumDelegate::sizeHint(option, index);

    int valueIndex = getValueIndex(index);
    if (valueIndex != -1)
    {
        if (mDisplayMode == Mode_IconOnly)
        {
            size.setWidth(mIconSize.width() + 2 * mHorizontalMargin);
        }
        else if (mDisplayMode == Mode_IconAndText)
        {
            size.setWidth(size.width() + mIconSize.width() + mTextLeftOffset);
        }

        if (mDisplayMode != Mode_TextOnly)
        {
            size.setHeight(qMax(size.height(), mIconSize.height()));
        }
    }
    return size;
}

void CSVWorld::DataDisplayDelegate::paint(
    QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    painter->save();

    // default to enum delegate's paint method for text-only conditions
    if (mDisplayMode == Mode_TextOnly)
        EnumDelegate::paint(painter, option, index);
    else
    {
        int valueIndex = getValueIndex(index);
        if (valueIndex != -1)
        {
            paintIcon(painter, option, valueIndex);
        }
    }

    painter->restore();
}

void CSVWorld::DataDisplayDelegate::paintIcon(QPainter* painter, const QStyleOptionViewItem& option, int index) const
{
    QRect iconRect = option.rect;
    QRect textRect = iconRect;

    iconRect.setLeft(iconRect.left() + mHorizontalMargin);
    iconRect.setRight(option.rect.right() - mHorizontalMargin);
    if (mDisplayMode == Mode_IconAndText)
    {
        iconRect.setWidth(mIconSize.width());
        textRect.setLeft(iconRect.right() + mTextLeftOffset);
        textRect.setRight(option.rect.right() - mHorizontalMargin);

        QString text = option.fontMetrics.elidedText(mValues.at(index).second, option.textElideMode, textRect.width());
        QApplication::style()->drawItemText(
            painter, textRect, Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text);
    }
    QApplication::style()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, mPixmaps.at(index).second);
}

void CSVWorld::DataDisplayDelegate::updateDisplayMode(const std::string& mode)
{
    if (mode == "Icon and Text")
        mDisplayMode = Mode_IconAndText;

    else if (mode == "Icon Only")
        mDisplayMode = Mode_IconOnly;

    else if (mode == "Text Only")
        mDisplayMode = Mode_TextOnly;
}

void CSVWorld::DataDisplayDelegate::settingChanged(const CSMPrefs::Setting* setting)
{
    if (*setting == mSettingKey)
        updateDisplayMode(setting->toString());
}

void CSVWorld::DataDisplayDelegateFactory::add(int enumValue, const QString& enumName, const QString& iconFilename)
{
    EnumDelegateFactory::add(enumValue, enumName);

    Icon icon;
    icon.mValue = enumValue;
    icon.mName = enumName;
    icon.mIcon = Misc::ScalableIcon::load(iconFilename);

    for (auto it = mIcons.begin(); it != mIcons.end(); ++it)
    {
        if (it->mName > enumName)
        {
            mIcons.insert(it, icon);
            return;
        }
    }

    mIcons.push_back(icon);
}

CSVWorld::CommandDelegate* CSVWorld::DataDisplayDelegateFactory::makeDelegate(
    CSMWorld::CommandDispatcher* dispatcher, CSMDoc::Document& document, QObject* parent) const
{
    return new DataDisplayDelegate(mValues, mIcons, dispatcher, document, "", "", parent);
}
