// Squawk messenger. 
// Copyright (C) 2019  Yury Gubich <blue@macaw.me>
// 
// 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 3 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/>.

#include "keydelegate.h"
#include <QPainter>
#include <QDebug>

#include "ui/models/info/omemo/keys.h"
#include <shared/global.h>

constexpr uint8_t margin = 10;
constexpr uint8_t partSize = 8;
constexpr uint8_t maxSingleLineParts = 3;

UI::KeyDelegate::KeyDelegate(QObject* parent):
    QStyledItemDelegate(parent),
    defaultFont(Shared::Global::getInstance()->defaultFont),
    fingerPrintFont(Shared::Global::getInstance()->monospaceFont),
    labelFont(Shared::Global::getInstance()->smallFont),
    defaultMetrics(Shared::Global::getInstance()->defaultFontMetrics),
    fingerPrintMetrics(Shared::Global::getInstance()->monospaceMetrics),
    labelMetrics(Shared::Global::getInstance()->smallFontMetrics),
    spaceWidth(fingerPrintMetrics.horizontalAdvance(" "))
{}

UI::KeyDelegate::~KeyDelegate() {}


void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing, true);

    QRect r;
    int maxRight = 0;
    int leftOrigin = option.rect.left() + margin;
    QColor q = painter->pen().color();
    q.setAlpha(180);
    QRect rect = option.rect;
    bool hover = option.state & QStyle::State_MouseOver;
    if (hover) {
        painter->save();
        painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight));
        painter->restore();
    }

    QVariant dirtyV = index.data(Models::Keys::Dirty);
    if (dirtyV.isValid() && dirtyV.toBool()) {
        painter->save();
        rect.setWidth(margin / 2);
        painter->fillRect(rect, option.palette.brush(QPalette::Active, QPalette::Highlight));
        rect.setWidth(option.rect.width());
        painter->restore();
    }

    rect.adjust(margin, margin, -margin, -margin);
    QVariant labelV = index.data(Models::Keys::Label);
    if (labelV.isValid()) {
        QString label = labelV.toString();
        if (label.size() > 0) {
            painter->save();
            painter->setFont(labelFont);
            painter->setPen(q);
            painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, label, &r);
            rect.adjust(0, labelMetrics.lineSpacing(), 0, 0);
            maxRight = std::max(r.width(), maxRight);
            painter->restore();
        }
    }

    QVariant fingerPrintV = index.data(Models::Keys::FingerPrint);
    if (fingerPrintV.isValid()) {
        painter->save();
        painter->setFont(fingerPrintFont);
        QByteArray fingerPrint = fingerPrintV.toByteArray();
        std::vector<QString> parts = getParts(fingerPrint);
        uint8_t partsLength = parts.size();
        uint8_t firstLine;
        if (partsLength > maxSingleLineParts)
            firstLine = partsLength / 2 + partsLength % 2;
        else
            firstLine = partsLength;

        for (uint8_t i = 0; i < partsLength; ++i) {
            if (i == firstLine) {
                maxRight = std::max(rect.left() - leftOrigin - margin, maxRight);
                rect.setLeft(leftOrigin);
                rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0);
            }
            painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, parts[i], &r);
            rect.adjust(r.width() + spaceWidth ,0, 0, 0);
        }

        maxRight = std::max(rect.left() - leftOrigin - margin, maxRight);
        rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0);
        rect.setLeft(leftOrigin);

        painter->restore();
    }


    QVariant lastV = index.data(Models::Keys::LastInteraction);
    if (lastV.isValid()) {
        QDateTime last = lastV.toDateTime();
        if (last.isValid()) {
            painter->save();
            painter->setFont(labelFont);
            painter->setPen(q);
            painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, last.toString(), &r);
            rect.adjust(0, labelMetrics.lineSpacing(), 0, 0);
            maxRight = std::max(r.width(), maxRight);
            painter->restore();
        }
    }

    QVariant levelV = index.data(Models::Keys::TrustLevel);
    if (levelV.isValid()) {
        Shared::TrustLevel level = static_cast<Shared::TrustLevel>(levelV.toUInt());
        QString levelName = Shared::Global::getName(level);

        if (maxRight > 0)
            maxRight += margin;
        rect.setLeft(leftOrigin + maxRight);
        rect.setTop(option.rect.top() + maxRight);
        rect.setBottom(option.rect.bottom() - maxRight);
        painter->setFont(defaultFont);
        painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, levelName, &r);
    }


    painter->restore();
}

QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
    QSize size = QStyledItemDelegate::sizeHint(option, index);

    int mh = margin * 2;
    int mw = margin * 2;
    int width = 0;

    QVariant labelV = index.data(Models::Keys::Label);
    if (labelV.isValid()) {
        QString label = labelV.toString();
        if (label.size() > 0) {
            mh += labelMetrics.lineSpacing();
            width = labelMetrics.horizontalAdvance(label);
        }
    }

    QVariant fingerPrintV = index.data(Models::Keys::FingerPrint);
    if (fingerPrintV.isValid()) {
        QString hex = fingerPrintV.toByteArray().toHex();
        uint8_t parts = hex.size() / partSize;

        mh += fingerPrintMetrics.height();
        if (parts > maxSingleLineParts)
            mh += fingerPrintMetrics.height() + fingerPrintMetrics.leading();

        uint8_t firstLine;
        if (parts > maxSingleLineParts)
            firstLine = parts / 2 + parts % 2;
        else
            firstLine = parts;

        width = std::max(width, firstLine * fingerPrintMetrics.horizontalAdvance(hex, partSize) + (firstLine - 1) * spaceWidth);
        width += 1;     //there is a mistake somewhere, this the cheapest way to compensate it
    }
    QVariant lastV = index.data(Models::Keys::LastInteraction);
    if (lastV.isValid()) {
        QDateTime last = lastV.toDateTime();
        if (last.isValid()) {
            mh += labelMetrics.lineSpacing();
            QString dt = last.toString();
            width = std::max(labelMetrics.horizontalAdvance(dt), width);
        }
    }

    QVariant levelV = index.data(Models::Keys::TrustLevel);
    if (levelV.isValid()) {
        Shared::TrustLevel level = static_cast<Shared::TrustLevel>(levelV.toUInt());
        QString levelName = Shared::Global::getName(level);
        if (width > 0)
            width += margin;

        width += defaultMetrics.horizontalAdvance(levelName);
        width += 1;     //there is a mistake somewhere, this the cheapest way to compensate it
    }

    mw += width;
    if (size.width() < mw)
        size.setWidth(mw);

    if (size.height() < mh)
        size.setHeight(mh);

    return size;
}

std::vector<QString> UI::KeyDelegate::getParts(const QByteArray& data) {
    QString hex = data.toHex();
    uint8_t parts = hex.size() / partSize;
    std::vector<QString> result(parts);
    for (uint8_t i = 0; i < parts; ++i) {
        uint16_t index = i * partSize;
        uint8_t length = partSize;
        if (index + length > hex.size())
            length = hex.size() - index;
        QStringRef part(&hex, index, length);
        result[i] = part.toString();
    }

    return result;
}