#pragma once

#include <qpainter.h>
#include <qpainterpath.h>

#include "modern-qt/utility/animation/animatable.hh"
#include "modern-qt/utility/animation/state/pid.hh"
#include "modern-qt/utility/animation/transition.hh"
#include "modern-qt/utility/wrapper/widget.hh"

namespace creeper::mixer::internal {

class MixerMask : public QWidget {
public:
    explicit MixerMask(auto* widget) noexcept
        : QWidget { widget }
        , animatable { *this } {

        QWidget::setAttribute(Qt::WA_TransparentForMouseEvents);

        mask_frame.fill(Qt::transparent);
        {
            auto state = std::make_shared<PidState<double>>();

            state->config.kp      = 05.0;
            state->config.ki      = 00.0;
            state->config.kd      = 00.0;
            state->config.epsilon = 1e-3;

            mask_radius = make_transition(animatable, std::move(state));
        }
    }

    auto initiate_animation(QPoint const& point) noexcept {
        mask_frame.fill(Qt::transparent);

        auto* widget = parentWidget();
        if (widget == nullptr) return;

        mask_radius->snap_to(0.);
        mask_radius->transition_to(1.);

        mask_point = point;
        mask_frame = widget->grab();

        update_animation = true;
        QWidget::setFixedSize(widget->size());
    }
    auto initiate_animation(int x, int y) noexcept {
        // Forward Point
        initiate_animation(QPoint { x, y });
    }

protected:
    auto paintEvent(QPaintEvent* e) -> void override {
        if (!update_animation) return;

        auto const w = QWidget::width();
        auto const h = QWidget::height();
        auto const x = std::sqrt(w * w + h * h);

        auto painter = QPainter { this };

        auto const radius = double { *mask_radius * x };
        auto const round  = [&] {
            auto path = QPainterPath {};
            path.addRect(QWidget::rect());

            auto inner = QPainterPath();
            inner.addEllipse(mask_point, radius, radius);

            return path.subtracted(inner);
        }();
        painter.setClipPath(round);
        painter.setClipping(true);

        painter.drawPixmap(QWidget::rect(), mask_frame);

        if (std::abs(*mask_radius - 1.) < 1e-2) {
            update_animation = false;
        }
    }

private:
    QPixmap mask_frame;
    QPointF mask_point;

    bool update_animation = false;

    Animatable animatable;
    std::unique_ptr<TransitionValue<PidState<double>>> mask_radius;
};

}
namespace creeper::mixer::pro {

struct SetMixerMask : widget::pro::Token {
    internal::MixerMask*& mask;
    explicit SetMixerMask(auto*& mask)
        : mask { mask } { }
    auto apply(auto& self) noexcept {
        //
        mask = new internal::MixerMask { &self };
    }
};

}
namespace creeper {

using MixerMask = mixer::internal::MixerMask;

}
