#include "Globals.hpp"
#include "PhongShader.hpp"

using namespace SH;

PhongShader::PhongShader()
{
}

PhongShader::~PhongShader()
{
}

void PhongShader::init()
{
  vsh = SH_BEGIN_VERTEX_PROGRAM {
    // declare input vertex parameters
    // unpacked in order given
    ShInputTexCoord2f ui;       // texture coords
    ShInputNormal3f normal;     // normal vectors (MCS)
    ShInputPosition4f pm;          // poisition (MCS)

    // declare output vertex parameters
    // packed in order given
    ShOutputVector3f hv;        // half-vector (VCS)
    ShOutputTexCoord2f uo = ui; // texture coords
    ShOutputNormal3f nv;        // normal (VCS)
    ShOutputColor3f ec;         // irradiance
    ShOutputPosition4f pd;         // position (HDCS)

    // compute VCS position
    ShPoint3f pv = (Globals::mv | pm)(0,1,2);
    // compute DCS position
    pd = Globals::mvp | pm;
    // compute normalized VCS normal
    ShNormal3f nvt = normalize(Globals::mv | normal)(0,1,2);
    nv = nvt;
    // compute normalized VCS light vector
    ShVector3f lvv = Globals::lightPos - pv;
    ShAttrib1f rsq = ShConstant1f(1.0)/(lvv|lvv);
    lvv *= sqrt(rsq);
    // compute irradiance
    ShAttrib1f ct = (nvt | lvv);
    ct *= ct > ShAttrib1f(0.0);
    ec = ShColor3f(1.0, 1.0, 1.0) * ct;
    // compute normalized VCS view vector
    ShVector3f vvv = -normalize(pv);
    // compute normalized VCS half vector
    hv = normalize(lvv + vvv);
  } SH_END_PROGRAM;

  fsh = SH_BEGIN_FRAGMENT_PROGRAM {
    // declare input fragment parameters
    // unpacked in order given
    ShInputVector3f hv;   // half-vector (VCS)
    ShInputTexCoord2f u;  // texture coordinates
    ShInputNormal3f nv;   // normal (VCS)
    ShInputColor3f ec;    // irradiance
    ShInputPosition3f pdxy; // fragment position (DCS)

    // Declare output fragment parameters
    // packed in order given
    ShOutputColor3f fc;            // fragment color

    // compute texture-mapped Blinn-Phong model
    fc = (Globals::color
          + ShAttrib3f(1.0, 1.0, 1.0) * (dot(normalize(hv), normalize(nv)) ^ Globals::exponent)) * ec;
  } SH_END_PROGRAM;

}

void PhongShader::apply(void)
{
  shBindShader(vsh);
  shBindShader(fsh);
}

void PhongShader::vertex(const SH::ShPoint4f& n, // normal
                         const SH::ShPoint4f& t, // tangent
                         const SH::ShPoint4f& tex, // texture
                                                   // coordinates
                         const SH::ShPoint4f& t2, // secondary tangent
                         const SH::ShPoint4f& v // vertex
                         )
{
  pushSmAttrib(tex);
  pushSmAttrib(n);
  pushSmVertex(v);
}

// Instantiate the shader
PhongShader phongShader;
