#include "../vsgl3/glHelper.h"

#include <vector>
#include <memory>

enum class PlaneType { PLUS_X, PLUS_Y, PLUS_Z, MINUS_X, MINUS_Y, MINUS_Z }; // C++11 'strong' enums

struct QuadAA // Axis Aligned Quad
{
  QuadAA(){}
  QuadAA(PlaneType a_axis, float3 a_pos, float2 a_size, float3 a_color, int a_subdivs) : axis(a_axis), centerPos(a_pos), size(a_size), diffuseColor(a_color), subdivs(a_subdivs){}

  float3 boxMin() const;
  float3 boxMax() const;

  float3 dirX() const;
  float3 dirY() const;

  float3 normal() const;

  PlaneType axis; 
  float2    size;
  float3    centerPos;

  float3 diffuseColor;
  int subdivs;
};


struct PatchGeom // this is all relative to patch geometry params, scene and materials
{
  float3 vmin;
  float3 vmax;

  float3 normal;
  float3 color;        // color which used for drawing patch
  float3 diffuseColor;

  float  surfaceArea;
  PlaneType axis;

  // aux geometry functions
  //
  float3 dirX() const;
  float3 dirY() const;
  
  float sizeX() const;
  float sizeY() const;
};

struct PatchRadiosity // separate radiosity params from geometry
{
  float3 emmision;
  float3 reflectance;
  float3 incident;
  float3 excident;
  float3 deposit; // this is summ of all passes
};

struct EmissivePatch
{

  EmissivePatch(float3 a_color, int a_id) : color(a_color), patchId(a_id) {}

  int patchId;
  float3 color;
};

struct RadiosityScene
{
  RadiosityScene();
  ~RadiosityScene();

  std::vector<QuadAA>         m_quads;
  std::vector<PatchGeom>      m_patches;
  std::vector<PatchRadiosity> m_patchesRad;
  std::vector<float>          m_formFactors;

  std::vector<EmissivePatch>  m_initialEmissivePatches;

  void initGL();
  void CopyToGL();

  void Draw(float4x4 worldView, float4x4 projection);
  void DrawIndices(float4x4 worldView, float4x4 projection);

protected:

  GLuint m_vbo;
  GLuint m_vao;

  std::unique_ptr<ShaderProgram> m_pDrawProg;
  std::unique_ptr<ShaderProgram> m_pDrawIdProg;
};

std::vector<PatchGeom> subdivideQuads(const std::vector<QuadAA>& a_quads);
std::vector<float>     computeFormFactors(const std::vector<PatchGeom>& a_patches, const std::vector<QuadAA>& a_quads);
std::vector<float3>    radiosityBruteForce(const std::vector<PatchGeom>& a_patches, const std::vector<float>& a_formFactors, const std::vector<EmissivePatch>& a_lightSources, int a_numPasses);

void checkFormFactors(std::vector<PatchGeom>& a_patches, std::vector<float>& a_formFactors, int a_w);