/*
  Copyright (C) Skeletex Research, s.r.o. - All Rights Reserved
  Unauthorized copying of this file, via any medium is strictly prohibited
  Proprietary and confidential
*/
#pragma once
#include <vector>

#include <Utils/GeometryStructures.h>
#include <GLW/ArrayObject.h>
#include <HIRO_DRAW/API.h>



namespace hiro::draw
{

  //! List of available names for geometry objects generated by hiro::draw::PrimitiveDatabase.
  enum class GeometryName
  {
    cube,       //!< Standard cube with unit side length.
    sphere_s0,  //!< Icosahedron with unit radius.
    sphere_s1,  //!< Icosahedron with unit radius subdivided.
    sphere_s2,  //!< Icosahedron with unit radius subdivided twice.
    octahedron, //!< Anisotropic thin octahedron laying on x-axis <0,1>.
    camera,     //!< Camera mesh aligned with view space looking to negative z.
    plane_x,    //!< Unit sized plane perpendicular to x-axis.
    plane_y,    //!< Unit sized plane perpendicular to y-axis.
    plane_z,    //!< Unit sized plane perpendicular to z-axis.
    _count      //!< Number of available names.
  };

  //! Converts hiro::draw::GeometryName type to the text reprersentation.
  HIRO_DRAW_API std::string ToString(hiro::draw::GeometryName geom);



  //! Holds information about geometry.
  struct Geometry
  {
    //! Identifier name.
    hiro::draw::GeometryName name;
    //! Number of vertices which are entering vertex shader.
    uint32_t vertex_count;
    //! Number of rendered faces.
    uint32_t face_count;
    //! Pointer to array buffer object holding OpenGL buffers.
    glw::PArrayObject buffer;
  };



  //! List of available names for primitives generated by hiro::draw::PrimitiveDatabase.
  enum class PrimitiveName
  {
    line,   //! Unit sized line laying on x-axis.
    axes,   //! Unit sized spacial basis.
    square, //! Unit sized 2D square in interval <0,1> with texture coordinates.
    _count  //! Number of available names.
  };

  //! Converts hiro::draw::PrimitiveName type to text reprersentation.
  HIRO_DRAW_API std::string ToString(hiro::draw::PrimitiveName prim);



  /*!
    \brief Database of common mesh primitives. Automatically manages OpenGL resources.
  */
  class HIRO_DRAW_API PrimitiveDatabase
  {
  public:
    PrimitiveDatabase(const hiro::draw::PrimitiveDatabase &) = delete;

    static hiro::draw::PrimitiveDatabase &GetSingleton();

    //! Request single draw call of a specified primitive.
    void Draw(const hiro::draw::PrimitiveName primitive) const;

    //! Request single draw call of a specified geometry.
    void Draw(const hiro::draw::GeometryName geom) const;

    //! Request instanced draw call of a specified primitive.
    void DrawInstanced(uint32_t count, const hiro::draw::PrimitiveName primitive) const;

    //! Request instanced draw call of a specified geometry.
    void DrawInstanced(uint32_t count, const hiro::draw::GeometryName geom) const;

    //! Request pointer to buffer object of primitive with specified name.
    const glw::PArrayObject &Get(const hiro::draw::PrimitiveName primitive) const;

    //! Request pointer to geometry object with specified name.
    const hiro::draw::Geometry &GetGeometry(const hiro::draw::GeometryName geom) const;

    //! Releases all allocated resources from video memory.
    void Free();

    //! Generates sphere with defined radius. Smoothness parameter influences number of sphere segments.
    static glw::PArrayObject GenerateUvSphere(float radius, uint32_t smoothness);

    //! Generates plane at position, with specified size, perpendicular to specified axis.
    static glw::PArrayObject GeneratePlane(const glm::vec3 &position, float sizing, geom::Axis perpandicular_to);

  private:
    PrimitiveDatabase();
    ~PrimitiveDatabase() = default;
    std::vector<glw::PArrayObject> primitives_;
    std::vector<hiro::draw::Geometry> geometries_;
  };

}
