#ifndef MOUNTPHONGSHADER_HXX
#define MOUNTPHONGSHADER_HXX

#include "Shader.hxx"
#include "Noise.hxx"

/* This class determines the dye of the mountains.
** The base is colored with a mix of two colors and 
** the peak is covered with snow.
*/
class MountPhongShader : public Shader
{

private:

    // base colors
    Vec3f first_color;    // 1. base color
    Vec3f second_color;   // 2. base color
   
    // Phong parameters 
    float ka;             // ambient coefficient
    float kd;             // diffuse reflection coefficients
    float ks;             // specular refelection coefficients
    float ke;             // shininess exponent
    
    //snow lines
    float low_level;      // lower level
    float high_level;     // higher level

public:

    MountPhongShader(Scene *scene, Vec3f first, Vec3f second, 
		     float ka,float kd, float ks, float ke, float low, float high)
      : Shader(scene),first_color(first),second_color(second),ka(ka),kd(kd),ks(ks),ke(ke),low_level(low),high_level(high)
    {};
    
    virtual Vec3f Shade(Ray &ray)
    {
        /* determine color */
       
        Vec3f color;
        Vec3f point = ray.org + ray.t * ray.dir;
	float height = point.y() + (PerlinNoise3D_new(point.x(), point.y(), point.z()) / 4.0f);
	float noise = fabs(PerlinNoise3D_new(point.x(), point.y(), point.z()));
	
	//peak is white and rest is random mix between two base colors
	if(height >= high_level)
	    color = Vec3f(0.9f); 

	else
	    color = noise * first_color + (1.0f-noise) * second_color;
	

	/* Phong shading */

        // get shading normal
        Vec3f normal = ray.hit->GetNormal(ray);
        
        // turn normal to front
        if (Dot(normal,ray.dir) > 0)
            normal = -normal;
        
        // calculate reflection vector
        Vec3f reflect = ray.dir - 2*Dot(normal,ray.dir)*normal;
        
        // ambient term
        Vec3f ambientIntensity(1,1,1);
        Vec3f ambientColor = ka * color;
        Vec3f result = Product(ambientColor, ambientIntensity);
        
        // shadow ray (up to now only for the light direction)    
        Ray shadow;
        shadow.org = ray.org + ray.t * ray.dir;
        
        // iterate over all light sources
        for (unsigned int l=0; l < scene->mLights.size(); l++)
	{      
	    // get direction to light, and intensity 
            Vec3f lightIntensity;
            Vec3f result_local = Vec3f(0.0);

            // check whenever the shader is computing area light source
            for(unsigned int s = 0; s < scene->mLights[l]->GetNumberOfRays(); s++)
            {
                // illuminate the ray by the light source
                if (scene->mLights[l]->Illuminate(shadow, lightIntensity, s))
		{	
		    // compute distance to the light source 
		    float distance = shadow.t;
        
		    // diffuse term, also used as a check if illuminating the front-side
		    float cosLightNormal = Dot(shadow.dir,normal);
		    if (cosLightNormal > 0)
		    {
			// if the ray is occluded, hence there is a shadow
			// we put this here to optimize all things, since we work only for in-front surfaces
			if (scene->castShadows && scene->Intersect(shadow) && shadow.hit->castShadows())
			{
			    // if the shadow ray intersects a surface which is near then the light source, then we 
			    // are in shadow
			    if (shadow.t < distance)
			        continue;
			}
			
			// compute diffuse term	            
			Vec3f diffuseColor = kd * color;
			result_local = result_local + Product(diffuseColor * cosLightNormal,
							      lightIntensity);
			
			// specular term is computed only if shading the front-side
			float cosLightReflect = Dot(shadow.dir,reflect);
			if (cosLightReflect > 0)
			{
			    Vec3f specularColor = ks * Vec3f(1,1,1); // white highlight;
			    result_local = result_local + Product(specularColor * powf(cosLightReflect,ke),
								  lightIntensity);
			}
		    }
                }
            }    
	    
            result += result_local / float(scene->mLights[l]->GetNumberOfRays());
        }
        
        return result;
    };
};

#endif