/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "MvQMethods.h"

#include <QtGlobal>
#include <QApplication>
#include <QGuiApplication>
#include <QClipboard>

#include <QAbstractButton>
#include <QAbstractItemModel>
#include <QAction>
#include <QButtonGroup>
#include <QComboBox>
#include <QDebug>
#include <QFile>
#include <QFontDatabase>
#include <QFontMetrics>
#include <QHBoxLayout>
#include <QImage>
#include <QImageReader>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <QPainter>
#include <QStackedWidget>
#include <QSvgRenderer>
#include <QTabBar>
#include <QTabWidget>
#include <QTreeView>
#include <QVBoxLayout>
#include <QOpenGLContext>

#include "MvMiscellaneous.h"
#include "MvQTheme.h"
#ifdef METVIEW
#include "MvQPanel.h"
#include "MvQPalette.h"
#endif

namespace MvQ
{
void initComboBox(QSettings& settings, QString key, QComboBox* cb)
{
    Q_ASSERT(cb);
    QString txt = settings.value(key).toString();
    for (int i = 0; i < cb->count(); i++) {
        if (cb->itemText(i) == txt) {
            cb->setCurrentIndex(i);
            return;
        }
    }

    if (cb->currentIndex() == -1)
        cb->setCurrentIndex(0);
}

void initComboBoxByData(QSettings& settings, QString key, QComboBox* cb)
{
    Q_ASSERT(cb);
    QString txt = settings.value(key).toString();
    for (int i = 0; i < cb->count(); i++) {
        if (cb->itemData(i).toString() == txt) {
            cb->setCurrentIndex(i);
            return;
        }
    }

    if (cb->currentIndex() == -1)
        cb->setCurrentIndex(0);
}

void initTabId(QSettings& settings, QString key, QTabWidget* tab)
{
    Q_ASSERT(tab);

    QString s = settings.value(key).toString();
    for (int i = 0; i < tab->count(); i++) {
        if (tab->tabBar()->tabData(i).toString() == s) {
            tab->setCurrentIndex(i);
            return;
        }
    }
}

void saveTabId(QSettings& settings, QString key, QTabWidget* tab)
{
    Q_ASSERT(tab);

    int idx = tab->currentIndex();
    if (idx >= 0) {
        settings.setValue(key, tab->tabBar()->tabData(idx).toString());
    }
}

void initTreeColumnWidth(QSettings& settings, QString key, QTreeView* tree)
{
    Q_ASSERT(tree);

    QStringList dataColumns = settings.value(key).toStringList();
    for (int i = 0; i < tree->model()->columnCount() - 1 && i < dataColumns.size(); i++) {
        tree->setColumnWidth(i, dataColumns[i].toInt());
    }
}

void saveTreeColumnWidth(QSettings& settings, QString key, QTreeView* tree)
{
    QStringList dataColumns;
    for (int i = 0; i < tree->model()->columnCount() - 1; i++) {
        dataColumns << QString::number(tree->columnWidth(i));
    }
    settings.setValue(key, dataColumns);
}


void initStacked(QSettings& settings, QString key, QStackedWidget* stacked)
{
    Q_ASSERT(stacked);

    int v = settings.value(key).toInt();
    if (v >= 0 && v < stacked->count())
        stacked->setCurrentIndex(v);
}

void initButtonGroup(QSettings& settings, QString key, QButtonGroup* bg)
{
    Q_ASSERT(bg);

    int v = settings.value(key).toInt();
    if (v >= 0 && v < bg->buttons().count()) {
        bg->buttons().at(v)->setChecked(true);
        bg->buttons().at(v)->click();
    }
}

void initCheckableAction(QSettings& settings, QString key, QAction* ac)
{
    Q_ASSERT(ac);

    if (settings.contains(key)) {
        ac->setChecked(settings.value(key).toBool());
    }
}


void showTabLabel(QTabWidget* tab, int index, QPixmap pix)
{
    Q_ASSERT(tab);
    QWidget* w = tab->tabBar()->tabButton(index, QTabBar::RightSide);
    QLabel* label = nullptr;
    if (w) {
        // qDebug() << QString(w->metaObject()->className());
        Q_ASSERT(QString(w->metaObject()->className()) == "QLabel");
    }
    else {
        label = new QLabel(tab);
        label->setPixmap(pix);
        tab->tabBar()->setTabButton(index, QTabBar::RightSide, label);
    }
}

void hideTabLabel(QTabWidget* tab, int index)
{
    Q_ASSERT(tab);
    QWidget* w = tab->tabBar()->tabButton(index, QTabBar::RightSide);
    if (w) {
        Q_ASSERT(QString(w->metaObject()->className()) == "QLabel");
        tab->tabBar()->setTabButton(index, QTabBar::RightSide, nullptr);
        delete w;
    }
}

QString formatBoldText(QString txt, QColor col)
{
    if (!col.isValid()) {
        return "<b>" + txt + "</b>";
    }
    return "<b><font color=\'" + col.name() + "\'>" + txt + "</font></b>";
}

QString formatText(QString txt, QColor col, bool bold)
{
    if (!col.isValid()) {
        return (!bold) ? txt : ("<b>" + txt + "</b>");
    }

    return "<font color=\'" + col.name() + "\'>" + ((!bold) ? txt : ("<b>" + txt + "</b>")) + "</font>";
}

QString formatTableThText(QString txt, QColor col)
{
    if (!col.isValid()) {
        return "<th>" + txt + "</th>";
    }
    return "<th><font color=\'" + col.name() + "\'>" + txt + "</font></th>";
}

QString formatTableTdText(QString txt, QColor col)
{
    if (!col.isValid()) {
        return "<td>" + txt + "</td>";
    }
    return "<td><font color=\'" + col.name() + "\'>" + txt + "</font></td>";
}

QString formatTableTrText(QString txt)
{
    return "<tr>" + txt + "</tr>";
}

QString formatTableRow(QString col1Text, QString col2Text, bool boldCol1)
{
    QString txt;
    if (boldCol1)
        txt = "<td>" + formatBoldText(col1Text) + "</td>";
    else
        txt = formatTableTdText(col1Text);

    txt += formatTableTdText(col2Text);

    return formatTableTrText(txt);
}

void toClipboard(QString txt)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QClipboard* cb = QGuiApplication::clipboard();
    cb->setText(txt, QClipboard::Clipboard);
    cb->setText(txt, QClipboard::Selection);
#else
    QClipboard* cb = QApplication::clipboard();
    cb->setText(txt, QClipboard::Clipboard);
    cb->setText(txt, QClipboard::Selection);
#endif
}

QString fromClipboard()
{
    return QGuiApplication::clipboard()->text();
}


void initSettings(QString path)
{
#ifdef __APPLE__
    QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, path);
#else
    QSettings::setPath(QSettings::NativeFormat, QSettings::UserScope, path);
#endif
}

QString iconPixmapPath(QString name)
{
    QString svg = QString::fromStdString(metview::iconDirFile(name.toStdString() + ".svg"));

    QFile f(svg);
    if (f.exists()) {
        return svg;
    }
    else {
        QString xpm = QString::fromStdString(metview::iconDirFile(name.toStdString() + ".xpm"));
        QFile fxpm(xpm);
        if (fxpm.exists()) {
            return xpm;
        }
    }

    return {};
}

void setLineEditTextFormat(QLineEdit* lineEdit, const QList<QTextLayout::FormatRange>& formats)
{
    if (!lineEdit)
        return;

    QList<QInputMethodEvent::Attribute> attributes;
    foreach (const QTextLayout::FormatRange& fr, formats) {
        QInputMethodEvent::AttributeType type = QInputMethodEvent::TextFormat;
        int start = fr.start - lineEdit->cursorPosition();
        int length = fr.length;
        QVariant value = fr.format;
        attributes.append(QInputMethodEvent::Attribute(type, start, length, value));
    }
    QInputMethodEvent event(QString(), attributes);
    QCoreApplication::sendEvent(lineEdit, &event);
}

void clearLineEditTextFormat(QLineEdit* lineEdit)
{
    setLineEditTextFormat(lineEdit, QList<QTextLayout::FormatRange>());
}

bool hasOpenGLSupport()
{
    QOpenGLContext* current = QOpenGLContext::currentContext();
    if (current && current->isValid())
        return true;

    QOpenGLContext ctx;
    return (ctx.create() == true);
}

void changeFontSize(QWidget* w, int delta)
{
    QFont f = w->font();
    int fs = f.pointSize();

    if (delta > 0) {
        if (fs >= 100)
            return;

        if (fs + delta > 100)
            fs = 100;

        f.setPointSize(fs + delta);
        w->setFont(f);
    }
    else if (delta < 0) {
        if (fs <= 6)
            return;

        if (fs + delta < 6)
            fs = 6;

        f.setPointSize(fs + delta);
        w->setFont(f);
    }
}

void setFontSize(QWidget* w, int fsNew)
{
    QFont f = w->font();
    int fs = f.pointSize();

    if (fsNew <= 0)
        return;

    if (fsNew < 6)
        fsNew = 6;

    if (fsNew > 100)
        fsNew = 100;

    if (fs == fsNew)
        return;

    f.setPointSize(fsNew);
    w->setFont(f);
}

void showShortcutInContextMenu(QWidget* w)
{
    QList<QAction*> lst = w->findChildren<QAction*>();
    showShortcutInContextMenu(lst);
}

void showShortcutInContextMenu(QAction* ac)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
    ac->setShortcutVisibleInContextMenu(true);
#endif
}

void showShortcutInContextMenu(QList<QAction*> lst)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
    foreach (QAction* ac, lst) {
        ac->setShortcutVisibleInContextMenu(true);
    }
#endif
}

QString formatShortCut(QString text)
{
    return "<code><font color=\'" + MvQTheme::colour("shortcut").name() + "\'>" + text + "</font></code>";
}

QString formatShortCut(QAction* ac)
{
    Q_ASSERT(ac);

    return "<code><font color=\'" + MvQTheme::colour("shortcut").name() + "\'>&nbsp;(" +
           ac->shortcut().toString() + ")</font></code>";
}

void addShortCutToToolTip(QWidget* w)
{
    QList<QAction*> lst = w->findChildren<QAction*>();
    addShortCutToToolTip(lst);
}

void addShortCutToToolTip(QList<QAction*> alst)
{
    Q_FOREACH (QAction* ac, alst) {
        if (!ac->shortcut().isEmpty()) {
            QString tt = ac->toolTip();
            if (!tt.isEmpty()) {
                tt += " " + formatShortCut(ac);
                ac->setToolTip(tt);
            }
        }
    }
}

int textWidth(const QFontMetrics& fm, QString txt, int len)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
    return fm.horizontalAdvance(txt, len);
#else
    return fm.width(txt, len);
#endif
}

int textWidth(const QFontMetrics& fm, QChar ch)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
    return fm.horizontalAdvance(ch);
#else
    return fm.width(ch);
#endif
}
QPixmap makePixmap(QString path, int width, int height, QColor bg)
{
    QImageReader imgR(path);
    if (imgR.canRead()) {
        imgR.setScaledSize(QSize(width, height));
        QImage img = imgR.read();
        if (bg.isValid()) {
            QPixmap pix(img.size());
            pix.fill(bg);
            QPainter painter(&pix);
            painter.drawImage(QPoint(0, 0), img);
            return pix;
        }
        return QPixmap::fromImage(img);
    }

    return {width, height};
}

QPixmap makePixmapFromSvgTemplate(QString path, int width, int height, QColor col, QString templateCol,
                                  QString txt, QString templateTxt)
{
    QFile file(path);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        return {width, height};
    }

    QString t = file.readAll();
    file.close();

    if (!templateCol.isEmpty() && col.isValid()) {
        t = t.replace(templateCol, col.name());
    }

    if (!templateTxt.isEmpty() && !txt.isEmpty()) {
        t = t.replace(templateTxt, txt);
    }

    QSvgRenderer r(t.toUtf8());
    QPixmap pix(width, height);
    pix.fill(Qt::transparent);

    QPainter painter(&pix);
    auto rect = QRect(0, 0, width, height);
    r.render(&painter, rect);
    return pix;
}

QPixmap fitPixmapToSize(QSize targetSize, QPixmap p, QColor bg)
{
    float w = p.width();
    float h = p.height();

    // We copy the image into a pixmap with the maximum size
    float hMax = targetSize.height() - 6;
    float wMax = 2 * hMax;
    QPixmap pix;
    if (h * wMax / w <= hMax) {
        pix = p.scaledToWidth(wMax, Qt::SmoothTransformation);
    }
    else {
        pix = p.scaledToHeight(hMax, Qt::SmoothTransformation);
    }

    // Define the pixmap rectangle
    int pw = pix.width();
    int ph = pix.height();
    int pdy = (targetSize.height() - ph) / 2;
    // int pdx = (pixSize_.width()  - pw) / 2;
    int pdx = 3;
    QRect pixRect(pdx, pdy, pw, ph);

    // We create a pixmap with a given background
    QPixmap pixRes = QPixmap(targetSize);
    pixRes.fill(bg);

    // Paint the image pixmap into the middle
    QPainter painter(&pixRes);
    painter.drawPixmap(pixRect, pix);

    return pixRes;
}

#ifdef METVIEW
QPen makePen(const char* style, int width, const char* colour,
             Qt::PenCapStyle cap, Qt::PenJoinStyle join)
{
    Qt::PenStyle lstyle(Qt::NoPen);
    QColor lcolour;

    static std::map<std::string, Qt::PenStyle> styleMap = {
        {"SOLID", Qt::SolidLine},
        {"DASH", Qt::DashLine},
        {"DOT", Qt::DotLine},
        {"DASH_DOT", Qt::DashDotLine},
        {"DASH_DOT_DOT", Qt::DashDotDotLine},
        {"NOPEN", Qt::NoPen}};

    if (style) {
        auto it = styleMap.find(std::string(style));
        if (it != styleMap.end())
            lstyle = it->second;
    }

    if (colour) {
        lcolour = MvQPalette::magics(colour);
    }

    return QPen(lcolour, width, lstyle, cap, join);
}

QBrush makeBrush(const char* style, const char* colour)
{
    Qt::BrushStyle lstyle(Qt::NoBrush);
    QColor lcolour;

    static std::map<std::string, Qt::BrushStyle> styleMap = {
        {"SOLID", Qt::SolidPattern},
        {"NOBRUSH", Qt::NoBrush},
        {"DENSE1", Qt::Dense1Pattern},
        {"DENSE2", Qt::Dense2Pattern},
        {"DENSE3", Qt::Dense3Pattern},
        {"DENSE4", Qt::Dense4Pattern},
        {"DENSE5", Qt::Dense5Pattern},
        {"DENSE6", Qt::Dense6Pattern},
        {"DENSE7", Qt::Dense7Pattern},
        {"HOR", Qt::HorPattern},
        {"VER", Qt::VerPattern},
        {"CROSS", Qt::CrossPattern},
        {"BDIAG", Qt::BDiagPattern},
        {"FDIAG", Qt::FDiagPattern},
        {"DIAG_CROSS", Qt::DiagCrossPattern},
        {"LINEAR_GRADIENT", Qt::LinearGradientPattern},
        {"CONICAL_GRADIENT", Qt::ConicalGradientPattern},
        {"RADIAL_GRADIENT", Qt::RadialGradientPattern},
    };

    if (style) {
        auto it = styleMap.find(std::string(style));
        if (it != styleMap.end())
            lstyle = it->second;
    }

    if (colour) {
        lcolour = MvQPalette::magics(colour);
    }

    return QBrush(lcolour, lstyle);
}
#endif

QFont makeFont(const std::string& family, const std::string& style, int fontSize)
{
    QFont font(QString::fromStdString(family));
    font.setPointSize(fontSize);
    if (style == "UNDERLINE") {
        font.setUnderline(true);
    }
    else if (style == "BOLD") {
        font.setBold(true);
    }
    else if (style == "ITALIC") {
        font.setItalic(true);
    }
    else if (style == "BOLDITALIC") {
        font.setItalic(true);
        font.setBold(true);
    }

    return font;
}

QFont makeFont(const std::string& family, bool bold, bool italic, bool underline, int fontSize)
{
    QFont font(QString::fromStdString(family));
    font.setPointSize(fontSize);
    font.setBold(bold);
    font.setItalic(italic);
    font.setUnderline(underline);
    return font;
}

#ifdef METVIEW
QWidget* makeLabelPanel(QString name, QWidget* parent, int fontSizeDelta)
{
    QFont font = QFont();
    font.setPointSize(font.pointSize() - 1);

    // auto labelHolder = new MvQPanel(parent);
    auto labelHolder = new QWidget(parent);
    auto labelLayout = new QHBoxLayout(labelHolder);
    labelLayout->setContentsMargins(1, 1, 1, 1);
    auto label = new QLabel(name, labelHolder);
    // label->setProperty("panelStyle", "2");
    if (fontSizeDelta != 0) {
        QFont font = QFont();
        font.setPointSize(font.pointSize() + fontSizeDelta);
        label->setFont(font);
    }
    labelLayout->addWidget(label);
    return labelHolder;
}
#endif

QFont findMonospaceFont(bool keepDefaultSize)
{
    QStringList lst{"Menlo", "Monospace", "Courier", "Courier New", "Monaco"};

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    auto fLst = QFontDatabase::families();
#else
    QFontDatabase db = QFontDatabase();
    auto fLst = db.families();
#endif
    for (auto s : lst) {
        for (auto fMem : fLst) {
            if (fMem == s || fMem.startsWith(s + "[")) {
                QFont f(fMem);
                f.setFixedPitch(true);
                if (!keepDefaultSize) {
                    f.setPointSize(10);
                }
                return f;
            }
        }
    }

    QFont fr;
    if (!keepDefaultSize) {
        fr.setPointSize(10);
    }

    return fr;
}

void safeDrawLine(const QPointF& p1, const QPointF& p2, QPainter* painter)
{
    QPolygonF ppLine;
    ppLine << p1 << p2;
    painter->drawPolygon(ppLine);
}

void safeDrawLine(const QPoint& p1, const QPoint& p2, QPainter* painter)
{
    QPolygon ppLine;
    ppLine << p1 << p2;
    painter->drawPolygon(ppLine);
}

void drawPolyline(const QVector<QPointF>& pp, QPainter* painter)
{
    QPolygonF pg;
    for (auto i : pp) {
        pg << i;
    }
    painter->drawPolyline(pg);
}

void drawPolyline(const std::vector<double>& x, const std::vector<double>& y, QPainter* painter)
{
    QPolygonF pg;
    for (size_t i = 0; i < x.size(); i++) {
        pg << QPointF(x[i], y[i]);
    }
    painter->drawPolyline(pg);
}

void showMessageBox(const std::string& msg, MvLogLevel level)
{
    QMessageBox::Icon icon;
    QString title;
    switch (level) {
        case MvLogLevel::INFO:
            icon = QMessageBox::Information;
            title = "Info";
            break;
        case MvLogLevel::WARN:
            icon = QMessageBox::Warning;
            title = "Warning";
            break;
        case MvLogLevel::ERROR:
            icon = QMessageBox::Critical;
            title = "Error";
            break;
        case MvLogLevel::DBG:
            icon = QMessageBox::NoIcon;
            title = "Debug";
            break;
        default:
            icon = QMessageBox::NoIcon;
            title = "Info";
            break;
    }
    title = "Metview - " + title;
    QMessageBox box(icon, title, QString::fromStdString(msg));
    box.exec();
}

}  // namespace MvQ
