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;

    vec3 circle_plane_normal;
    vec3 circle_center;
    vec4 circle_args;
    vec4 circle_color;
    vec4 circle_args2;
};

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;

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;
}

bool is_within_cut_plane(vec3 pos, vec3 plane_center, vec3 plane_normal) {
    return dot(pos - plane_center, plane_normal) > 0;
}

float intersection_distance(vec3 eye, vec3 dir, vec3 plane_normal, vec3 plane_center) {
    float dist_orto = dot(plane_center - eye, plane_normal);
    float dist_para = dot(dir, plane_normal);
    float dist = dist_orto / dist_para;

    vec3 to_plane = dist_orto * plane_normal;
    vec3 to_plane_normal = normalize(to_plane);
    float to_plane_proj = dot(to_plane_normal, dir);
    return length(to_plane) / to_plane_proj;



    return dist_para;

    //    return eye + dist * dir;
    //    vec3 to_center = plane_center - eye;
    //    vec3 projected = dot(to_center, plane_normal) * plane_normal;
    //    vec3 plane_pos = eye + projected;
    //    return plane_pos;
    //    return dot(plane_pos, dir) * dir;
}

//float intersection_distance(vec3 eye, vec3 dir, vec4 plane) {
//    return -(dot(eye, plane.xyz) + plane.w) / dot(dir, plane.xyz);
//}

vec3 triplanar_map(vec3 pos, vec3 normal) {
    //    vec3 c_normal = normalize(normal);
    //    vec3 c_tangent = normalize(tangent);
    //    vec3 c_bitangent = normalize(bitangent);
    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);
    //    return x * c_tangent + y * c_bitangent + z * c_normal;
    return texture(cut_map, pos.xy).rgb;
}


vec3 light_pixel(vec3 pos, vec3 normal, float ambient_occlusion, float ambient, vec3 base_color, float light2) {
    float light = calculate_light(pos);
    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.0) * light;

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

    return final_color;
}

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 main() {
    if (!is_within_cut_plane(v_world_pos, circle_center, circle_plane_normal)) {
        discard;
    }

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

    float light = calculate_light(v_model_pos);
    float ao_map_sample = texture(ambient_map, v_uv).r;
    light *= ao_map_sample*ao_map_sample;

    // 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 environment map
    vec3 eye_dir = normalize(v_world_eye);
    vec3 reflect_dir = reflect(-eye_dir, world_normal);
    vec2 reflect_uv = reflect_dir.xy * 0.5 + 0.5;
    vec3 env_color = texture(env_map, reflect_uv).rgb;

    // Calculate traced circle
    float trace_dist;
    vec4 trace_result = traced_color(v_world_pos, reflect_dir, circle_plane_normal,
    circle_center, circle_args, circle_color, 10000, trace_dist);
    float trace_intensity = clamp(1.0 + trace_dist * circle_args2.x, 0, 1);
    trace_result.a *= trace_intensity;

    // Calculate lighting
    vec3 light_dir = normalize(v_light_dir);
    float intensity = ambient + max(dot(normal, light_dir), 0) * light * (1-ambient);
    float specular = max(pow(dot(normal, light_dir), 5.0), 0.0) * light;

    // Calculate final color
    vec3 base_color = texture(tex, v_uv * (1 + tex_scale)).rgb * color.rgb;
    //    f_color = texture(tex, v_uv) * intensity * color + vec4(1.0, 1.0, 1.0, 0.0) * specular * 0.3;
    vec3 final_color = base_color * intensity * color.rgb + specular * env_color;

    //    final_color = final_color + trace_result.rgb * trace_result.a * 0.3;

    //    f_color = vec4(final_color, 1.0);
    vec3 color = light_pixel(v_world_pos, world_normal, ao_map_sample, ambient, base_color, light);
    f_color = vec4(color, 1.0);
}
