//#extension GL_ARB_texture_rectangle : enable

uniform sampler2D noiseMap;

//uniform sampler2DRect waternormalsMap;
//uniform sampler2D ringsMap;
uniform vec2 wg_cnt;


//uniform sampler2DRect dynamicRingsMap;
uniform sampler2D dynamicRingsMap;
varying vec4 proj_tc_normals;
uniform vec2 dynamicRingsMapWH;
uniform float ripple_scale;

//uniform samplerCube cubeMap;

uniform sampler3D wave3D;
uniform float slice_nr;

uniform float fadeFactor;

uniform sampler2D reflectMap;
uniform sampler2D refractMap;


uniform vec3 lightDir;
uniform vec4 lightColor;
uniform vec4 cc_below;
uniform vec4 cc;

uniform vec4 camPos;
uniform vec4 camDir;
uniform vec4 camUp;
uniform float sc_ratio;

uniform float texW;
uniform float texH;

uniform float waveScale;
uniform float waveOff;


varying vec2 tc;
varying float vertex_height;
varying vec4 vertexPos;

varying vec4 tmp_pos;

uniform vec2 texLookupScale;

varying vec3 eye2objW;



//tangent space
vec3 T, N, B;


//x ... d_focus
//y ... d_near
//z ... d_far
//w ... clamp_far
uniform vec4 dof_params; 


float ComputeDepthBlur(float depth)
{
  float f;

  if(depth < dof_params.x) // if(depth < d_focus)
    {
      // distance between near blur distance and
      // focal distance
      // map range to [-1, 0]

      // f = (depth - d_focus) / (d_focus - d_near);
      f = (depth - dof_params.x) / (dof_params.x - dof_params.y);
    }
  else
    {
      // distance between focal distance and
      // far blur distance
      // map range to [0, 1]

      // f = (depth - d_focus) / (d_far - d_focus);
      f = (depth - dof_params.x) / (dof_params.z - dof_params.x);
      f = clamp(f, 0.0, dof_params.w);
    }

  // scale and bias into [0, 1] range
  return (f * 0.5 + 0.5);
}



//
vec3 calc_normal(vec2 tc)
//vec3 calc_normal(vec2 tc, vec2 proj_tc)
{
  float sn = slice_nr/16.0;

  vec4 at_tc = texture3D(wave3D, vec3(tc, 12.0*sn), -0.5);

  vec4 at_tc4 = texture3D(wave3D, vec3(tc.xy*4.0, 20.0*sn), -0.5);

  //    vec4 at_tc_inv4 = texture3D(wave3D, vec3(tc*0.04, 1.5*(slice_nr/16.0)), -1.0);
  vec4 at_tc_inv4 = texture3D(wave3D, vec3(tc*0.04, 1.5*sn), -1.0);

  vec2 hdiff_base = (at_tc.gb*2.0 - 1.0)*waveScale;
			    
  vec2 hdiff_high = (at_tc4.gb*2.0 - 1.0)*waveScale;




    vec2 hdiff_low = (at_tc_inv4.gb*2.0 - 1.0)*waveScale;

    
    /*
    //orthogonal wavedir
    vec4 at_tc_inv4 = texture3D(wave3D, vec3(tc.yx*0.04, 1.5*(slice_nr/16.0)), -1.0);

    vec2 hdiff_low = vec2( ((at_tc_inv4.b*2.0 - 1.0)*waveScale*hdiff_scale)/1020.0,
			    ((at_tc_inv4.g*2.0 - 1.0)*waveScale*hdiff_scale)/1020.0 );
    */


    
    //    at_tc_inv4 = texture3D(wave3D, vec3(tc*0.125, 6.0*(slice_nr/16.0)), -1.0);
    at_tc_inv4 = texture3D(wave3D, vec3(tc*0.125, 6.0*sn), -1.0);

   
    vec2 hdiff_low2 = (at_tc_inv4.gb*2.0 - 1.0)*waveScale;
    
      vec2 combined_hdiff = 0.3*mix(hdiff_low,hdiff_low2,0.7) + 0.4*hdiff_base + 0.3*hdiff_high;

      combined_hdiff /= 1020.0;

  
     //sharper, more hifrequ detail
  vec3 dx = vec3(2.0 *(1.0/texW),
		 combined_hdiff.x,
		 0.0);

  vec3 dz = vec3(0.0,
		 combined_hdiff.y,
		 2.0 *(1.0/texH)
		 );
  

  //tangent space
  T = normalize(dx);
  B = normalize(dz);
  N = normalize(cross(dz, dx));

  return N;

  //!    return normalize(cross(dz, dx));


}

float fastFresnel(vec3 I, vec3 N, float R0, float power)
{
  //  return clamp( R0 + (1.0-R0)*pow(1.0 - dot(I, N), power) , 0.2, 1.0);
  //  return clamp( R0 + (1.0-R0)*pow(1.0 - dot(I, N), power) , 0.1, 0.9);
  //
  return clamp( R0 + (1.0-R0)*pow(1.0 - dot(I, N), power) , 0.0, 0.75);
  //  return clamp( R0 + (1.0-R0)*pow(1.0 - dot(I, N), power) , 0.0, 0.5);
  //  return clamp( R0 + (1.0-R0)*pow(1.0 - dot(I, N), power) , 0.0, 1.0);

}

//void calc_light_intensity(vec3 V, vec3 N, out float diff, out float spec)
float calc_light_intensity(vec3 V, vec3 N)
{
  //  float kA = 0.005;
  //
float kA = 0.05;

  //float kD = 0.75;
  //float kD = 0.65;
  //
  float kD = 0.5;

//  float kS = 0.95;
///DEBUG
  float kS = 0.8;

  //  vec4 amb = kA * lightColor;

  //    vec4 diff = min(max(dot(N, lightDir), 0.0)*kD,1.0)*lightColor;
    //!
float diff = max(dot(N, lightDir), 0.0)*kD;
    //diff = max(dot(N, lightDir), kA)*kD;

    //    vec4 spec = min(pow(max(dot(normalize(lightDir+V), N), 0.0), 22.0)*kS, 1.0)*lightColor;
    //    float spec = pow(max(dot(normalize(lightDir+V), N), 0.0), 22.0)*kS;
     //!
    float spec = pow(max(dot(normalize(lightDir+V), N), 0.0), 16.0)*kS;
     //     spec = pow(max(dot(normalize(lightDir+V), N), kA), 16.0)*kS;


     
    //    return clamp(diff+spec,0.0,1.0);

    //    return clamp(diff+spec,0.1,1.0); //ambient intensity "for free"
    //    return clamp(diff+spec,0.25,1.0); //ambient intensity "for free"

    //    return clamp(diff+spec,0.3,1.0); //ambient intensity "for free"
     //!    return clamp(kA+diff+spec,0.1,1.0);
    return clamp(kA+diff+spec,0.1,0.8);

    //    return clamp(kA+diff+spec*vec4(0.5,0.5,0.8, 0.0),0.1,1.0);

  //  return amb+diff+spec;
    //return diff+spec;
  //  return amb+spec;
}

void main() 
{
  //  vec2 scaled_tc = tc*vec2(1.0/texW, 1.0/texH);
  vec2 scaled_tc = tc*texLookupScale.xy;

  vec4 reflection_color;
  vec4 refraction_color;

  vec4 color;
  float r_off = 0.045;
  float r_off_refr = 0.035;


    vec3 proj_coords;
    proj_coords.xy = tmp_pos.xy/tmp_pos.w;
    proj_coords.z = r_off/(tmp_pos.z/tmp_pos.w);


    vec3 proj_coords_refr;
    proj_coords_refr.xy = tmp_pos.xy/tmp_pos.w;
    //less perturbation on refracted parts of terrain
    proj_coords_refr.z = (r_off_refr)/(tmp_pos.z/tmp_pos.w);

    vec3 world_space_normal;
  ////!!    vec3 world_space_normal = calc_normal(scaled_tc, proj_coords.xy);

    //unsigned byte tex
    //   vec3 world_space_normal = normalize(texture2DRect(dynamicRingsMap, (proj_tc_normals.st/proj_tc_normals.q)*dynamicRingsMapWH).xyz*2.0-1.0);
    //float tex    vec3 world_space_normal = normalize(texture2DRect(dynamicRingsMap, (proj_tc_normals.st/proj_tc_normals.q)*dynamicRingsMapWH).xyz);


    //    vec4 wdyn_tex_val = texture2DRect(dynamicRingsMap, (proj_tc_normals.st/proj_tc_normals.q)*dynamicRingsMapWH);

    //    vec4 wdyn_tex_val = texture2D(dynamicRingsMap, proj_tc_normals.st/proj_tc_normals.q);
    vec3 ptc = proj_tc_normals.stp/proj_tc_normals.q;

    vec3 cn = calc_normal(scaled_tc);

    //    vec4 noiseColor = vec4(1.0, 1.0, 1.0, 0.0);

 if( (ptc.s>0.0) && (ptc.s<1.0) &&
     (ptc.t>0.0) && (ptc.t<1.0) &&
     (ptc.p>0.0) && (ptc.p<1.0) )
   {
    vec4 wdyn_tex_val;
    wdyn_tex_val = texture2D(dynamicRingsMap, ptc.st);
    //    vec4 wdyn_tex_val = texture2DProj(dynamicRingsMap, proj_tc_normals);

    //for unsigned tex
  wdyn_tex_val.xyz-=0.5;
  wdyn_tex_val.xyz*=2.0;

  /*
  //for unsigned tex
  //
  wdyn_tex_val.a*=2.0*ripple_scale;
  //  wdyn_tex_val.a*=ripple_scale;
  wdyn_tex_val.a-=ripple_scale;

  wdyn_tex_val.a/=ripple_scale;
  */

  //  world_space_normal = normalize(mix(cn, normalize(wdyn_tex_val.xyz+cn), (wdyn_tex_val.a)*0.075));
  //  world_space_normal = normalize(mix(cn, normalize(0.5*wdyn_tex_val.xyz+0.5*cn), abs(wdyn_tex_val.a)*0.75));
  //  world_space_normal = normalize(mix(cn, normalize(0.25*wdyn_tex_val.xyz+0.75*cn), wdyn_tex_val.a*wdyn_tex_val.a));
  //  world_space_normal = normalize(mix(cn, normalize(0.35*wdyn_tex_val.xyz+0.65*cn), wdyn_tex_val.a));
  // world_space_normal = wdyn_tex_val.xyz;

  // world_space_normal = vec3(1.0, 0.0, 0.0);

  
  float blend_fact;

  //  blend_fact = cos(wdyn_tex_val.y*3.1415);
  //
  blend_fact = sin((1.0-wdyn_tex_val.y)*3.1415*0.5);
  //  blend_fact = cos((1.0-wdyn_tex_val.y)*3.1415*0.5);

  //!!  blend_fact = 1.0-wdyn_tex_val.y;
  //
  blend_fact *= blend_fact;


  //!!  blend_fact *= blend_fact*blend_fact;

  //!!  world_space_normal = normalize(mix(cn, wdyn_tex_val.xyz, blend_fact));
  

  //tangent space
  vec3 nn;
  /*
  nn.x = T.x * wdyn_tex_val.x + N.x * wdyn_tex_val.y + B.x * wdyn_tex_val.z;
  nn.y = T.y * wdyn_tex_val.x + N.y * wdyn_tex_val.y + B.y * wdyn_tex_val.z;
  nn.z = T.z * wdyn_tex_val.x + N.z * wdyn_tex_val.y + B.z * wdyn_tex_val.z;
  */

  nn.x = T.x * wdyn_tex_val.x + T.y * wdyn_tex_val.y + T.z * wdyn_tex_val.z;
  nn.y = N.x * wdyn_tex_val.x + N.y * wdyn_tex_val.y + N.z * wdyn_tex_val.z;
  nn.z = B.x * wdyn_tex_val.x + B.y * wdyn_tex_val.y + B.z * wdyn_tex_val.z;

  //  world_space_normal = normalize(nn);
  //
  world_space_normal = normalize(mix(cn, nn, blend_fact));
  //  world_space_normal = normalize(mix(cn, nn, clamp(blend_fact, 0.05, 0.95) ));

  //  world_space_normal = normalize(mix(cn, normalize(0.5*(wdyn_tex_val.xyz+cn)), blend_fact));

  ///  noiseColor.a = blend_fact*blend_fact;
    ///  noiseColor.a = 1.0 - blend_fact;
  //  noiseColor.rgb = texture2D(noiseMap,  tc/512.0).rgb +.25;

   }
 else
   {
    //    wdyn_tex_val = vec4(0.5, 0.5, 0.5, 0.5);
    //    wdyn_tex_val = vec4(0.0, 0.0, 0.0, 0.0);
     world_space_normal = cn;
   }

    //    vec3 cn = calc_normal(scaled_tc, proj_coords.xy);
 //    vec3 cn = calc_normal(scaled_tc);
    //    world_space_normal = normalize(mix(cn, normalize(wdyn_tex_val.xyz+cn), abs(wdyn_tex_val.a)*0.025));




    //    world_space_normal = normalize(mix(cn, wdyn_tex_val.xyz, (wdyn_tex_val.a)*0.05));

    //    world_space_normal = normalize(mix(cn, normalize(wdyn_tex_val.xyz+cn), (wdyn_tex_val.a)*0.075));
    //    world_space_normal = cn;

    //    world_space_normal = normalize(mix(cn, wdyn_tex_val.xyz+cn, (wdyn_tex_val.a)*0.075));


    vec3 V = -eye2objW; //already normalized in the VS ... good enough!
  



  float fres = fastFresnel(V, world_space_normal, 0.0204, 5.0);


    //fix border object-reflection
  //reflection_color = texture2D(reflectMap, proj_coords.xy-proj_coords.z*world_space_normal.xz);

  //
reflection_color = texture2D(reflectMap, proj_coords.xy-proj_coords.z*world_space_normal.xz+vec2(camUp.x*0.003, -camUp.y*0.003));


/*
 float diff, spec;
 calc_light_intensity(V.xyz, world_space_normal.xyz, diff, spec);
 reflection_color = reflection_color*diff + spec*lightColor;
*/

///!
 reflection_color = mix(calc_light_intensity(V.xyz, world_space_normal.xyz)*lightColor+reflection_color, reflection_color, reflection_color.a);
//? reflection_color = mix(calc_light_intensity(V.xyz, world_space_normal.xyz)*vec4(0.3,0.3,0.3, 1.0)+reflection_color, reflection_color, reflection_color.a);
  // reflection_color = mix(calc_light_intensity(V.xyz, world_space_normal.xyz)*reflection_color, reflection_color, reflection_color.a);

// reflection_color = mix(calc_light_intensity(V.xyz, world_space_normal.xyz)+reflection_color, reflection_color, reflection_color.a);
// reflection_color = mix(calc_light_intensity(V.xyz, world_space_normal.xyz)+reflection_color, reflection_color, min((reflection_color.a+0.5), 1.0));

  // reflection_color = mix(calc_light_intensity(V.xyz, world_space_normal.xyz)*reflection_color, reflection_color, reflection_color.a);

 //  reflection_color *= calc_light_intensity(V.xyz, world_space_normal.xyz);

  /*
  float light_intensity = calc_light_intensity(V.xyz, world_space_normal.xyz);
  vec4 lightmix = mix(reflection_color, lightColor, light_intensity);
 reflection_color = mix(lightmix, reflection_color, reflection_color.a);
  */


  refraction_color = texture2D(refractMap, proj_coords_refr.xy-proj_coords_refr.z*world_space_normal.xz);

  //refraction_color = refraction_color*diff + spec*lightColor;

  // 
 color = mix(refraction_color, reflection_color, fres);

  /**
  float lightI = calc_light_intensity(V.xyz, world_space_normal.xyz);
  //  lightI += 0.5;
  //  color = mix(refraction_color, reflection_color*lightI, fres);
  color = mix(refraction_color, reflection_color, fres)*lightI;
  **/

  //final color composite
  // 
   gl_FragColor = color;

  gl_FragColor.a = gl_TexCoord[4].q / dof_params.z;

  ///  gl_FragColor = mix(color, noiseColor, noiseColor.a);

  //  gl_FragColor = wdyn_tex_val;

  //DEBUG visualize homogeneous coord
  //  gl_FragColor = vec4( (255.0/tmp_pos.w).xxx, 1.0);
  //  gl_FragColor = vec4( (tmp_pos.w/1255.0).xxx, 1.0);


    //DEBUG  light-intensity term
    //    gl_FragColor = calc_light_intensity(V.xyz, world_space_normal.xyz)*lightColor;


    //DEBUG reflectivity term (brighter <=> more reflective)
    //    gl_FragColor = vec4(fres.xxx, 1.0);


  //DEBUG projective texturemapping - looked up normalmap
  //  gl_FragColor = texture2DRect(waternormalsMap, proj_coords.xy);
  //  gl_FragColor = texture2DRect(waternormalsMap, proj_coords.xy*wg_cnt);
  //  gl_FragColor = vec4(normalize(texture2DRect(waternormalsMap, proj_coords.xy*wg_cnt).rgb), 1.0);


    //DEBUG ringmap
    //gl_FragColor = texture2D(ringsMap, proj_coords.xy).aaaa;
    //    gl_FragColor = texture2D(ringsMap, proj_coords.xy);


    //DEBUG WS normal
    //    gl_FragColor = vec4(world_space_normal*.5+0.5, 1.0);
}
