/////////////////////////////////////////////////////////////////////////
//                                                                     //
// This file contains the implementation of the CConcreteSurface       //
// class.                                                              //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
#include "popassert.hpp"
#include <iostream>
#include "CConcreteSurface.hpp"
using std::string;
using std::ostream;




/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  Class    : CConcreteSurface                                        //
//                                                                     //
//  Function : CConcreteSurface (constructor)                          //
//                                                                     //
//  Comments : Gives default values to properties and registers        //
//             them.                                                   //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
CConcreteSurface::CConcreteSurface(const string& strName,
                                   bool useTransformation)
  : CSurface(strName)                  ,
    m_PropSurfaceColor(1, 1, 1)        ,
    m_PropAmbientCoef(MAKE_REAL(0.2))  ,
    m_PropDiffuseCoef(MAKE_REAL(0.8))  ,
    m_PropSpecularCoef(MAKE_REAL(0.0)) ,
    m_PropShininessCoef(5)             ,
    m_PropReflexionCoef(0)             ,
    m_PropRefractionCoef(0)            ,
    m_displayList(0)                   ,
    m_useTransformation(useTransformation)
{
  REGISTER_PROPERTY(SurfaceColor);
  REGISTER_PROPERTY(AmbientCoef);
  REGISTER_PROPERTY(DiffuseCoef);
  REGISTER_PROPERTY(SpecularCoef);
  REGISTER_PROPERTY(ShininessCoef);
  REGISTER_PROPERTY(ReflexionCoef);
  REGISTER_PROPERTY(RefractionCoef);
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  Class    : CConcreteSurface                                        //
//                                                                     //
//  Function : ~CConcreteSurface (destructor)                          //
//                                                                     //
//  Comments :                                                         //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
CConcreteSurface::~CConcreteSurface()
{
  glDeleteLists(m_displayList, 1);
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  Class    : CConcreteSurface                                        //
//                                                                     //
//  Function : Intersect()                                             //
//                                                                     //
//  Comments : This function fills a container with intersections      //
//             between the ray and the scene object.  It returns the   //
//             number of hits.                                         //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
int CConcreteSurface::Intersect(const CRay&              ray,
                                hitcontainer&            hits,
                                const CConcreteSurface **ppFirstHit) const
{
  if (ppFirstHit != NULL)
    *ppFirstHit = this;

  return Intersect(ray, hits);
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  Class    : CConcreteSurface                                        //
//                                                                     //
//  Function : DrawGL()                                                //
//                                                                     //
//  Comments : This function renders the surface in OpenGL.            //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
void CConcreteSurface::DrawGL() const
{
  glCallList(m_displayList);
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  Class    : CConcreteSurface                                        //
//                                                                     //
//  Function : GenerateShaderForObject()                               //
//                                                                     //
//  Comments : This function generates shader code for the surface.    //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
void CConcreteSurface::GenerateShaderForObject(ostream& o,
                                               const string& className) const
{
  o << "const " << className << " " << getName()
    << " = " << className << "(\n"

    << "    Surface(vec3(" << m_PropSurfaceColor.red()
    << " , " << m_PropSurfaceColor.green()
    << " , " << m_PropSurfaceColor.blue() << "),\n"
    << "            " << m_PropAmbientCoef << ",\n"
    << "            " << m_PropDiffuseCoef << ",\n"
    << "            " << m_PropSpecularCoef << ",\n"
    << "            " << m_PropShininessCoef << ",\n"
    << "            " << m_PropReflexionCoef << ",\n"
    << "            " << m_PropRefractionCoef << ",\n"
    << "            " << m_PropRefractionIndex << ")";
}




/////////////////////////////////////////////////////////////////////////
//                                                                     //
//  Class    : CConcreteSurface                                        //
//                                                                     //
//  Function : PreprocessGL()                                          //
//                                                                     //
//  Comments : Do the necessary preprocessing that a surface needs the //
//             be rendered in OpenGL.                                  //
//                                                                     //
/////////////////////////////////////////////////////////////////////////
void CConcreteSurface::PreprocessGL()
{
  // Generate a display list for this concrete surface
  m_displayList = glGenLists(1);

  // Generate a display list corresponding to the rendering of all
  // surfaces
  glNewList(m_displayList, GL_COMPILE);




  ///////////////////////////////
  // OpenGL rendering commands //
  ///////////////////////////////

  // FIXME: using float, not real_t
  // Material color setting
  GLfloat values[4];
  real_t coef;

  // Ambient color
  coef = m_PropAmbientCoef;
  values[0] = (GLfloat) (coef * m_PropSurfaceColor.red());
  values[1] = (GLfloat) (coef * m_PropSurfaceColor.green());
  values[2] = (GLfloat) (coef * m_PropSurfaceColor.blue());
  values[3] = 1;
  glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, values );

  // Diffuse color
  coef = m_PropDiffuseCoef;
  values[0] = (GLfloat) (coef * m_PropSurfaceColor.red());
  values[1] = (GLfloat) (coef * m_PropSurfaceColor.green());
  values[2] = (GLfloat) (coef * m_PropSurfaceColor.blue());
  glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, values );

  // Specular color
  coef = m_PropSpecularCoef;
  values[0] = (GLfloat) coef;
  values[1] = (GLfloat) coef;
  values[2] = (GLfloat) coef;
  glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, values );

  // Shininess (only one value)
  values[0] = (GLfloat) m_PropShininessCoef;
  glMaterialfv( GL_FRONT_AND_BACK, GL_SHININESS, values );


  if (m_useTransformation)
    {
      // Push matrix
      PushMatrixState();
    }

  // Call to the real function
  ConcreteDrawGL();

  if (m_useTransformation)
    {
      // Pop matrix
      PopMatrixState();
    }

  //////////////////////////////////////
  // End of OpenGL rendering commands //
  //////////////////////////////////////




  // End of display list
  glEndList();
}
