@tool
extends ShaderMaterial
class_name VFEZMaterial2D

enum BlendModeEnum
{
	Mix = 0,
	Add = 1,
	Subtract = 2,
	Multiply = 3,
	Premultiplied_Alpha= 4
}

enum LightModeEnum
{
	Normal = 0,
	Unshaded = 1,
	LightOnly = 2
}

@export_group("Render Options")
@export var BlendMode: BlendModeEnum:
	get:
		return _blendMode
	set(value):
		_blendMode = value
		_update_shader_code()

@export var LightMode: LightModeEnum:
	get:
		return _lightMode
	set(value):
		_lightMode = value
		_update_shader_code()
		
var _blendMode: BlendModeEnum = BlendModeEnum.Mix
var _lightMode: LightModeEnum = LightModeEnum.Normal

# handle property gets. 
# if is shader property set it in shader 
# and if it starts with use_ update shader code to include new definitions
# if is render property set new value and update shader code
func _set(property, value):
	if property.begins_with("shader_parameter/"):
		set_shader_parameter(property.replace("shader_parameter/", ""), value)
		if property.begins_with("shader_parameter/use_"):
			_update_shader_code()

func generate_render_options_definition_string() -> String:
	var definition_string: String = ""
	
	match _blendMode:
		BlendModeEnum.Mix:
			definition_string += "#define BLEND_MIX\n"
		BlendModeEnum.Add:
			definition_string += "#define BLEND_ADD\n"
		BlendModeEnum.Subtract:
			definition_string += "#define BLEND_SUB\n"
		BlendModeEnum.Multiply:
			definition_string += "#define BLEND_MUL\n"
		BlendModeEnum.Premultiplied_Alpha:
			definition_string += "#define BLEND_PREMUL_ALPHA\n"
	
	match _lightMode:
		LightModeEnum.Unshaded:
			definition_string  += "#define UNSHADED\n"
		LightModeEnum.LightOnly:
			definition_string  += "#define LIGHT_ONLY\n"
			
	return definition_string
	
# update shader code to include new option definitions
func _update_shader_code():
	if not Engine.is_editor_hint():
		return
	
	var template_header = """
// This shader was dynamically generated by the VFEZ material.
// **********************************
// Every change to the VFEZ material Render Options or 
// Include Options generates a new shader. After every change
// you can click on the new exported shader in the editor to view
// the latest changes. Only the definitions (#define) actually change.
// **********************************
"""
	# find current directory name and create absolute include path for template shader
	var base_dir_name = get_script().get_path().get_base_dir()
	var shader_include_str: String = "#include \"" + base_dir_name + "/Shaders/vfez_2d_template.gdshaderinc\"\n"
	var template_code: String = "shader_type canvas_item;\n"
	
	# we duplicate the shader, else the code bugs 
	# if the generated shader is open in the editor
	shader = shader.duplicate()
	# initialize the shader code with the included shader template. 
	# This is necessary to be able to read the shader uniforms later.
	shader.code = template_code + shader_include_str

	template_code += generate_render_options_definition_string()

	# set all shader definition options based on the relevant use_X uniform values
	for uniform in shader.get_shader_uniform_list():
		var uniform_name: String = uniform["name"]
		
		# if start with use_ there is a relevant define options
		if uniform_name.begins_with("use_"):
			var shader_parameter = get_shader_parameter(uniform_name)
			# if use_ shader parameter is true (1) then set the define option
			# to enable the effect	
			if shader_parameter != null && int(shader_parameter) == 1:
				# extract define option from shader_parameter name
				# for example use_uv_wave becomes -> UV_WAVE
				var define_option = uniform_name.replace("use_", "").to_upper()
				template_code += "#define %s\n" % define_option
	
	# update final code to include description template, the define options 
	# and the shader include string at the end
	shader.code = template_header + template_code + shader_include_str

func _init() -> void:	
	if Engine.is_editor_hint():
		shader = Shader.new()
		shader.resource_name = "VFEZ2DPreview"
		_update_shader_code()
