#include "ProgressBar.h"
#include <QPainter>
#include <QMetaObject>
#include <QThread>
#include <algorithm>
#include <cmath>

Progress_Bar::Progress_Bar(const std::vector<Tick>& tick_list, QWidget *parent) : QWidget(parent), tick_list(tick_list) {
    setAttribute(Qt::WA_OpaquePaintEvent, true);
    timer = startTimer(30);
}

double Progress_Bar::get_draw_width(double value) {
    int n = tick_list.size();
    if (n < 2 || QWidget::width() <= 0)
        return 0.0;
    if (!std::isfinite(value))
        value = tick_list.front().value;
    int i = 1;
    for(; i < n - 1; ++i) {
        double cur = tick_list[i].value;
        if(value <  cur || qFuzzyCompare(value, cur)) {
            break;
        }
    }
    int lower_offset = i - 1;
    int upper_offset = i;
    double lower = tick_list[lower_offset].value;
    double upper = tick_list[upper_offset].value;
    double w_step = (double)QWidget::width()/(double)(n-1);
    double last_rate = upper == lower ? 0.0 : (value - lower)/(upper - lower);
    last_rate = std::clamp(last_rate, 0.0, 1.0);
    double draw_width = w_step * (last_rate + lower_offset);
    return std::clamp(draw_width, 0.0, (double)QWidget::width());
}
void Progress_Bar::setValue(double value) {
    if (thread() != QThread::currentThread()) {
        queued_value.store(value, std::memory_order_release);
        if (!queued_value_update.exchange(true, std::memory_order_acq_rel)) {
            QMetaObject::invokeMethod(this, [this]() {
                double value = queued_value.load(std::memory_order_acquire);
                queued_value_update.store(false, std::memory_order_release);
                setValue(value);
            }, Qt::QueuedConnection);
        }
        return;
    }
    this->value = value;
    update();
}
void Progress_Bar::paintEvent(QPaintEvent *event) {
    Q_UNUSED(event)
    QPainter painter(this);
    painter.setPen(Qt::NoPen);
    painter.fillRect(rect(), QApplication::style()->standardPalette().brush(QPalette::Window));
    painter.setPen(QPen(Qt::black));
    auto w = (double)QWidget::width();
    auto hh = (double)height()/2;
    int n = tick_list.size();
    int tick_w = 1;
    double draw_width = get_draw_width(value);
    int fill_width = std::max(0, static_cast<int>(draw_width) - tick_w - 4);
    if (fill_width > 0)
        painter.fillRect(0, 0, fill_width, height()/2, Qt::black);
    int cur_x = 0;
    int tick_step = std::max(1, tick_w + (int)tick_space_w);
    while(cur_x + tick_w + 4 < draw_width){
        painter.fillRect(cur_x, 0, tick_w, height()/2, Qt::gray);
        cur_x += tick_step;
    }
    int line_y = (int)hh + 2;
    int marker_x = std::clamp(cur_x - (int)tick_space_w, 0, std::max(0, QWidget::width() - 4));
    painter.fillRect(marker_x, 0, 4, line_y, Qt::yellow);
    painter.drawLine(0, line_y , (int)w, line_y);
    QFont font;
    font.setBold(true);
    QFontMetrics fm(font);
    for(int i = 0; i < n; ++i){
        int tick_x = (int)((double)(QWidget::width() * i)/(double)(n - 1));
        int text_w = fm.horizontalAdvance( tick_list[i].text);
        int text_x;
        if(i == n - 1) {
            text_x = tick_x - text_w;
            tick_x -=1;
        } else if( i == 0) {
            text_x = tick_x;
        } else {
            text_x = tick_x - text_w/2;
        }
        painter.setPen(Qt::black);
        painter.drawLine(tick_x, line_y, tick_x, (line_y + 5));
        painter.setPen(tick_list[i].color);
        painter.setFont(font);
        painter.drawText(QRectF(text_x, line_y, text_w, line_y), Qt::AlignCenter, tick_list[i].text);
    }
}

void Progress_Bar::timerEvent(QTimerEvent *event) {
    if(event->timerId() == timer) {
        if(set_value) set_value();
        update();
    }
    QObject::timerEvent(event);
}
