/*
 * Copyright (C) 2013-2016 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 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 warranties of MERCHANTABILITY,
 * SATISFACTORY QUALITY, 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 QTMIR_PLATFORMSCREEN_H
#define QTMIR_PLATFORMSCREEN_H

// Qt
#include <QObject>
#include <QScopedPointer>
#include <QTimer>
#include <qpa/qplatformscreen.h>
#include <QtSensors/QOrientationReading>

// Mir
#include <mir/version.h>
#include <mir_toolkit/common.h>
#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(2, 16, 0)
#include <mir/graphics/platform.h>
#endif
#include <mir/graphics/display_configuration.h>

// Miroil
#include <miroil/display_configuration_storage.h>

#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(2, 20, 0)

#include <mir/renderer/gl/gl_surface.h>
#include <mir/graphics/display_sink.h>

#else

#include <mir/graphics/display.h>
#include <mir/graphics/display_buffer.h>
#include <mir/renderer/gl/render_target.h>

#include <miroil/mirbuffer.h>

#endif

// local
#include "qtmir/types.h"
#include "cursor.h"
#include "screenplatformwindow.h"

// std
#include <memory>

class OrientationSensor;
namespace mir {
    namespace graphics {
#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(2, 20, 0)
        class DisplaySink; class DisplaySyncGroup; class DisplayConfigurationOutput;
        namespace gl { class OutputSurface; }
#else
    namespace graphics { class DisplayBuffer; class DisplaySyncGroup; class DisplayConfigurationOutput; }
    namespace renderer { namespace gl { class RenderTarget; }}
#endif
    }
}

class PlatformScreen : public QObject,
                       public QPlatformScreen
{
    Q_OBJECT
public:
    PlatformScreen(const mir::graphics::DisplayConfigurationOutput &, const std::shared_ptr<OrientationSensor>);
    ~PlatformScreen();

    // QPlatformScreen methods.
    QRect geometry() const override { return m_geometry; }
    int depth() const override { return m_depth; }
    QImage::Format format() const override { return m_format; }
    qreal devicePixelRatio() const override { return m_devicePixelRatio; }
    QSizeF physicalSize() const override { return m_physicalSize; }
    qreal refreshRate() const override { return m_refreshRate; }
    Qt::ScreenOrientation nativeOrientation() const override { return m_nativeOrientation; }
    Qt::ScreenOrientation orientation() const override { return m_currentOrientation; }
    QPlatformCursor *cursor() const override;
    QString name() const override;
    QWindow *topLevelAt(const QPoint &point) const;

    bool used() const { return m_used; }
    float scale() const { return m_scale; }
    float* scaleRef() { return &m_scale; }
    qtmir::FormFactor formFactor() const { return m_formFactor; }
    MirPowerMode powerMode() const { return m_powerMode; }
    miroil::DisplayId displayId() const { return m_displayId; }
    qtmir::OutputTypes outputType() const { return m_type; }
    uint32_t currentModeIndex() const { return m_currentModeIndex; }
    bool isActive() const { return m_isActive; }

    typedef QPair<qreal, QSize> Mode;
    QList<Mode> availableModes() const;

    const QVector<ScreenPlatformWindow*>& windows() const { return m_screenWindows; }
    ScreenPlatformWindow* primaryWindow() const;

    // QObject methods.
    void customEvent(QEvent* event) override;

    // To make it testable
    bool orientationSensorEnabled();

    void setActive(bool active);

    void destroyWindows();

Q_SIGNALS:
    void primaryWindowChanged(ScreenPlatformWindow* window);

    void usedChanged();
    void nameChanged();
    void outputTypeChanged();
    void scaleChanged();
    void formFactorChanged();
    void orientationChanged();
    void powerModeChanged();
    void currentModeIndexChanged();
    void positionChanged();
    void modeChanged();
    void physicalSizeChanged();
    void availableModesChanged();
    void activeChanged(bool active);

public Q_SLOTS:
   void onOrientationReadingChanged(QOrientationReading::Orientation);

    void activate();

protected:
    void addWindow(ScreenPlatformWindow *window);
    void removeWindow(ScreenPlatformWindow *window);

    void setMirDisplayConfiguration(const mir::graphics::DisplayConfigurationOutput &, bool notify = true);
#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(2, 20, 0)
    void setMirDisplaySink(mir::graphics::GLRenderingProvider &, mir::graphics::DisplaySink *, mir::graphics::DisplaySyncGroup *);
#else
    void setMirDisplayBuffer(mir::graphics::DisplayBuffer *, mir::graphics::DisplaySyncGroup *);
#endif
    void swapBuffers();
    void makeCurrent();
    void doneCurrent();

private:
    bool internalDisplay() const;

    bool m_used;
    QRect m_geometry;
    int m_depth;
    QImage::Format m_format;
    qreal m_devicePixelRatio;
    QSizeF m_physicalSize;
    qreal m_refreshRate;
    float m_scale;
    qtmir::FormFactor m_formFactor;
    uint32_t m_currentModeIndex;
    bool m_sensorEnabled;
    QList<PlatformScreen::Mode> m_availableModes;
    bool m_isActive;

#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(2, 20, 0)
    std::unique_ptr<mir::graphics::gl::OutputSurface> m_outputSurface;
    mir::graphics::DisplaySink *m_displaySink;
#else
    mir::renderer::gl::RenderTarget *m_renderTarget;
#endif
    mir::graphics::DisplaySyncGroup *m_displayGroup;
    miroil::DisplayId m_displayId;
    qtmir::OutputTypes m_type;
    MirPowerMode m_powerMode;

    Qt::ScreenOrientation m_nativeOrientation;
    Qt::ScreenOrientation m_currentOrientation;

    QVector<ScreenPlatformWindow*> m_screenWindows;

    QScopedPointer<qtmir::Cursor> m_cursor;

    friend class ScreensModel;
    friend class ScreenPlatformWindow;
};

#endif // QTMIR_PLATFORMSCREEN_H
