/*
 *  Copyright (C) 2005-2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#pragma once

#include "../../AddonBase.h"
#include "../Window.h"
#include "../renderHelper.h"

namespace kodi
{
namespace gui
{
namespace controls
{

//============================================================================
///
/// \defgroup cpp_kodi_gui_controls_CRendering Control Rendering
/// \ingroup cpp_kodi_gui
/// @brief \cpp_class{ kodi::gui::controls::CRendering }
/// **Window control for rendering own parts**
///
/// This rendering control is used  when own parts are needed.  You  have  the
/// control  over them  to render direct  OpenGL or  DirectX  content  to  the
/// screen set by the size of them.
///
/// Alternative  can be  the virtual  functions from t his been ignored if the
/// callbacks are  defined  by the  \ref CRendering_SetIndependentCallbacks function  and
/// class is used as single and not as a parent class.
///
/// It has the header \ref Rendering.h "#include <kodi/gui/controls/Rendering.h>"
/// be included to enjoy it.
///
/// Here you find the needed skin part for a \ref Addon_Rendering_control "rendering control"
///
/// @note The  call of  the control is only  possible  from  the corresponding
/// window as its class and identification number is required.
///

//============================================================================
///
/// \defgroup cpp_kodi_gui_controls_CRendering_Defs Definitions, structures and enumerators
/// \ingroup cpp_kodi_gui_controls_CRendering
/// @brief **Library definition values**
///

class ATTRIBUTE_HIDDEN CRendering : public CAddonGUIControlBase
{
public:
  //==========================================================================
  ///
  /// \ingroup cpp_kodi_gui_controls_CRendering
  /// @brief Construct a new control
  ///
  /// @param[in] window               related window control class
  /// @param[in] controlId            Used skin xml control id
  ///
  CRendering(CWindow* window, int controlId) : CAddonGUIControlBase(window)
  {
    m_controlHandle = m_interface->kodi_gui->window->get_control_render_addon(
        m_interface->kodiBase, m_Window->GetControlHandle(), controlId);
    if (m_controlHandle)
      m_interface->kodi_gui->control_rendering->set_callbacks(m_interface->kodiBase,
                                                              m_controlHandle, this, OnCreateCB,
                                                              OnRenderCB, OnStopCB, OnDirtyCB);
    else
      kodi::Log(ADDON_LOG_FATAL, "kodi::gui::controls::%s can't create control class from Kodi !!!",
                __FUNCTION__);
  }
  //--------------------------------------------------------------------------

  //==========================================================================
  ///
  /// \ingroup cpp_kodi_gui_controls_CRendering
  /// @brief Destructor
  ///
  ~CRendering() override
  {
    m_interface->kodi_gui->control_rendering->destroy(m_interface->kodiBase, m_controlHandle);
  }
  //--------------------------------------------------------------------------

  //==========================================================================
  ///
  /// \ingroup cpp_kodi_gui_controls_CRendering
  /// @brief To create rendering control on Add-on
  ///
  /// Function  creates the  needed rendering  control for Kodi  which becomes
  /// handled and processed from Add-on
  ///
  /// @note This is  callback  function  from Kodi  to  Add-on and  not to use
  /// for calls from add-on to this function.
  ///
  /// @param[in] x                    Horizontal position
  /// @param[in] y                    Vertical position
  /// @param[in] w                    Width of control
  /// @param[in] h                    Height of control
  /// @param[in] device               The device to use.  For OpenGL  is empty
  ///                                 on Direct X is the needed device send.
  /// @return                         Add-on needs to return true if successed,
  ///                                 otherwise false.
  ///
  virtual bool Create(int x, int y, int w, int h, void* device) { return false; }
  //--------------------------------------------------------------------------

  //==========================================================================
  ///
  /// \ingroup cpp_kodi_gui_controls_CRendering
  /// @brief Render process call from Kodi
  ///
  /// @note This  is callback  function from  Kodi to  Add-on  and not  to use
  /// for calls from add-on to this function.
  ///
  virtual void Render() {}
  //--------------------------------------------------------------------------

  //==========================================================================
  ///
  /// \ingroup cpp_kodi_gui_controls_CRendering
  /// @brief Call from Kodi to stop rendering process
  ///
  /// @note This  is callback  function from  Kodi to  Add-on  and not  to use
  /// for calls from add-on to this function.
  ///
  virtual void Stop() {}
  //--------------------------------------------------------------------------

  //==========================================================================
  ///
  /// \ingroup cpp_kodi_gui_controls_CRendering
  /// @brief Call from Kodi where add-on  becomes asked about  dirty rendering
  /// region.
  ///
  /// @note This  is callback  function from  Kodi to  Add-on  and not  to use
  /// for calls from add-on to this function.
  ///
  virtual bool Dirty() { return false; }
  //--------------------------------------------------------------------------

  //==========================================================================
  ///
  /// \ingroup cpp_kodi_gui_controls_CRendering
  /// \anchor CRendering_SetIndependentCallbacks
  /// @brief If the  class is used  independent (with "new CRendering")
  /// and not as  parent (with "cCLASS_own : CRendering") from own must
  /// be the callback from Kodi to add-on overdriven with own functions!
  ///
  void SetIndependentCallbacks(
      GUIHANDLE cbhdl,
      bool (*CBCreate)(GUIHANDLE cbhdl, int x, int y, int w, int h, void* device),
      void (*CBRender)(GUIHANDLE cbhdl),
      void (*CBStop)(GUIHANDLE cbhdl),
      bool (*CBDirty)(GUIHANDLE cbhdl))
  {
    if (!cbhdl || !CBCreate || !CBRender || !CBStop || !CBDirty)
    {
      kodi::Log(ADDON_LOG_ERROR, "kodi::gui::controls::%s called with nullptr !!!", __FUNCTION__);
      return;
    }

    m_interface->kodi_gui->control_rendering->set_callbacks(
        m_interface->kodiBase, m_controlHandle, cbhdl, CBCreate, CBRender, CBStop, CBDirty);
  }
  //--------------------------------------------------------------------------

private:
  /*
   * Defined callback functions from Kodi to add-on, for use in parent / child system
   * (is private)!
   */
  static bool OnCreateCB(void* cbhdl, int x, int y, int w, int h, void* device)
  {
    static_cast<CRendering*>(cbhdl)->m_renderHelper = kodi::gui::GetRenderHelper();
    return static_cast<CRendering*>(cbhdl)->Create(x, y, w, h, device);
  }

  static void OnRenderCB(void* cbhdl)
  {
    if (!static_cast<CRendering*>(cbhdl)->m_renderHelper)
      return;
    static_cast<CRendering*>(cbhdl)->m_renderHelper->Begin();
    static_cast<CRendering*>(cbhdl)->Render();
    static_cast<CRendering*>(cbhdl)->m_renderHelper->End();
  }

  static void OnStopCB(void* cbhdl)
  {
    static_cast<CRendering*>(cbhdl)->Stop();
    static_cast<CRendering*>(cbhdl)->m_renderHelper = nullptr;
  }

  static bool OnDirtyCB(void* cbhdl) { return static_cast<CRendering*>(cbhdl)->Dirty(); }

  std::shared_ptr<kodi::gui::IRenderHelper> m_renderHelper;
};

} /* namespace controls */
} /* namespace gui */
} /* namespace kodi */
