/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt WebGL module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwebglwindow.h"
#include "qwebglwindow_p.h"

#include "qwebglintegration_p.h"
#include "qwebglwebsocketserver.h"

#include <QtCore/qtextstream.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtGui/private/qwindow_p.h>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtGui/qpa/qplatformintegration.h>
#include <QtGui/qopenglcontext.h>
#include <QtGui/qoffscreensurface.h>

#include "qwebglwindow.h"

QT_BEGIN_NAMESPACE

static Q_LOGGING_CATEGORY(lc, "qt.qpa.webgl.window")

QAtomicInt QWebGLWindowPrivate::nextId(1);

QWebGLWindowPrivate::QWebGLWindowPrivate(QWebGLWindow *p) :
    q_ptr(p)
{}

QWebGLWindow::QWebGLWindow(QWindow *w) :
    QPlatformWindow(w),
    d_ptr(new QWebGLWindowPrivate(this))
{
}

QWebGLWindow::~QWebGLWindow()
{
    destroy();
}

void QWebGLWindow::create()
{
    Q_D(QWebGLWindow);
    if (d->flags.testFlag(QWebGLWindowPrivate::Created))
        return;

    d->id = QWebGLWindowPrivate::nextId.fetchAndAddAcquire(1);
    qCDebug(lc, "Window %d created", d->id);

    // Save the original surface type before changing to OpenGLSurface.
    d->raster = (window()->surfaceType() == QSurface::RasterSurface);
    if (d->raster) // change to OpenGL, but not for RasterGLSurface
        window()->setSurfaceType(QSurface::OpenGLSurface);

    if (window()->windowState() == Qt::WindowFullScreen) {
        QRect fullscreenRect(QPoint(), screen()->availableGeometry().size());
        QPlatformWindow::setGeometry(fullscreenRect);
        QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect);
        return;
    }

    d->flags = QWebGLWindowPrivate::Created;

    if (window()->type() == Qt::Desktop)
        return;

    d->flags |= QWebGLWindowPrivate::HasNativeWindow;
    setGeometry(window()->geometry()); // will become fullscreen
    QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size()));

    if (d->raster) {
        QOpenGLContext *context = new QOpenGLContext(QGuiApplication::instance());
        context->setShareContext(qt_gl_global_share_context());
        context->setFormat(d->format);
        context->setScreen(window()->screen());
        if (Q_UNLIKELY(!context->create()))
            qFatal("QWebGL: Failed to create compositing context");
    }
}

void QWebGLWindow::destroy()
{
    Q_D(QWebGLWindow);
    qCDebug(lc, "Destroying %d", d->id);
    if (d->flags.testFlag(QWebGLWindowPrivate::HasNativeWindow)) {
        invalidateSurface();
    }

    qt_window_private(window())->updateRequestPending = false;

    d->flags = QWebGLWindowPrivate::Flags{};

    auto integrationPrivate = QWebGLIntegrationPrivate::instance();
    auto clientData = integrationPrivate->findClientData(surface()->surfaceHandle());
    if (clientData) {
        const QVariantMap values {
            { "winId", winId() }
        };
        if (clientData->socket)
            integrationPrivate->sendMessage(clientData->socket,
                                            QWebGLWebSocketServer::MessageType::DestroyCanvas,
                                            values);
        clientData->platformWindows.removeAll(this);
    }
}

void QWebGLWindow::raise()
{
    QWindow *wnd = window();
    if (wnd->type() != Qt::Desktop) {
        QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
    }
}

QSurfaceFormat QWebGLWindow::format() const
{
    Q_D(const QWebGLWindow);
    return d->format;
}

QWebGLScreen *QWebGLWindow::screen() const
{
    return static_cast<QWebGLScreen *>(QPlatformWindow::screen());
}

void QWebGLWindow::setGeometry(const QRect &rect)
{
    QWindowSystemInterface::handleGeometryChange(window(), rect);
    QPlatformWindow::setGeometry(rect);
}

void QWebGLWindow::setDefaults(const QMap<GLenum, QVariant> &values)
{
    Q_D(QWebGLWindow);
    d->defaults.set_value(values);
}

WId QWebGLWindow::winId() const
{
    Q_D(const QWebGLWindow);
    return d->id;
}

QWebGLOffscreenSurface::QWebGLOffscreenSurface(QOffscreenSurface *offscreenSurface)
    : QPlatformOffscreenSurface(offscreenSurface)
{
}

QSurfaceFormat QWebGLOffscreenSurface::format() const
{
    return offscreenSurface()->format();
}

bool QWebGLOffscreenSurface::isValid() const
{
    return true;
}

QT_END_NAMESPACE
