#include "knobgadget.h"
using namespace Xybrid::UI;

#include "ui/gadgets/layoutgadget.h"
#include "nodelib/basics.h"

#include <cmath>

#include <QDebug>
#include <QPainter>
#include <QRadialGradient>
#include <QGraphicsSceneMouseEvent>

template KnobGadget* KnobGadget::bind(double&);
template KnobGadget* KnobGadget::bind(float&);
template KnobGadget* KnobGadget::bind(int8_t&);
template KnobGadget* KnobGadget::bind(uint8_t&);
template KnobGadget* KnobGadget::bind(int16_t&);
template KnobGadget* KnobGadget::bind(uint16_t&);
template KnobGadget* KnobGadget::bind(int32_t&);
template KnobGadget* KnobGadget::bind(uint32_t&);

template KnobGadget* KnobGadget::bind(std::atomic<double>&);
template KnobGadget* KnobGadget::bind(std::atomic<float>&);
template KnobGadget* KnobGadget::bind(std::atomic<int8_t>&);
template KnobGadget* KnobGadget::bind(std::atomic<uint8_t>&);
template KnobGadget* KnobGadget::bind(std::atomic<int16_t>&);
template KnobGadget* KnobGadget::bind(std::atomic<uint16_t>&);
template KnobGadget* KnobGadget::bind(std::atomic<int32_t>&);
template KnobGadget* KnobGadget::bind(std::atomic<uint32_t>&);

double KnobGadget::get() {
    double v = fGet();
    if (v != lastVal) {
        lastVal = v;
        dirty = true;
    }
    return v;
}

KnobGadget::KnobGadget(QGraphicsItem* parent) : Gadget(parent) {
    static QFont f("Arcon Rounded", 7);

    label = new QGraphicsSimpleTextItem(this);
    valLabel = new QGraphicsSimpleTextItem(this);
    label->setBrush(QColor(255, 255, 255));
    valLabel->setBrush(QColor(255, 255, 255));
    label->setFont(f);
    valLabel->setFont(f);
    //label->setScale(0.8);
    //valLabel->setScale(0.8);
}

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

QRectF KnobGadget::layoutBoundingRect() const {
    constexpr qreal lh = 14;
    return boundingRect().adjusted(0, -lh, 0, lh);
}

QPainterPath KnobGadget::shape() const {
    QPainterPath p;
    p.addEllipse(boundingRect());
    return p;
}

void KnobGadget::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) {
    auto val = get();
    auto proportion = (val - min) / (max - min);

    auto r = boundingRect();
    auto ir = r - QMarginsF(2, 2, 2, 2);

    if (dirty) {
        dirty = false;

        valLabel->setText(fText(val));

        const qreal pad = 2;

        auto ls = label->boundingRect().size() * label->scale();
        label->setPos(r.center().x() - ls.width() / 2.0, r.height() + pad);
        auto vls = valLabel->boundingRect().size() * valLabel->scale();
        valLabel->setPos(r.center().x() - vls.width() / 2.0, -vls.height() - pad);
    }

    QColor outline = QColor(31, 31, 31);
    if (highlighted) outline = QColor(127, 127, 255);
    QColor needle = QColor(127, 127, 127);

    // base circle
    painter->setPen(Qt::NoPen);
    painter->setBrush(outline);
    painter->drawEllipse(r);

    // range
    painter->setBrush(QColor(63, 63, 63));
    painter->drawPie(ir, 250*16, -320*16);

    // fill
    painter->setBrush(QColor(95, 95, 95));
    painter->drawPie(ir, 250*16, static_cast<int>(-320.0*16*proportion));

    // needle
    QRadialGradient gr = QRadialGradient(ir.center(), ir.width() / 2.0, ir.center());
    gr.setColorAt(0.0, QColor(0, 0, 0, 0));
    gr.setColorAt(1.0, needle);

    painter->setPen(QPen(gr, 1.25));
    painter->setBrush(Qt::NoBrush);
    painter->drawPie(ir, 250*16 + static_cast<int>(-320.0*16*proportion), 0);
}

void KnobGadget::mousePressEvent(QGraphicsSceneMouseEvent* e) {
    if (e->button() == Qt::LeftButton) {
        highlighted = true;
        startVal = get();
        if (step != 0.0) {
            startVal = std::round(startVal / step) * step;
        }
    } else if (e->button() == Qt::RightButton) {
        fSet(defaultVal);
        e->accept();
    }
    update();
}

void KnobGadget::mouseReleaseEvent(QGraphicsSceneMouseEvent* e) {
    if (e->button() == Qt::LeftButton) {
        highlighted = false;
    }
    update();
}

void KnobGadget::mouseMoveEvent(QGraphicsSceneMouseEvent* e) {
    if (highlighted) {
        auto tdelta = -(e->screenPos().y() - e->buttonDownScreenPos(Qt::LeftButton).y());
        tdelta /= stepPx;
        fSet(std::clamp(startVal + tdelta * step, min, max));
    }
    update();
}

void KnobGadget::contextMenuEvent(QGraphicsSceneContextMenuEvent* e) {
    e->accept();
}

void KnobGadget::autoCreate(LayoutGadget* l, NodeLib::ADSR& adsr) {
    KnobGadget* k;

    k = new KnobGadget(l);
    k->min = 0.0;
    k->max = 5.0;
    k->step = .01;
    k->bind(adsr.a);
    k->setLabel("Attack");

    k = new KnobGadget(l);
    k->min = 0.0;
    k->max = 5.0;
    k->step = .01;
    k->bind(adsr.d);
    k->setLabel("Decay");

    k = new KnobGadget(l);
    k->min = 0.0;
    k->max = 1.0;
    k->defaultVal = 1.0;
    k->step = .01;
    k->bind(adsr.s);
    k->setLabel("Sustain");

    k = new KnobGadget(l);
    k->min = 0.0;
    k->max = 5.0;
    k->step = .01;
    k->bind(adsr.r);
    k->setLabel("Release");
}
