//
// Created by ulrich on 08.04.19.
//

#include "TouchDrawingInputHandler.h"

#include "gui/XournalppCursor.h"
#include "gui/inputdevices/InputUtils.h"
#include "gui/widgets/XournalWidget.h"
#include "model/Stroke.h"
#include "util/DeviceListHelper.h"

#include "InputContext.h"

TouchDrawingInputHandler::TouchDrawingInputHandler(InputContext* inputContext): PenInputHandler(inputContext) {
    inputContext->getToolHandler()->addToolChangedListener([&](ToolType newToolType) {
        InputDeviceClass touchscreenClass =
                DeviceListHelper::getSourceMapping(GDK_SOURCE_TOUCHSCREEN, this->inputContext->getSettings());

        // If using the touchscreen as a touchscreen...
        if (touchscreenClass == INPUT_DEVICE_TOUCHSCREEN) {
            updateKineticScrollingEnabled();
        }
    });
}

TouchDrawingInputHandler::~TouchDrawingInputHandler() = default;

auto TouchDrawingInputHandler::handleImpl(InputEvent const& event) -> bool {
    auto* mainWindow = inputContext->getView()->getControl()->getWindow();
    ToolHandler* toolHandler = this->inputContext->getToolHandler();

    // Do we need to end the touch sequence?
    bool mustEnd = event.type == BUTTON_RELEASE_EVENT;
    mustEnd = mustEnd || event.type == GRAB_BROKEN_EVENT && this->deviceClassPressed;

    // Notify if finger enters/leaves widget
    // Note: Drawing outside window doesn't seem to work
    //  if this is put later in handleImpl.
    if (event.type == ENTER_EVENT) {
        this->actionEnterWindow(event);
        return false;
    }

    if (event.type == LEAVE_EVENT) {
        this->actionLeaveWindow(event);
        return false;
    }

    // Multitouch
    if (this->primarySequence && this->primarySequence != event.sequence || this->secondarySequence) {
        if (!this->secondarySequence) {
            this->secondarySequence = event.sequence;

            // Let Gtk's touchscreen-based scrolling do
            // momentum-based scrolling!
            mainWindow->setGtkTouchscreenScrollingEnabled(true);

            XojPageView* currentPage = this->getPageAtCurrentPosition(event);

            if (currentPage) {
                currentPage->onMotionCancelEvent();
            }
        }

        if (mustEnd) {
            // Ending two-finger panning/zooming? We should now only have a primary sequence.
            if (event.sequence == this->primarySequence) {
                this->primarySequence = this->secondarySequence;
                this->secondarySequence = nullptr;

                // Only scrolling now if using the hand tool.
                mainWindow->setGtkTouchscreenScrollingEnabled(toolHandler->getToolType() == TOOL_HAND);
            } else if (event.sequence == this->secondarySequence) {
                this->secondarySequence = nullptr;
            }
        }

        // false lets another input handler (e.g. TouchInputHandler)
        // handle the event.
        return false;
    }

    // Trigger start of action when pen/mouse is pressed
    if (event.type == BUTTON_PRESS_EVENT && this->primarySequence == nullptr) {
        this->primarySequence = event.sequence;
        this->deviceClassPressed = true;

        this->actionStart(event);

        updateKineticScrollingEnabled();

        return false;
    }

    // Trigger motion action when finger is pressed and moved,
    // unless a hand tool, in which case, we return false to
    // let an alternate handler (e.g. TouchInputHandler) decide
    // what to do...
    if (this->deviceClassPressed && event.type == MOTION_EVENT && toolHandler->getToolType() != TOOL_HAND) {
        GtkXournal* xournal = inputContext->getXournal();

        this->inputContext->getView()->getCursor()->setRotationAngle(event.relativeX);

        this->actionMotion(event);
        XournalppCursor* cursor = xournal->view->getCursor();
        cursor->updateCursor();

        return true;
    }

    if (mustEnd) {
        this->primarySequence = nullptr;
        this->actionEnd(event);

        this->deviceClassPressed = false;

        return false;  // Any alternates should also end.
    }

    return false;
}

auto TouchDrawingInputHandler::changeTool(InputEvent const& event) -> bool {
    Settings* settings = this->inputContext->getSettings();
    ButtonConfig* cfg = settings->getButtonConfig(Button::BUTTON_TOUCH);
    ToolHandler* toolHandler = this->inputContext->getToolHandler();
    bool toolChanged = false;

    if (cfg->device == event.deviceName) {
        if (InputUtils::touchDrawingDisallowed(toolHandler, settings))
            return false;
        toolChanged = InputUtils::applyButton(toolHandler, settings, Button::BUTTON_TOUCH);
    } else {
        toolChanged = toolHandler->pointActiveToolToToolbarTool();
    }

    this->updateKineticScrollingEnabled();

    if (toolChanged) {
        toolHandler->fireToolChanged();
    }

    return true;
}

void TouchDrawingInputHandler::updateKineticScrollingEnabled() {
    auto* control = inputContext->getView()->getControl();
    auto* mainWindow = control->getWindow();
    auto* toolHandler = this->inputContext->getToolHandler();

    //  Kinetic scrolling is nice; however, we need to disable it so we can draw (it steals
    // single-finger input).
    if (mainWindow != nullptr && control->getSettings()->getTouchDrawingEnabled()) {
        mainWindow->setGtkTouchscreenScrollingEnabled(toolHandler->getToolType() == TOOL_HAND);
    }
}
