#include "switch.h"

#include <QPainter>
#include <QMouseEvent>
#include <QEasingCurve>
#include <QStyleOption>
#include <QtMath>

Switch::Switch(QWidget *parent)
    : QAbstractButton(parent), m_anim(this, "offset")
{
    setCheckable(true);
    setCursor(Qt::PointingHandCursor);
    setFocusPolicy(Qt::StrongFocus);

    m_anim.setDuration(120);
    m_anim.setEasingCurve(QEasingCurve::InOutQuad);

    // Sync initial offset with checked state
    m_offset = isChecked() ? 1.0 : 0.0;
}

QSize Switch::sizeHint() const {
    return {m_width, m_height};
}

void Switch::setOffset(qreal v) {
    // Clamp to [0,1] without relying on C++17 std::clamp
    if (v < 0.0)
        v = 0.0;
    else if (v > 1.0)
        v = 1.0;
    if (qFuzzyCompare(m_offset, v)) return;
    m_offset = v;
    update();
    emit offsetChanged(m_offset);
}

void Switch::paintEvent(QPaintEvent *) {
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing, true);

    const QRectF r = rect();
    const qreal h = r.height();
    const qreal w = r.width();
    const qreal trackRadius = h / 2.0;
    const qreal knobRadius = (h / 2.0) - m_margin;

    p.setPen(Qt::NoPen);
    const QRectF trackRect = r.adjusted(0.5, 0.5, -0.5, -0.5);
    if (isEnabled() && isChecked()) {
        // Linear gradient ~104.04deg from #B0E237 to #00BFFF
        const qreal angleDeg = 104.04;
        const qreal theta = qDegreesToRadians(angleDeg);
        const QPointF center = trackRect.center();
        const qreal halfDiag = 0.5 * qSqrt(w*w + h*h);
        const QPointF dir(qCos(theta), qSin(theta));
        const QPointF start = center - dir * halfDiag;
        const QPointF stop  = center + dir * halfDiag;

        QLinearGradient grad(start, stop);
        grad.setColorAt(0.0, QColor(0xB0, 0xE2, 0x37));
        grad.setColorAt(1.0, QColor(0x00, 0xBF, 0xFF));
        p.setBrush(grad);
    } else {
        QColor bg = isEnabled() ? m_off : QColor(m_off.red(), m_off.green(), m_off.blue(), 120);
        p.setBrush(bg);
    }
    p.drawRoundedRect(trackRect, trackRadius, trackRadius);

    // Knob position
    const qreal left = m_margin + knobRadius;
    const qreal right = w - m_margin - knobRadius;
    const qreal cx = left + m_offset * (right - left);
    const qreal cy = h / 2.0;

    // Knob shadow: approximate CSS box-shadow: 1px 1px 3px rgba(0,0,0,0.12)
    {
        const qreal blur = 3.0;
        const QPointF shadowCenter(cx + 1.0, cy + 1.0);
        QRadialGradient shadowGrad(shadowCenter, knobRadius + blur);
        shadowGrad.setColorAt(0.0, QColor(0, 0, 0, int(0.12 * 255)));
        shadowGrad.setColorAt(1.0, QColor(0, 0, 0, 0));
        p.setBrush(shadowGrad);
        p.drawEllipse(shadowCenter, knobRadius + blur, knobRadius + blur);
    }

    p.setBrush(isEnabled() ? m_knob : QColor(255,255,255,200));
    p.drawEllipse(QPointF(cx, cy), knobRadius, knobRadius);
}

void Switch::mousePressEvent(QMouseEvent *e) {
    if (e->button() != Qt::LeftButton) {
        QAbstractButton::mousePressEvent(e);
        return;
    }
    m_dragging = false;
    m_pressPos = e->pos();
    m_pressOffset = m_offset;
    m_pressChecked = isChecked();
    e->accept();
}

void Switch::mouseMoveEvent(QMouseEvent *e) {
    const qreal dx = e->pos().x() - m_pressPos.x();
    if (!m_dragging) {
        if (std::abs(dx) >= m_dragStartThreshold) {
            m_dragging = true;
        } else {
            QAbstractButton::mouseMoveEvent(e);
            return;
        }
    }
    const qreal travel = width() - 2.0 * (m_margin + (height()/2.0 - m_margin)); // = w - 2*(m + knobRadius)
    if (travel > 1e-3) {
        setOffset(m_pressOffset + dx / travel);
    }
}

void Switch::mouseReleaseEvent(QMouseEvent *e) {
    if (e->button() != Qt::LeftButton) {
        QAbstractButton::mouseReleaseEvent(e);
        return;
    }
    if (m_dragging) {
        m_dragging = false;
        const bool toChecked = m_offset >= 0.5;
        if (toChecked != isChecked()) {
            setChecked(toChecked);
        }
        animateToChecked(isChecked());
        e->accept();
    } else {
        setChecked(!m_pressChecked);
        animateToChecked(isChecked());
        e->accept();
    }
}

void Switch::keyPressEvent(QKeyEvent *e) {
    if (e->key() == Qt::Key_Space || e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
        nextCheckState();
        e->accept();
        return;
    }
    QAbstractButton::keyPressEvent(e);
}

void Switch::nextCheckState() {
    setChecked(!isChecked());
    animateToChecked(isChecked());
}

void Switch::checkStateSet() {
    // Sync offset immediately when checked state is set programmatically
    // (e.g., via setChecked() without animation)
    setOffset(isChecked() ? 1.0 : 0.0);
    QAbstractButton::checkStateSet();
}

void Switch::animateToChecked(bool checked) {
    m_anim.stop();
    m_anim.setStartValue(m_offset);
    m_anim.setEndValue(checked ? 1.0 : 0.0);
    m_anim.start();
}
