// SPDX-FileCopyrightText: 2025 Prayag Jain <prayagjain2@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "freeformtool.hpp"
#include <KLocalizedString>

#include "canvas/canvas.hpp"
#include "command/commandhistory.hpp"
#include "command/insertitemcommand.hpp"
#include "common/renderitems.hpp"
#include "context/applicationcontext.hpp"
#include "context/coordinatetransformer.hpp"
#include "context/renderingcontext.hpp"
#include "context/spatialcontext.hpp"
#include "context/uicontext.hpp"
#include "event/event.hpp"
#include "item/factory/freeformfactory.hpp"
#include "item/freeform.hpp"
#include "properties/widgets/propertymanager.hpp"
using namespace Qt::Literals::StringLiterals;

FreeformTool::FreeformTool(ApplicationContext *context)
    : DrawingTool(context)
{
    m_itemFactory = std::make_unique<FreeformFactory>();

    m_cursor = QCursor{Qt::CrossCursor};
    m_properties = {Property::Type::StrokeWidth, Property::Type::StrokeColor, Property::Type::StrokeStyle, Property::Type::Opacity};
}

void FreeformTool::mousePressed(ApplicationContext *context)
{
    const UIContext *uiContext{context->uiContext()};

    if (uiContext->appEvent()->button() == Qt::LeftButton) {
        auto spatialContext{context->spatialContext()};
        auto renderingContext{context->renderingContext()};
        CoordinateTransformer &transformer{spatialContext->coordinateTransformer()};

        m_currentItem = std::dynamic_pointer_cast<FreeformItem>(m_itemFactory->create());

        m_currentItem->setProperty(Property::Type::StrokeWidth, uiContext->propertyManager()->value(Property::Type::StrokeWidth));
        m_currentItem->setProperty(Property::Type::StrokeColor, uiContext->propertyManager()->value(Property::Type::StrokeColor));
        m_currentItem->setProperty(Property::Type::Opacity, uiContext->propertyManager()->value(Property::Type::Opacity));
        m_currentItem->setProperty(Property::Type::StrokeStyle, uiContext->propertyManager()->value(Property::Type::StrokeStyle));

        m_lastPoint = uiContext->appEvent()->pos();
        m_itemList.clear();

        m_currentItem->addPoint(transformer.viewToWorld(m_lastPoint), uiContext->appEvent()->pressure());
        m_currentCache = QPixmap{renderingContext->canvas()->dimensions()};
        m_currentCache.fill(Qt::transparent);

        m_isDrawing = true;
    }
}

void FreeformTool::mouseMoved(ApplicationContext *context)
{
    if (m_isDrawing) {
        auto spatialContext{context->spatialContext()};
        auto renderingContext{context->renderingContext()};
        auto uiContext{context->uiContext()};
        CoordinateTransformer &transformer{spatialContext->coordinateTransformer()};

        const QPointF curPoint{uiContext->appEvent()->pos()};

        // distance between the two points in the "view" coordinate system
        const double dist{std::sqrt(std::pow(m_lastPoint.x() - curPoint.x(), 2) + std::pow(m_lastPoint.y() - curPoint.y(), 2))};

        if (dist < FreeformItem::minPointDistance()) {
            return;
        }

        const qreal zoom{renderingContext->zoomFactor()};
        m_currentItem->addPoint(transformer.viewToWorld(curPoint), uiContext->appEvent()->pressure());

        if (m_currentItem->isBufferFull()) {
            QPainter cachePainter{&m_currentCache};
            cachePainter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing);
            cachePainter.scale(zoom, zoom);
            m_currentItem->drawBuffer(cachePainter, spatialContext->offsetPos());
        } else {
            // clear the overlay and draw the cached pixmap and the latest buffer
            renderingContext->canvas()->setOverlayBg(renderingContext->canvas()->overlayBg());
            renderingContext->canvas()->paintOverlay([&](QPainter &painter) -> void {
                painter.drawPixmap(QPointF{0, 0}, m_currentCache);
                painter.scale(zoom, zoom);
                m_currentItem->drawBuffer(painter, spatialContext->offsetPos());
            });
        }

        m_lastPoint = curPoint;
        renderingContext->markForUpdate();
    }
}

void FreeformTool::mouseReleased(ApplicationContext *context)
{
    const UIContext *uiContext{context->uiContext()};

    if (uiContext->appEvent()->button() == Qt::LeftButton && m_isDrawing) {
        auto spatialContext{context->spatialContext()};
        auto renderingContext{context->renderingContext()};
        auto commandHistory{spatialContext->commandHistory()};

        renderingContext->canvas()->setOverlayBg(Qt::transparent);

        m_currentItem->finalizeStroke();

        m_itemList.push_back(m_currentItem);
        commandHistory->push(std::make_shared<InsertItemCommand>(m_itemList));

        m_currentItem.reset();
        m_currentCache = QPixmap{};

        m_isDrawing = false;
        renderingContext->markForRender();
        renderingContext->markForUpdate();
    }
}

void FreeformTool::tablet([[maybe_unused]] ApplicationContext *context)
{
    if (m_currentItem && m_currentItem->isPressureSimulated()) {
        m_currentItem->setSimulatePressure(false);
    }
}

void FreeformTool::cleanup()
{
    m_context->uiContext()->appEvent()->setButton(Qt::LeftButton);
    m_itemList.clear();
    mouseReleased(m_context);
}

Tool::Type FreeformTool::type() const
{
    return Tool::Type::Freeform;
}

QString FreeformTool::icon() const
{
    return u"draw-freehand"_s;
}
