/***

  Olive - Non-Linear Video Editor
  Copyright (C) 2022 Olive Team

  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 3 of the License, or
  (at your option) 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.

  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 MANAGEDDISPLAYOBJECT_H
#define MANAGEDDISPLAYOBJECT_H

//#define USE_QOPENGLWINDOW

#include <QMouseEvent>
#include <QOpenGLContext>
#ifdef USE_QOPENGLWINDOW
#include <QOpenGLWindow>
#else
#include <QOpenGLWidget>
#endif

#include "node/color/colormanager/colormanager.h"
#include "render/renderer.h"
#include "widget/menu/menu.h"

namespace olive {

class ManagedDisplayWidgetOpenGL
#ifdef USE_QOPENGLWINDOW
    : public QOpenGLWindow
#else
    : public QOpenGLWidget
#endif
{
  Q_OBJECT
public:
  ManagedDisplayWidgetOpenGL() = default;

  virtual ~ManagedDisplayWidgetOpenGL() override
  {
    if (context()) {
      DestroyListener();
      disconnect(context(), &QOpenGLContext::aboutToBeDestroyed,
                 this, &ManagedDisplayWidgetOpenGL::DestroyListener);
    }
  }

signals:
  // Render signals
  void OnInit();
  void OnPaint();
  void OnDestroy();

protected:
  virtual void initializeGL() override
  {
    connect(context(), &QOpenGLContext::aboutToBeDestroyed,
            this, &ManagedDisplayWidgetOpenGL::DestroyListener,
            Qt::DirectConnection);

    emit OnInit();
  }

  virtual void paintGL() override
  {
    emit OnPaint();
  }

private slots:
  void DestroyListener()
  {
    makeCurrent();

    emit OnDestroy();

    doneCurrent();
  }

};

#define MANAGEDDISPLAYWIDGET_DEFAULT_DESTRUCTOR_INNER \
  makeCurrent();OnDestroy();doneCurrent()
#define MANAGEDDISPLAYWIDGET_DEFAULT_DESTRUCTOR(x) \
  virtual ~x() override{MANAGEDDISPLAYWIDGET_DEFAULT_DESTRUCTOR_INNER;}

class ManagedDisplayWidget : public QWidget
{
  Q_OBJECT
public:
  ManagedDisplayWidget(QWidget* parent = nullptr);

  virtual ~ManagedDisplayWidget() override;

  /**
   * @brief Disconnect a ColorManager (equivalent to ConnectColorManager(nullptr))
   */
  void DisconnectColorManager();

  /**
   * @brief Access currently connected ColorManager (nullptr if none)
   */
  ColorManager* color_manager() const;

  /**
   * @brief Get current color transform
   */
  const ColorTransform& GetColorTransform() const;

  /**
   * @brief Get menu that can be used to select the colorspace
   */
  Menu* GetColorSpaceMenu(QMenu* parent, bool auto_connect = true);

  /**
   * @brief Get menu that can be used to select the display transform
   */
  Menu* GetDisplayMenu(QMenu* parent, bool auto_connect = true);

  /**
   * @brief Get menu that can be used to select the view transform
   */
  Menu* GetViewMenu(QMenu* parent, bool auto_connect = true);

  /**
   * @brief Get menu that can be used to select the look transform
   */
  Menu* GetLookMenu(QMenu* parent, bool auto_connect = true);

  /**
   * @brief Passes update signal through to inner widget
   */
  void update();

  virtual bool eventFilter(QObject *o, QEvent *e) override;

public slots:
  /**
   * @brief Replaces the color transform with a new one
   */
  void SetColorTransform(const ColorTransform& transform);

  /**
   * @brief Connect a ColorManager (ColorManagers usually belong to the Project)
   */
  void ConnectColorManager(ColorManager* color_manager);

signals:
  /**
   * @brief Emitted when the color processor changes
   */
  void ColorProcessorChanged(ColorProcessorPtr processor);

  /**
   * @brief Emitted when a new color manager is connected
   */
  void ColorManagerChanged(ColorManager* color_manager);

  void frameSwapped();

protected:
  /**
   * @brief Provides access to the color processor (nullptr if none is set)
   */
  ColorProcessorPtr color_service();

  /**
   * @brief Enables a context menu that allows simple access to the DVL pipeline
   */
  void EnableDefaultContextMenu();

  /**
   * @brief Function called whenever the processor changes
   *
   * Default functionality is just to call update()
   */
  virtual void ColorProcessorChangedEvent();

  Renderer* renderer() const
  {
    return attached_renderer_;
  }

  void makeCurrent();

  void doneCurrent();

#ifdef USE_QOPENGLWINDOW
  QWindow*
#else
  QWidget*
#endif
  inner_widget() const
  {
    return inner_widget_;
  }

  /**
   * @brief Get inner widget as paint device for QPainter
   *
   * NOTE: This will be incompatible with QVulkanWindow so functions using it
   *       will need to be replaced soon.
   */
  QPaintDevice *paint_device() const;

  void SetInnerMouseTracking(bool e);

  QRect GetInnerRect() const
  {
    return wrapper_ ? wrapper_->rect() : QRect();
  }

  VideoParams GetViewportParams() const;

protected slots:
  /**
   * @brief Called whenever the internal rendering context has been created
   */
  virtual void OnInit();

  /**
   * @brief Called while the internal rendering context is being rendered
   */
  virtual void OnPaint() = 0;

  /**
   * @brief Called just before the internal rendering context is destroyed
   */
  virtual void OnDestroy();

private:
  /**
   * @brief Call this if this user has selected a different display/view/look to recreate the processor
   */
  void SetupColorProcessor();

  /**
   * @brief Cleanup function
   */
  void ClearOCIOLutTexture();

  /**
   * @brief Main drawing surface abstraction
   */
#ifdef USE_QOPENGLWINDOW
  QWindow* inner_widget_;
#else
  QWidget* inner_widget_;
#endif
  QWidget *wrapper_;

  /**
   * @brief Renderer abstraction
   */
  Renderer* attached_renderer_;

  /**
   * @brief Connected color manager
   */
  ColorManager* color_manager_;

  /**
   * @brief Color management service
   */
  ColorProcessorPtr color_service_;

  /**
   * @brief Internal color transform storage
   */
  ColorTransform color_transform_;

private slots:
  /**
   * @brief Sets all color settings to the defaults pertaining to this configuration
   */
  void ColorConfigChanged();

  /**
   * @brief The default context menu shown
   */
  void ShowDefaultContextMenu();

  /**
   * @brief If GetDisplayMenu() is called with `auto_connect` set to true, it will be connected to this
   */
  void MenuDisplaySelect(QAction* action);

  /**
   * @brief If GetViewMenu() is called with `auto_connect` set to true, it will be connected to this
   */
  void MenuViewSelect(QAction* action);

  /**
   * @brief If GetLookMenu() is called with `auto_connect` set to true, it will be connected to this
   */
  void MenuLookSelect(QAction* action);

  /**
   * @brief If GetColorSpaceMenu() is called with `auto_connect` set to true, it will be connected to this
   */
  void MenuColorspaceSelect(QAction* action);

};

}

#endif // MANAGEDDISPLAYOBJECT_H
