#include "DrawCanvasManager.h"
#include "DrawCanvas.h"

#include "RKTransformObject.h"
#include "RKMaterial.h"

#include "CasualCore/Graphics/Level.h"
#include "CasualCore/RKEngine/Public/Include/RKVertexDeclaration.h"

static const char* s_guiVertexFormat = "DrawCanvasVertexFormat";

/// Helper class to re-direct draw calls to the canvas manager class (prevents exposing implementtation details)
class DrawCanvasObject : public RKTransformObject
{
public:

  explicit DrawCanvasObject(DrawCanvasManager& a_manager) : m_manager(a_manager) {}
  virtual void Draw(const class RKCamera* camera, class RKFrustumFitter* frustumFitter) override
  {
    m_manager.Draw();
  }

  DrawCanvasManager & m_manager;
};

void DrawCanvasManager::Init(CasualCore::Level* a_level)
{
  // Store the canvas level object
  m_level = a_level;

  // Create canvas object
  m_drawObject = RKNEW(DrawCanvasObject)(*this);
  m_drawObject->Init();
  m_drawObject->PostInit();

  // Add object to the level
  m_level->AddObject(m_drawObject);

  // Create the shared vertex decleration
  m_vertexDecl = RKVertexDeclaration_Create(s_guiVertexFormat);
    RKVertexDeclaration_AddElement(m_vertexDecl, RKDECLTYPE_FLOAT3, RKDECLUSAGE_POSITION,   0,  0);
    RKVertexDeclaration_AddElement(m_vertexDecl, RKDECLTYPE_FLOAT2, RKDECLUSAGE_TEXCOORD_0, 12, 0);
    RKVertexDeclaration_AddElement(m_vertexDecl, RKDECLTYPE_FLOAT4, RKDECLUSAGE_COLOR,      20, 0);
  RKVertexDeclaration_End(m_vertexDecl, 36);

  // Create alpha material
  m_pAlphaMaterial = RKMaterial_Create(DrawCanvas::GetAlphaMaterialName(), false, false, "bneUI", "white");
  RKMaterialType *pType = m_pAlphaMaterial->GetMaterialType();
  pType->SetSrcBlendMode(RK_SRC_ALPHA);
  pType->SetDstBlendMode(RK_ONE_MINUS_SRC_ALPHA);
  pType->SetEnableBlending(true);
  pType->SetDepthTest(false);
  pType->SetDepthWrite(false);
  pType->SetEnableCulling(false);

  // Create solid material
  m_pSolidMaterial = RKMaterial_Create(DrawCanvas::GetSolidMaterialName(), false, false, "bneUI", "white");
  pType = m_pSolidMaterial->GetMaterialType();
  pType->SetEnableBlending(false);
  pType->SetDepthTest(false);
  pType->SetDepthWrite(false);
  pType->SetEnableCulling(false);
}

void DrawCanvasManager::Done()
{
  RKASSERT(m_canvasArray.Size() == 0, "Drawing canvas objects not deleted");

  // Destroy solid and alpha material
  RKMaterial_Destroy(&m_pSolidMaterial);
  RKMaterial_Destroy(&m_pAlphaMaterial);

  // Remove object from the level (we delete it ourselves)
  if (m_drawObject)
  {
    m_drawObject->Deinit();
    m_level->RemoveObject(m_drawObject, false);
    SAFE_DELETE(m_drawObject);
  }
  m_level = nullptr;

  // Destroy the vertex format
  RKVertexDeclaration_Destroy(&m_vertexDecl);
}

DrawCanvas* DrawCanvasManager::CreateCanvas()
{
  DrawCanvas* retCanvas = RKNEW(DrawCanvas)(m_vertexDecl);
  m_canvasArray.Append(retCanvas);
  return retCanvas;
}

void DrawCanvasManager::DestroyCanvas(DrawCanvas* a_canvas)
{
  m_canvasArray.EraseFirst(a_canvas);
  RKDELETE(a_canvas);
}

void DrawCanvasManager::Draw()
{
  // NOTE: It is very important for thread safety that this is not called except from an object draw call

  // Loop for all canvas objects and draw them
  for (uint32_t i = 0; i < m_canvasArray.Size(); i++)
  {
    m_canvasArray[i]->Draw();
  }
}


