/*
 * Copyright © Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MIR_FRONTEND_XWAYLAND_SURFACE_OBSERVER_H
#define MIR_FRONTEND_XWAYLAND_SURFACE_OBSERVER_H

#include <mir/scene/null_surface_observer.h>
#include <mir/synchronised.h>

#include <memory>
#include <mutex>
#include <chrono>
#include <functional>
#include <optional>

struct wl_client;

namespace mir
{
class Executor;
namespace frontend
{
class WlSeat;
class WlSurface;
class WaylandInputDispatcher;
class XWaylandSurfaceObserverSurface;

/// Must not outlive the XWaylandSurface
class XWaylandSurfaceObserver
    : public scene::NullSurfaceObserver
{
public:
    XWaylandSurfaceObserver(
        Executor& wayland_executor,
        WlSeat& seat,
        WlSurface* wl_surface,
        XWaylandSurfaceObserverSurface* wm_surface,
        float scale);
    ~XWaylandSurfaceObserver();

    auto latest_move_resize_event() -> std::shared_ptr<MirInputEvent const>;

    /// Overrides from scene::SurfaceObserver
    ///@{
    void attrib_changed(scene::Surface const*, MirWindowAttrib attrib, int value) override;
    void content_resized_to(scene::Surface const*, geometry::Size const& content_size) override;
    void moved_to(scene::Surface const*, geometry::Point const& top_left) override;
    void client_surface_close_requested(scene::Surface const*) override;
    void input_consumed(scene::Surface const*, std::shared_ptr<MirEvent const> const& event) override;
    ///@}

private:
    struct ThreadsafeInputDispatcher
    {
        ThreadsafeInputDispatcher(std::unique_ptr<WaylandInputDispatcher> dispatcher);
        ~ThreadsafeInputDispatcher();

        std::mutex mutex;

        // Innter value should only be used from the Wayland thread and while the mutex is locked
        // Can be nullified from any thread as long as the mutex is locked
        std::optional<std::unique_ptr<WaylandInputDispatcher>> dispatcher;
    };

    XWaylandSurfaceObserverSurface* const wm_surface;
    Executor& wayland_executor;
    std::shared_ptr<ThreadsafeInputDispatcher> const input_dispatcher;
    float const scale;
    Synchronised<std::shared_ptr<MirInputEvent const>> latest_move_resize_event_;

    /// Runs work on the Wayland thread if the input dispatcher still exists
    /// Does nothing if the input dispatcher has already been destroyed
    void aquire_input_dispatcher(std::function<void(WaylandInputDispatcher*)>&& work);
};
}
}

#endif // MIR_FRONTEND_XWAYLAND_SURFACE_OBSERVER_H
