/*
 * Carla Bridge Toolkit, Qt version
 * Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or any later version.
 *
 * 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.
 *
 * For a full copy of the GNU General Public License see the GPL.txt file
 */

#include "CarlaBridgeClient.hpp"
#include "CarlaBridgeToolkit.hpp"

#include <QtCore/QSettings>
#include <QtCore/QThread>
#include <QtCore/QTimerEvent>

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
# include <QtWidgets/QApplication>
# include <QtWidgets/QMainWindow>
# ifdef Q_WS_X11
#  undef Q_WS_X11
# endif
#else
# include <QtGui/QApplication>
# include <QtGui/QMainWindow>
# ifdef Q_WS_X11
#  include <QtGui/QX11EmbedContainer>
# endif
#endif

#if defined(BRIDGE_COCOA) || defined(BRIDGE_HWND) || defined(BRIDGE_X11)
# define BRIDGE_CONTAINER
# ifdef Q_WS_X11
typedef QX11EmbedContainer QEmbedContainer;
# else
typedef QWidget QEmbedContainer;
# endif
#endif

CARLA_BRIDGE_START_NAMESPACE

// -------------------------------------------------------------------------

#if defined(BRIDGE_QT4)
static const char* const appName = "Carla-Qt4UIs";
#elif defined(BRIDGE_QT5)
static const char* const appName = "Carla-Qt5UIs";
#elif defined(BRIDGE_COCOA)
static const char* const appName = "Carla-CocoaUIs";
#elif defined(BRIDGE_HWND)
static const char* const appName = "Carla-HWNDUIs";
#elif defined(BRIDGE_X11)
static const char* const appName = "Carla-X11UIs";
#else
static const char* const appName = "Carla-UIs";
#endif

static int   qargc = 0;
static char* qargv[0] = {};

// -------------------------------------------------------------------------

class CarlaBridgeToolkitQt: public CarlaBridgeToolkit,
                            public QObject
{
public:
    CarlaBridgeToolkitQt(CarlaBridgeClient* const client, const char* const uiTitle)
        : CarlaBridgeToolkit(client, uiTitle),
          QObject(nullptr),
          settings("falkTX", appName)
    {
        qDebug("CarlaBridgeToolkitQt::CarlaBridgeToolkitQt(%p, \"%s\")", client, uiTitle);

        app = nullptr;
        window = nullptr;

        msgTimer = 0;

        needsResize = false;
        nextWidth   = 0;
        nextHeight  = 0;

#ifdef BRIDGE_CONTAINER
        embedContainer = nullptr;
#endif
    }

    ~CarlaBridgeToolkitQt()
    {
        qDebug("CarlaBridgeToolkitQt::~CarlaBridgeToolkitQt()");
        CARLA_ASSERT(! app);
        CARLA_ASSERT(! window);
        CARLA_ASSERT(! msgTimer);
    }

    void init()
    {
        qDebug("CarlaBridgeToolkitQt::init()");
        CARLA_ASSERT(! app);
        CARLA_ASSERT(! window);
        CARLA_ASSERT(! msgTimer);

        app = new QApplication(qargc, qargv);

        window = new QMainWindow(nullptr);
        window->resize(30, 30);
        window->hide();
    }

    void exec(const bool showGui)
    {
        qDebug("CarlaBridgeToolkitQt::exec(%s)", bool2str(showGui));
        CARLA_ASSERT(app);
        CARLA_ASSERT(window);
        CARLA_ASSERT(kClient);

#if defined(BRIDGE_QT4) || defined(BRIDGE_QT5)
        QWidget* const widget = (QWidget*)kClient->getWidget();

        window->setCentralWidget(widget);
        window->adjustSize();

        widget->setParent(window);
        widget->show();
#endif

        if (! kClient->isResizable())
        {
            window->setFixedSize(window->width(), window->height());
#ifdef Q_OS_WIN
            window->setWindowFlags(window->windowFlags() | Qt::MSWindowsFixedSizeDialogHint);
#endif
        }

        window->setWindowTitle(kUiTitle);

        if (settings.contains(QString("%1/pos_x").arg(kUiTitle)))
        {
            bool hasX, hasY;
            int posX = settings.value(QString("%1/pos_x").arg(kUiTitle), window->x()).toInt(&hasX);
            int posY = settings.value(QString("%1/pos_y").arg(kUiTitle), window->y()).toInt(&hasY);

            if (hasX && hasY)
                window->move(posX, posY);

            if (kClient->isResizable())
            {
                bool hasWidth, hasHeight;
                int width  = settings.value(QString("%1/width").arg(kUiTitle), window->width()).toInt(&hasWidth);
                int height = settings.value(QString("%1/height").arg(kUiTitle), window->height()).toInt(&hasHeight);

                if (hasWidth && hasHeight)
                    window->resize(width, height);
            }
        }

        if (showGui)
            show();
        else
            kClient->sendOscUpdate();

        // Timer
        msgTimer = startTimer(50);

        // First idle
        handleTimeout();

        // Main loop
        app->exec();
    }

    void quit()
    {
        qDebug("CarlaBridgeToolkitQt::quit()");
        CARLA_ASSERT(app);

        if (msgTimer != 0)
        {
            killTimer(msgTimer);
            msgTimer = 0;
        }

        if (window)
        {
            settings.setValue(QString("%1/pos_x").arg(kUiTitle), window->x());
            settings.setValue(QString("%1/pos_y").arg(kUiTitle), window->y());
            settings.setValue(QString("%1/width").arg(kUiTitle), window->width());
            settings.setValue(QString("%1/height").arg(kUiTitle), window->height());
            settings.sync();

            window->close();

            delete window;
            window = nullptr;
        }

#ifdef BRIDGE_CONTAINER
        if (embedContainer)
        {
            embedContainer->close();

            delete embedContainer;
            embedContainer = nullptr;
        }
#endif

        if (app)
        {
            if (! app->closingDown())
                app->quit();

            delete app;
            app = nullptr;
        }
    }

    void show()
    {
        qDebug("CarlaBridgeToolkitQt::show()");
        CARLA_ASSERT(window);

        if (window)
            window->show();
    }

    void hide()
    {
        qDebug("CarlaBridgeToolkitQt::hide()");
        CARLA_ASSERT(window);

        if (window)
            window->hide();
    }

    void resize(const int width, const int height)
    {
        qDebug("CarlaBridgeToolkitQt::resize(%i, %i)", width, height);
        CARLA_ASSERT(window);

        if (app->thread() != QThread::currentThread())
        {
            nextWidth  = width;
            nextHeight  = height;
            needsResize = true;
            return;
        }

        if (window)
            window->setFixedSize(width, height);

#ifdef BRIDGE_CONTAINER
        if (embedContainer)
            embedContainer->setFixedSize(width, height);
#endif
    }

#ifdef BRIDGE_CONTAINER
    void* getContainerId()
    {
        qDebug("CarlaBridgeToolkitQt::getContainerId()");
        CARLA_ASSERT(window);

        if (! embedContainer)
        {
            embedContainer = new QEmbedContainer(window);

            window->setCentralWidget(embedContainer);
            window->adjustSize();

            embedContainer->setParent(window);
            embedContainer->show();
        }

        return (void*)embedContainer->winId();
    }
#endif

protected:
    QApplication* app;
    QMainWindow* window;
    QSettings settings;
    int msgTimer;

    bool needsResize;
    int nextWidth, nextHeight;

#ifdef BRIDGE_CONTAINER
    QEmbedContainer* embedContainer;
#endif

    void handleTimeout()
    {
        if (! kClient)
            return;

        if (needsResize)
        {
            kClient->toolkitResize(nextWidth, nextHeight);
            needsResize = false;
        }

        if (kClient->isOscControlRegistered() && ! kClient->oscIdle())
        {
            killTimer(msgTimer);
            msgTimer = 0;
        }
    }

private:
    void timerEvent(QTimerEvent* const event)
    {
        if (event->timerId() == msgTimer)
            handleTimeout();

        QObject::timerEvent(event);
    }
};

// -------------------------------------------------------------------------

CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const client, const char* const uiTitle)
{
    return new CarlaBridgeToolkitQt(client, uiTitle);
}

CARLA_BRIDGE_END_NAMESPACE
