#include "stfslider.h"
#include <cmath>
#include <QPainter>
#include <QPaintEvent>
#include <QPainterPath>
#include <QToolTip>

static float clamp(float x)
{
    return std::min(std::max(x, 0.0f), 1.0f);
}

STFSlider::STFSlider(QWidget *parent) : QWidget(parent)
{
    setMinimumHeight(15);
    setMaximumHeight(15);
    setMouseTracking(true);
    m_blackPoint = 0;
    m_midPoint = 0.5;
    m_whitePoint = 1;
    m_grabbed = -1;
}

float STFSlider::blackPoint() const
{
    return m_blackPoint;
}

float STFSlider::midPoint() const
{
    return m_midPoint;
}

float STFSlider::whitePoint() const
{
    return m_whitePoint;
}

void STFSlider::setMTFParams(float low, float mid, float high)
{
    m_blackPoint = clamp(low);
    m_midPoint = clamp(mid);
    m_whitePoint = clamp(high);
    update();
}

void STFSlider::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QRect rect = event->rect();
    qreal w = rect.width() - 1;
    qreal h = rect.height();
    QLinearGradient gradient(rect.topLeft(), rect.topRight());
    gradient.setColorAt(0, Qt::black);
    for(int i=1; i<=32; i++)
    {
        qreal p = i/32.0f;
        qreal c = std::pow(p, 1.0/2.2)*255;
        gradient.setColorAt(p, QColor(c, c, c));
    }

    QPainterPath tick(QPointF(0, 0));
    tick.lineTo(0, h - 5);
    tick.lineTo(-5, h);
    tick.lineTo(5, h);
    tick.lineTo(0, h - 5);

    painter.setBrush(gradient);
    painter.setPen(Qt::NoPen);
    painter.drawRect(rect);

    auto drawTick = [&](qreal p)
    {
        painter.setPen(p < 0.4 ? Qt::white : Qt::black);
        painter.resetTransform();
        painter.translate(w*p, 0);
        painter.drawPath(tick);
    };

    painter.setBrush(Qt::NoBrush);
    drawTick(m_blackPoint);
    drawTick(m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint);
    drawTick(m_whitePoint);
}

void STFSlider::mouseMoveEvent(QMouseEvent *event)
{
    if(std::abs(m_blackPoint*width() - event->x()) < 5 ||
            std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - event->x()) < 5 ||
            std::abs(m_whitePoint*width() - event->x()) < 5)
        setCursor(Qt::SplitHCursor);
    else
        unsetCursor();

    switch(m_grabbed)
    {
    case 0:
        m_blackPoint = clamp((qreal)event->x()/width());
        m_whitePoint = std::max(m_whitePoint, m_blackPoint);
        QToolTip::showText(event->globalPos(), QString::number(m_blackPoint), this);
        break;
    case 1:
        m_midPoint = ((qreal)event->x()/width() - m_blackPoint) / (m_whitePoint - m_blackPoint);
        m_midPoint = clamp(m_midPoint);
        QToolTip::showText(event->globalPos(), QString::number(m_midPoint), this);
        break;
    case 2:
        m_whitePoint = clamp((qreal)event->x()/width());
        m_blackPoint = std::min(m_blackPoint, m_whitePoint);
        QToolTip::showText(event->globalPos(), QString::number(m_whitePoint), this);
        break;
    }
    if(m_grabbed >= 0)
    {
        emit paramChanged(m_blackPoint, midPoint(), m_whitePoint);
        update();
    }
}

void STFSlider::mousePressEvent(QMouseEvent *event)
{
    if(std::abs((m_blackPoint + (m_whitePoint - m_blackPoint) * m_midPoint)*width() - event->x()) < 5)
        m_grabbed = 1;
    else if(std::abs(m_blackPoint*width() - event->x()) < 5)
        m_grabbed = 0;
    else if(std::abs(m_whitePoint*width() - event->x()) < 5)
        m_grabbed = 2;
    else
        m_grabbed = -1;
}

void STFSlider::mouseReleaseEvent(QMouseEvent *)
{
    m_grabbed = -1;
    emit paramChanged(m_blackPoint, midPoint(), m_whitePoint);
}
