
const int DIRECTIONAL_LIGHT = 0;
const int POINT_LIGHT = 1;

uniform mat4 PROJECTION_MATRIX;
uniform mat4 VIEW_MATRIX;
uniform mat4 WORLD_MATRIX;

uniform vec3 diffuse_light_direction;
uniform vec3 diffuse_light_colour;

uniform mediump float SHADOW_MAP_SUN_VALUE;
uniform mat4 SHADOW_MAP_SUN_PROJECTION;
uniform mat4 SHADOW_MAP_SUN_VIEW;
            
attribute vec4 vertex_position;
attribute vec4 vertex_colour;
attribute vec3 vertex_normal;

varying vec4 lit_vertex_colour;

varying vec4 sun_shadow_coord;
varying float cosTheta;

vec3 getLightColour(vec3 normal, vec3 vertex_view, vec3 light_position, vec3 light_colour, vec4 vertex_colour, int type, float falloff)
{
  vec3 vertex_light_offset;
  float attenuation = 1.0;
  if(type == 0)
  {
    vertex_light_offset = normalize(vec3(-light_position.xyz));
    attenuation = 1.0;
  }
  else if(type == 1)
  {
    vertex_light_offset = normalize(light_position.xyz - vertex_view);
    float light_distance = length(light_position.xyz - vertex_view);
    attenuation = 1.0 / (1.0 + falloff * pow(light_distance, 2.0));
  }

  float ambient_val = 0.0;
  vec3 ambient = ambient_val * vertex_colour.rgb * light_colour.rgb;

  float diffuse_val = max(0.0, dot(normal, vertex_light_offset));
  vec3 diffuse = diffuse_val * vertex_colour.rgb * light_colour.rgb;

  vec3 final_colour = ambient + attenuation * (diffuse);
  return final_colour;
}

vec4 setupShadow(float value, mat4 projection, mat4 view)
{
  vec4 coord = vec4(0.0, 0.0, 0.0, 1.0);
  if(value > 0.0)
  {
    // Calculate shadow map
//    const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
//                              0.0, 0.5, 0.0, 0.0,
//                              0.0, 0.0, 0.5, 0.0,
//                              0.5, 0.5, 0.5, 1.0);
    const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
                              0.0, 0.5, 0.0, 0.0,
                              0.0, 0.0, 1.0, 0.0,
                              0.5, 0.5, 0.0, 1.0);

    /* Calculate vertex position, which is being seen from the light. */
    coord = projection * view * WORLD_MATRIX * vec4(vertex_position.xyz, 1.0);
    /* Normalize texture coords from -1..1 to 0..1 now, before projection. */
    coord = biasMat * coord;
  }

  return coord;
}

void main()
{
  mat4 MODELVIEW = VIEW_MATRIX * WORLD_MATRIX;
  mat4 MVP = PROJECTION_MATRIX * MODELVIEW;
// This clears scaling, however is not available & needs version 150.
// However it is for removing the scaling from normal transformation. We don't use scaling (much)
// TODO: Could be precomputed.
//  mat3 NORMAL_MATRIX = transpose(inverse(mat3(WORLD_MATRIX)));
  mat3 NORMAL_MATRIX = mat3(WORLD_MATRIX);

  sun_shadow_coord = setupShadow(SHADOW_MAP_SUN_VALUE, SHADOW_MAP_SUN_PROJECTION, SHADOW_MAP_SUN_VIEW);
  vec3 nn = normalize(vertex_normal);
  vec3 ns = normalize(-diffuse_light_direction);
  cosTheta = clamp(dot(nn,ns), 0.0, 1.0);

  vec4 view_position = WORLD_MATRIX * vertex_position;

  vec3 normal = normalize(NORMAL_MATRIX * vertex_normal);
  vec3 vertex = vec3(view_position);

  vec3 light_colour = vec3(0.0);
  light_colour += getLightColour(normal, vertex, diffuse_light_direction, diffuse_light_colour, vertex_colour, DIRECTIONAL_LIGHT, 0.0);

  // Adjust vertex colour based on visibility (AFTER shading to blend right)
  lit_vertex_colour = vec4(light_colour, vertex_colour.a);

  gl_Position = MVP * vertex_position;
}