layout (location = 0) in vec2 v_uv;
layout (location = 1) in vec3 v_normal;
layout (location = 2) in vec3 v_model_pos;
layout (location = 3) in vec3 v_tangent;
layout (location = 4) in vec3 v_light_dir;
layout (location = 5) in vec3 v_world_normal;
layout (location = 6) in vec3 v_world_pos;
layout (location = 7) in vec3 v_world_eye;
layout (location = 8) in vec3 v_world_tangent;
layout (location = 9) in vec3 v_camera_pos;

layout (location = 0) out vec4 f_color;

layout (set = 1, binding = 0) uniform Uniforms {
    mat4 g_light_projection_from_world;
    mat4 g_camera_from_world;
    mat4 g_projection_from_camera;
    float g_chart_time;
    float g_app_time;
    vec3 g_light_dir;

    vec4 color;
    float bias;
    float ambient;
    float normal_factor;

    vec3 specular_color;
    vec4 foo;
    float cut_scale;
    float tex_scale;
    vec3 cut_color;

    float obj_sphere_radius;
    vec3 obj_center;
    vec3 obj_rotate;
    float obj_transition;
};

layout (set = 1, binding = 1) uniform sampler2D tex;
layout (set = 1, binding = 2) uniform sampler2D shadow;
layout (set = 1, binding = 3) uniform sampler2D ambient_map;
layout (set = 1, binding = 4) uniform sampler2D normal_map;
layout (set = 1, binding = 5) uniform sampler2D env_map;
layout (set = 1, binding = 6) uniform sampler2D cut_map;

const float DISTANCE_MAX = 100;
const float SURFACE_PROXIMITY = 0.0001;

float calculate_light(vec3 world_pos) {
    vec3 lightspace_pos = (g_light_projection_from_world * vec4(world_pos, 1.0)).xyz;
    lightspace_pos.xy = lightspace_pos.xy * 0.5 + 0.5;
    float shadow_z = texture(shadow, lightspace_pos.xy).r;
    return (shadow_z + bias*0.01 > lightspace_pos.z) ? 1.0 : 0.0;
}

vec3 calculate_normal(vec3 normal, vec3 tangent, vec3 normal_map_sample, float intensity) {
    vec3 c_normal = normalize(normal);
    vec3 c_tangent = normalize(tangent);
    vec3 c_bitangent = cross(c_normal, c_tangent);
    vec3 mapped_normal = mat3(c_tangent, c_bitangent, c_normal) * normal_map_sample;
    vec3 scaled_normal = c_normal + intensity * (mapped_normal - c_normal);
    return scaled_normal;
}

vec3 triplanar_map(vec3 pos, vec3 normal) {
    float xr = abs(normal.x);
    float yr =  abs(normal.y);
    float zr =  abs(normal.z);
    vec3 x = texture(cut_map, pos.yz).rgb;
    vec3 y = texture(cut_map, pos.zx).rgb;
    vec3 z = texture(cut_map, pos.xy).rgb;
    return 1-max(max(xr * x, yr * y), zr * z);
}


vec3 light_pixel(vec3 pos, vec3 normal, float ambient_occlusion, float ambient, vec3 base_color, float light_min) {
    float light = calculate_light(pos);
    light = max(light, light_min);
    light *= ambient_occlusion * ambient_occlusion;

    // Calculate environment map
    vec3 eye_dir = normalize(pos - v_world_eye);
    vec3 reflect_dir = reflect(eye_dir, normal);
    vec2 reflect_uv = reflect_dir.xy * 0.5 + 0.5;
    vec3 env_color = texture(env_map, reflect_uv).rgb;

    // Light components
    vec3 light_dir = normalize(g_light_dir);
    float diffuse = max(dot(normal, light_dir), 0) * light;
    float specular = max(pow(dot(normal, light_dir), 5.0), 0.00) * (light + 0.1);

    vec3 final_color = base_color * (ambient + (1-ambient) * diffuse) + specular * env_color * specular_color;

    return final_color;
}



// https://iquilezles.org/articles/normalsSDF
vec3 calc_sdf_normal(vec3 pos_cs) {
    vec2 e = vec2(1.0, -1.0)*0.5773;
    const float eps = 0.0005;
    return normalize(e.xyy*sd(pos_cs + e.xyy*eps) +
    e.yyx*sd(pos_cs + e.yyx*eps) +
    e.yxy*sd(pos_cs + e.yxy*eps) +
    e.xxx*sd(pos_cs + e.xxx*eps));
}

void main_backface() {
    vec3 dir_cs_norm = normalize(v_camera_pos);

    float dist = DISTANCE_MAX;

    // Find backface
    for (int i=0; i<256; i++) {
        vec3 pos_cs = dir_cs_norm * dist;
        float sdval = sd(pos_cs);
        if (sdval < SURFACE_PROXIMITY) break;
        dist -= sdval;
        if (dist < 0) discard;
    }

    // Hit point
    vec3 hit_pos_cs = dir_cs_norm * dist;
    vec3 sdf_normal_csn = -calc_sdf_normal(hit_pos_cs);
    vec3 hit_pos_ws = (inverse(g_camera_from_world) * vec4(hit_pos_cs, 1.0)).xyz;

    // Shade point
    vec3 color = light_pixel(hit_pos_ws, sdf_normal_csn, 1.0, ambient, cut_color, 0.0);
    f_color = vec4(color, 1.0);
}

//void main_backface() {
//    vec3 circle_normal = normalize(circle_plane_normal);
//
//    vec3 camera_plane_center = (g_camera_from_world * vec4(circle_center, 1)).xyz;
//    vec3 camera_plane_normal = (g_camera_from_world * vec4(circle_normal, 0)).xyz;
//    vec3 dir = normalize(v_camera_pos);
//    float dist = intersection_distance(vec3(0), dir, normalize(camera_plane_normal), camera_plane_center);
//
//    vec3 camera_hit_pos = dir * dist;
//    vec3 world_hit_pos = (inverse(g_camera_from_world) * vec4(camera_hit_pos, 1.0)).xyz;
//    vec3 cut_map_sample = triplanar_map(world_hit_pos * cut_scale + 0.5, circle_normal);
//
//    vec3 hcolor = light_pixel(world_hit_pos, -circle_normal, 1.0, ambient, cut_map_sample, 1.0);
//
//    f_color = vec4(hcolor * cut_color, 1);
//}

void setup_sdf() {
    mat4 world_from_sdf_model = translate(-obj_center) * rotate(obj_rotate);
    mat4 camera_from_sdf_model = g_camera_from_world * world_from_sdf_model;
    sdf_model_from_camera = inverse(camera_from_sdf_model);
    l_obj_sphere_radius = obj_sphere_radius;
    l_obj_transition = obj_transition;
    if (sd(v_camera_pos) < 0) discard;
}

void main() {
    setup_sdf();

    if (!gl_FrontFacing) {
        main_backface();
        return;
    }

    float ao_map_sample = texture(ambient_map, v_uv).r;

    vec3 base_color = texture(tex, v_uv * (1 + tex_scale)).rgb * color.rgb;

    // Calculate normal
    vec3 normal_map_sample = texture(normal_map, v_uv).xyz * 2 - 1;
    vec3 normal = calculate_normal(v_normal, v_tangent, normal_map_sample, normal_factor);
    vec3 world_normal = calculate_normal(v_world_normal, v_world_tangent, normal_map_sample, normal_factor);

    // Calculate final color
    vec3 color = light_pixel(v_world_pos, world_normal, ao_map_sample, ambient, base_color, 0.0);
    f_color = vec4(color, 1.0);
}
