@tool
class_name GBShader
extends ColorRect

@export var game_boy_palette: GBPalette:
	set(value):
		game_boy_palette = value
		_update_palette()

var shader_code: String = """
shader_type canvas_item;

uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear;

uniform vec4 color_black: source_color = vec4(0.251, 0.314, 0.063, 1.0);
uniform vec4 color_dark: source_color = vec4(0.439, 0.502, 0.157, 1.0);
uniform vec4 color_light: source_color = vec4(0.627, 0.659, 0.251, 1.0);
uniform vec4 color_white: source_color = vec4(0.816, 0.816, 0.345, 1.0);

uniform float threshold_black: hint_range(0.0, 1.0, 0.01) = 0.25;
uniform float threshold_dark: hint_range(0.0, 1.0, 0.01) = 0.5;
uniform float threshold_light: hint_range(0.0, 1.0, 0.01) = 0.75;

uniform int fade: hint_range(-3, 3, 1) = 0;
uniform bool inversion = false;


void fragment() {
	vec4 current_color = texture(SCREEN_TEXTURE, SCREEN_UV);
	float rgb_avg = (current_color.r + current_color.g + current_color.b) / 3.0;

	int base_index;
	if (rgb_avg <= threshold_black) {
		base_index = 0;
	} else if (rgb_avg <= threshold_dark) {
		base_index = 1;
	} else if (rgb_avg <= threshold_light) {
		base_index = 2;
	} else {
		base_index = 3;
	}

	int color_index = clamp(base_index + fade, 0, 3);
	color_index = inversion ? 3 - color_index : color_index;

	vec4 palette[] = {color_black, color_dark, color_light, color_white};

	COLOR = palette[color_index];
}
"""
var game_boy_shader: ShaderMaterial = null


func _init() -> void:
	var shader: Shader = Shader.new()
	shader.code = shader_code
	var shader_material: ShaderMaterial = ShaderMaterial.new()
	shader_material.shader = shader
	material = shader_material
	game_boy_shader = material


func _ready() -> void:
	if not Engine.is_editor_hint():
		game_boy_palette = Settings.get_current_palette()


func _update_palette() -> void:
	if not game_boy_palette or not game_boy_shader:
		return

	game_boy_shader.set_shader_parameter("color_black", game_boy_palette.black)
	game_boy_shader.set_shader_parameter("color_dark", game_boy_palette.dark)
	game_boy_shader.set_shader_parameter("color_light", game_boy_palette.light)
	game_boy_shader.set_shader_parameter("color_white", game_boy_palette.white)


func fade_from_black(duration: float = 1.0) -> void:
	set_fade(-3)
	for i in range(-3, 0):
		await get_tree().create_timer(duration / 4).timeout
		set_fade(i + 1)


func fade_from_white(duration: float = 1.0) -> void:
	set_fade(3)
	for i in range(3, 0, -1):
		await get_tree().create_timer(duration / 4).timeout
		set_fade(i - 1)


func fade_to_black(duration: float = 1.0) -> void:
	reset_fade()
	for i in range(0, -3, -1):
		await get_tree().create_timer(duration / 4).timeout
		set_fade(i - 1)


func fade_to_white(duration: float = 1.0) -> void:
	reset_fade()
	for i in range(0, 3):
		await get_tree().create_timer(duration / 4).timeout
		set_fade(i + 1)


func invert() -> void:
	var inversion: bool = game_boy_shader.get_shader_parameter("inversion")
	game_boy_shader.set_shader_parameter("inversion", not inversion)


func reset_fade() -> void:
	set_fade(0)


func set_fade(fade: int) -> void:
	fade = clampi(fade, -3, 3)
	game_boy_shader.set_shader_parameter("fade", fade)


func set_inversion(is_inverted: bool) -> void:
	game_boy_shader.set_shader_parameter("inversion", is_inverted)
