#include "waveformpreviewwidget.h"
using Xybrid::UI::WaveformPreviewWidget;

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

#include "config/colorscheme.h"

#include "util/strings.h"

#include <cmath>

#include <QDebug>

#include <QPaintEvent>
#include <QPainter>
#include <QStylePainter>
#include <QStyle>
#include <QStyleOptionFrame>
#include <QFocusFrame>

WaveformPreviewWidget::WaveformPreviewWidget(QWidget* parent) : QFrame(parent) {
    setFrameStyle(Shape::StyledPanel | Shadow::Sunken);
    setLineWidth(2);
}

void WaveformPreviewWidget::setSample(std::shared_ptr<Sample> smp) {
    currentSample = smp;
    //int ch = 1;
    //if (smp) ch = std::max(1, smp->numChannels());
    //setMinimumHeight(128 * ch);
    repaint(); // invalidate paint state
}

void WaveformPreviewWidget::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 WaveformPreviewWidget::paintEvent(QPaintEvent* event [[maybe_unused]]) {
    auto disabled = !isEnabled();

    QStylePainter p(this);
    p.setRenderHint(QPainter::RenderHint::Antialiasing, false); // ensure sharpness even when rendered in a QGraphicsScene
    QRect rect(QPoint(), size());
    auto lw = lineWidth();
    rect.adjust(lw, lw, -lw, -lw);

    if (highlighted && !disabled) p.fillRect(rect, Config::colorScheme.waveformBgHighlight);
    else p.fillRect(rect, Config::colorScheme.waveformBg);

    p.setPen(QPen(Config::colorScheme.waveformFgPrimary));
    if (disabled) p.setPen(Config::colorScheme.waveformFgPrimary.darker());

    QString name = qs("(no data)");
    if (auto smp = currentSample.lock(); smp) {
        name = smp->name;
        auto chs = smp->numChannels();
        if (chs == 1) paintChannel(p, smp, 0, rect);
        else if (chs == 2) {
            auto cs = rect.height() / 2;
            auto r = rect.adjusted(0, 0, 0, -cs);
            paintChannel(p, smp, 0, r);
            paintChannel(p, smp, 1, r.translated(0, cs));
        }
    }

    if (showName) {
        // draw label
        QFont font("Arcon Rounded", 8);
        QFontMetrics fm(font);
        name = fm.elidedText(name.section('/', -1, -1), Qt::ElideRight, rect.width() - 4);
        QPainterPath path;
        path.addText(rect.bottomLeft() + QPointF(2, -fm.descent()), font, name);

        p.setRenderHint(QPainter::Antialiasing, true);
        p.setRenderHint(QPainter::HighQualityAntialiasing, true);
        p.fillPath(path, QColor(0, 0, 0));
        p.strokePath(path, QPen(QColor(0, 0, 0), 3));
        p.fillPath(path, QColor(255, 255, 255));
    }

    setAttribute(Qt::WA_Hover, highlighted);
    QStyleOptionFrame optF;
    optF.initFrom(this);
    optF.lineWidth = 2;
    optF.state |= QStyle::State_Sunken;
    style()->drawPrimitive(QStyle::PE_Frame, &optF, &p, this);
}

void WaveformPreviewWidget::enterEvent(QEvent*) {
    if (highlightable) {
        highlighted = true;
        repaint();
    }
}

void WaveformPreviewWidget::leaveEvent(QEvent*) {
    if (highlightable) {
        highlighted = false;
        repaint();
    }
}
