/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2021 Marcus Britanicus (https://gitlab.com/marcusbritanicus)
 * Copyright (c) 2021 Abrar (https://gitlab.com/s96Abrar)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 **/

#pragma once

#include <QObject>
#include <QTimer>

class QWindow;
class QScreen;
class DesQLayerSurface;
class DesQHotSpot;
class DesQHotSpotPrivate;

struct wl_output;
struct wl_surface;
struct zdesq_shell_manager_v1;
struct zdesq_output_v1;
struct zdesq_output_v1_listener;
struct zdesq_hotspot_v1;
struct zdesq_hotspot_v1_listener;
struct zdesq_surface_v1;

namespace WQt {
    class DesQShell;
    class DesQOutput;
    class DesQHotSpot;
    class DesQSurface;
}

class WQt::DesQShell : public QObject {
    Q_OBJECT;

    public:
        DesQShell( zdesq_shell_manager_v1 *shellMgr );
        ~DesQShell();

        /**
         * Get the DesQ output for the give wl_output.
         * Don't forget to call DesQOutput::setup() before using it
         */
        WQt::DesQOutput *getOutput( wl_output * );

        /**
         * Get the DesQ surface for the given wl_surface.
         * This surface can be used for interactive move.
         */
        WQt::DesQSurface *getSurface( wl_surface * );

        /**
         * Set this window as desktop background on a given screen.
         * If screen is nullptr, current screen will be used
         */
        void setBackground( QWindow *window, QScreen *screen = nullptr );

        /**
         * Set this window as drop-down on a given screen.
         * If screen is nullptr, current screen will be used
         */
        void setDropdown( QWindow *window, QScreen *screen = nullptr );

        /**
         * Create a WQt::DesQPanel from this window.
         * If screen is nullptr, current screen will be used
         */
        // WQt::DesQPanel createPanel( QWindow *window, QScreen *screen = nullptr );

        /**
         * Get the underlying pointer
         */
        zdesq_shell_manager_v1 *get();

    private:
        zdesq_shell_manager_v1 *mObj;
};

class WQt::DesQOutput : public QObject {
    Q_OBJECT;

    public:
        enum HotSpotPoint {
            Top         = 1,
            Bottom      = 2,
            Left        = 4,
            Right       = 8,
            TopLeft     = Top | Left,
            TopRight    = Top | Right,
            BottomLeft  = Bottom | Left,
            BottomRight = Bottom | Right
        };

        Q_ENUM( HotSpotPoint );

        DesQOutput( zdesq_output_v1 *desq_output );
        ~DesQOutput();

        /**
         * We would like to give others a chance to setup signals
         */
        void setup();

        /**
         * Create a hotspot for a @location, defined by a region @threshold px
         * and the mouse stays there for atleast @timeout ms.
         */
        WQt::DesQHotSpot *createHotSpot( HotSpotPoint location, uint32_t threshold, uint32_t timeout );

        /**
         * Request the compositor to send this output's workspace grid size
         */
        void getWorkspaceExtents();

        /**
         * Request the compositor to send this output's current workspace
         */
        void getActiveWorkspace();

        /**
         * Request the compositor to set this output's current workspace
         */
        void setActiveWorkspace( int row, int col );

        zdesq_output_v1 *get();

    private:
        zdesq_output_v1 *mObj;

        static void handleEnterFullScreen( void *data, zdesq_output_v1 *wfOut );
        static void handleLeaveFullScreen( void *data, zdesq_output_v1 *wfOut );

        static void handleWorkspaceExtents( void *data, zdesq_output_v1 *wfOut, int rows, int columns );
        static void handleActiveWorkspace( void *data, zdesq_output_v1 *wfOut, int row, int column );

        static void handleViewAdded( void *data, zdesq_output_v1 *wfOut, uint32_t uuid, int row, int column );
        static void handleViewRemoved( void *data, zdesq_output_v1 *wfOut, uint32_t uuid, int row, int column );

        static void handleDone( void *data, zdesq_output_v1 *wfOut );

        static const zdesq_output_v1_listener mDesQOutputListener;

    Q_SIGNALS:
        void enteredFullScreen();
        void leftFullScreen();

        void workspaceExtentsChanged( int rows, int columns );
        void activeWorkspaceChanged( int row, int column );

        void viewAdded( uint32_t uuid, int row, int column );
        void viewRemoved( uint32_t uuid, int row, int column );

        void done();
};

class WQt::DesQHotSpot : public QObject {
    Q_OBJECT;

    public:
        DesQHotSpot( zdesq_hotspot_v1 * );
        ~DesQHotSpot();

        void setup();

        zdesq_hotspot_v1 *get();

    private:
        zdesq_hotspot_v1 *mObj;

        static void handleEnterHotSpot( void *data, zdesq_hotspot_v1 *wfHotspot );
        static void handleLeaveHotSpot( void *data, zdesq_hotspot_v1 *wfHotspot );

        static const zdesq_hotspot_v1_listener mWfHotSpotListener;

    Q_SIGNALS:
        void enteredHotSpot();
        void leftHotSpot();
};

class WQt::DesQSurface : public QObject {
    Q_OBJECT;

    public:
        DesQSurface( zdesq_surface_v1 * );
        ~DesQSurface();

        void move();

        zdesq_surface_v1 *get();

    private:
        zdesq_surface_v1 *mObj;
};
