#include "sampleselectorgadget.h"
using Xybrid::UI::SampleSelectorGadget;

#include "data/project.h"
#include "data/sample.h"
using namespace Xybrid::Data;

#include "ui/directorynode.h"
#include "ui/waveformpreviewwidget.h"
using namespace Xybrid::UI;

#include "config/colorscheme.h"

#include "util/strings.h"

#include <cmath>
#include <vector>

#include <QDebug>

#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QStyleOptionGraphicsItem>
#include <QMenu>
#include <QWidgetAction>

SampleSelectorGadget::SampleSelectorGadget(Data::Project* project, QGraphicsItem* parent) : Gadget(parent), project(project) {
    setAcceptHoverEvents(true);
}

QRectF SampleSelectorGadget::boundingRect() const {
    return {QPointF(0, 0), size};
}

namespace {
    void paintChannel(QPainter* p, std::shared_ptr<Sample> smp, size_t ch, QRect rect) {
        auto x = rect.x();
        auto y = rect.y();
        auto h = rect.height();
        auto c = h / 2;
        double scale = static_cast<double>(smp->length()) / static_cast<double>(rect.width());
        for (int i = 0; i < rect.width(); i++) {
            size_t sp1 = static_cast<size_t>(std::floor(static_cast<double>(i) * scale));
            size_t sp2 = static_cast<size_t>(std::floor(static_cast<double>(i+1) * scale)); // would be - 1, but this makes sure adjacent pixels always share at least one value
            auto v = smp->plotBetween(ch, sp1, sp2);
            v[0] = std::clamp(v[0], -1.0f, 1.0f);
            v[1] = std::clamp(v[1], -1.0f, 1.0f);
            //qDebug() << "x" << i << "v" << v[1] << v[2];
            p->drawLine(QPoint(x+i, static_cast<int>(y+c - (c * v[0]))), QPoint(x+i, static_cast<int>(y+c - (c * v[1]))));
        }
    }

}

void SampleSelectorGadget::paint(QPainter* p, const QStyleOptionGraphicsItem* opt, QWidget*) {
    const qreal corner = 5;
    auto r = boundingRect();
    auto br = r; // border rect
    bool hover = opt->state & QStyle::State_MouseOver;

    QColor outline = QColor(31, 31, 31);
    if (hover) outline = QColor(127, 127, 255);

    p->setPen(QPen(Qt::NoPen));
    p->setBrush(QBrush(Config::colorScheme.waveformBg));
    p->drawRoundedRect(br, corner, corner);

    r.adjust(2, 2, -2, -2);
    //p->fillRect(r, Config::colorScheme.waveformBg);

    QString name = qs("(no sample selected)");

    // draw waveforms
    //auto aa = p->renderHints() & QPainter::Antialiasing;
    p->setRenderHint(QPainter::Antialiasing, false);
    p->setPen(QPen(Config::colorScheme.waveformFgPrimary));
    if (auto smp = currentSample.lock(); smp) {
        name = smp->name;
        auto chs = smp->numChannels();
        if (chs == 1) paintChannel(p, smp, 0, r.toAlignedRect());
        else if (chs == 2) {
            auto cs = r.height() / 2;
            auto cr = r.adjusted(0, 0, 0, -cs);
            paintChannel(p, smp, 0, cr.toAlignedRect());
            paintChannel(p, smp, 1, cr.translated(0, cs).toAlignedRect());
        }
    }
    p->setRenderHint(QPainter::Antialiasing, true);
    p->setRenderHint(QPainter::HighQualityAntialiasing, true);
    p->setPen(QPen(outline, 2));
    p->setBrush(QBrush(Qt::NoBrush));
    p->drawRoundedRect(br, corner, corner);

    // draw label
    QFont font("Arcon Rounded", 8);
    QFontMetricsF fm(font);
    name = fm.elidedText(name.section('/', -1, -1), Qt::ElideRight, static_cast<int>(r.width() - 4));
    QPainterPath path;
    path.addText(r.bottomLeft() + QPointF(2, -fm.descent()), font, name);

    p->fillPath(path, QColor(0, 0, 0));
    p->strokePath(path, QPen(QColor(0, 0, 0), 3));
    p->fillPath(path, QColor(255, 255, 255));
}

void SampleSelectorGadget::buildSubmenu(DirectoryNode* dir, QMenu* menu) {
    auto smp = currentSample.lock();
    bool needSeparator = false;
    for (auto c : dir->children) {
        if (c->isDirectory()) {
            needSeparator = true;
            buildSubmenu(c, menu->addMenu(c->name));
        } else {
            if (needSeparator) { menu->addSeparator(); needSeparator = false; }
            auto s = project->samples[c->data.toUuid()];
            auto wa = new QWidgetAction(menu);
            auto wfp = new WaveformPreviewWidget(menu);
            wa->setDefaultWidget(wfp);
            wfp->showName = true;
            wfp->highlightable = true;
            wfp->setSample(s);
            wfp->setMinimumSize(192, 48);
            menu->addAction(wa);
            if (s == smp) wa->setDisabled(true);
            else connect(wa, &QWidgetAction::triggered, [this, s] { setSample(s); });
        }
    }
}

void SampleSelectorGadget::mousePressEvent(QGraphicsSceneMouseEvent* e) {
    if (!project) return;

    if (e->button() == Qt::LeftButton) {
        auto smp = currentSample.lock();
        auto* m = new QMenu();
        if (project->samples.empty()) m->addAction("(no samples in project)")->setDisabled(true);
        else {
            m->addAction("(no sample)", [this] { setSample(nullptr); });
            m->addSeparator();
            DirectoryNode root;
            for (auto s : project->samples) root.placeData(s->name, s->uuid);
            root.sortTree();

            buildSubmenu(&root, m);
        }
        m->setAttribute(Qt::WA_DeleteOnClose);
        m->popup(e->screenPos());
    }
}

void SampleSelectorGadget::hoverEnterEvent(QGraphicsSceneHoverEvent*) { update(); }
void SampleSelectorGadget::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { update(); }
