/*
 * Copyright © Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 2 or 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MIR_INPUT_EVDEV_RS_PLATFORM_BRIDGE_H
#define MIR_INPUT_EVDEV_RS_PLATFORM_BRIDGE_H

#include <mir/console_services.h>
#include <mir/input/input_device.h>
#include <mir/input/event_builder.h>
#include <mir/geometry/rectangle.h>
#include <memory>
#include <future>
#include <unordered_map>
#include <rust/cxx.h>

namespace mir
{
class Device;

namespace input
{
namespace evdev_rs
{
class Platform;

struct PointerEventData
{
    bool has_time;
    uint64_t time_microseconds;
    int32_t action;
    uint32_t buttons;
    bool has_position;
    float position_x;
    float position_y;
    float displacement_x;
    float displacement_y;
    int32_t axis_source;
    float precise_x;
    int discrete_x;
    int value120_x;
    bool scroll_stop_x;
    float precise_y;
    int discrete_y;
    int value120_y;
    bool scroll_stop_y;
};

struct KeyEventData
{
    bool has_time;
    uint64_t time_microseconds;
    int32_t action;
    uint32_t scancode;
};

// TouchContactData and TouchEventData are generated by CXX from the bridge definition
class TouchEventData;

class EventBuilderWrapper
{
public:
    explicit EventBuilderWrapper(EventBuilder* event_builder);
    std::shared_ptr<MirEvent> pointer_event(PointerEventData const&) const;
    std::shared_ptr<MirEvent> key_event(KeyEventData const&) const;
    std::shared_ptr<MirEvent> touch_event(TouchEventData const&) const;

private:
    EventBuilder* event_builder;
};

class RectangleWrapper
{
public:
    explicit RectangleWrapper(geometry::Rectangle&& rect);
    int32_t x() const;
    int32_t y() const;
    int32_t width() const;
    int32_t height() const;

private:
    geometry::Rectangle rect;
};

/// Bridge allowing conversion from Rust to C++.
///
/// The rust code may use this bridge to request things from the system.
class PlatformBridge
{
public:
    PlatformBridge(Platform* platform, std::shared_ptr<mir::ConsoleServices> const& console);
    std::shared_ptr<InputDevice> create_input_device(int device_id) const;
    std::unique_ptr<EventBuilderWrapper> create_event_builder_wrapper(EventBuilder* event_builder) const;
    std::unique_ptr<RectangleWrapper> bounding_rectangle(InputSink const&) const;

    /// Initiate device acquisition via ConsoleServices.
    /// Returns true if acquisition was started, false if already tracked.
    bool acquire_device(uint64_t devnum, rust::Str devnode) const;

    /// Transfer a pending device to the active set (called after activated() fires).
    void activate_pending_device(uint64_t devnum) const;

    /// Release an active device handle.
    void release_device(uint64_t devnum) const;

    /// Release a pending device (drop the future before activated() fires).
    void release_pending_device(uint64_t devnum) const;

    /// Check whether a device is active (not pending).
    bool has_device(uint64_t devnum) const;

private:
    Platform* platform;
    std::shared_ptr<mir::ConsoleServices> console;

    mutable std::unordered_map<dev_t, std::future<std::unique_ptr<mir::Device>>> pending_devices;
    mutable std::unordered_map<dev_t, std::unique_ptr<mir::Device>> device_watchers;
};
}
}
}

#endif // MIR_INPUT_EVDEV_RS_PLATFORM_BRIDGE_H
