# Material operations module
# Handles: create_material_op, get_material_properties_op
# Corresponds to: test_input_tilemap_material.py (Material section)
class_name MaterialOps


## Create a material resource and optionally assign it to a node
static func create_material_op(ctx: SceneTree, params: Dictionary) -> void:
	print("Creating material")
	
	# Get the save path for the material
	var save_path: String = params.save_path
	if not save_path.begins_with("res://"):
		save_path = "res://" + save_path
	
	if ctx.debug_mode:
		print("Material save path: " + save_path)
	
	# Create the material
	var material: StandardMaterial3D = StandardMaterial3D.new()
	
	# Set material properties from params
	if params.has("albedo_color"):
		var color: Color = _parse_color(ctx, params.albedo_color)
		if color:
			material.albedo_color = color
			if ctx.debug_mode:
				print("Set albedo_color: " + str(color))
	
	if params.has("roughness"):
		material.roughness = float(params.roughness)
		if ctx.debug_mode:
			print("Set roughness: " + str(params.roughness))
	
	if params.has("metallic"):
		material.metallic = float(params.metallic)
		if ctx.debug_mode:
			print("Set metallic: " + str(params.metallic))
	
	if params.has("emission_enabled"):
		material.emission_enabled = bool(params.emission_enabled)
		if ctx.debug_mode:
			print("Set emission_enabled: " + str(params.emission_enabled))
	
	if params.has("emission_color") and material.emission_enabled:
		var emission_color: Color = _parse_color(ctx, params.emission_color)
		if emission_color:
			material.emission = emission_color
			if ctx.debug_mode:
				print("Set emission: " + str(emission_color))
	
	if params.has("emission_energy"):
		material.emission_energy_multiplier = float(params.emission_energy)
		if ctx.debug_mode:
			print("Set emission_energy_multiplier: " + str(params.emission_energy))
	
	if params.has("transparency"):
		var transparency_mode: String = params.transparency
		match transparency_mode:
			"disabled":
				material.transparency = BaseMaterial3D.TRANSPARENCY_DISABLED
			"alpha":
				material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
			"alpha_scissor":
				material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR
			"alpha_hash":
				material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_HASH
			"alpha_depth_pre_pass":
				material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA_DEPTH_PRE_PASS
		if ctx.debug_mode:
			print("Set transparency: " + str(transparency_mode))
	
	if params.has("cull_mode"):
		var cull_mode: String = params.cull_mode
		match cull_mode:
			"back":
				material.cull_mode = BaseMaterial3D.CULL_BACK
			"front":
				material.cull_mode = BaseMaterial3D.CULL_FRONT
			"disabled":
				material.cull_mode = BaseMaterial3D.CULL_DISABLED
		if ctx.debug_mode:
			print("Set cull_mode: " + str(cull_mode))
	
	# Ensure directory exists
	var save_dir: String = save_path.get_base_dir()
	if save_dir != "res://" and not save_dir.is_empty():
		var dir: DirAccess = DirAccess.open("res://")
		if dir:
			var relative_dir: String = save_dir
			if relative_dir.begins_with("res://"):
				relative_dir = relative_dir.substr(6)
			if not relative_dir.is_empty() and not dir.dir_exists(relative_dir):
				var make_dir_error: int = dir.make_dir_recursive(relative_dir)
				if ctx.debug_mode:
					print("Created directory: " + relative_dir + ", result: " + str(make_dir_error))
	
	# Save the material
	var error: int = ResourceSaver.save(material, save_path)
	if ctx.debug_mode:
		print("Material save result: " + str(error) + " (OK=" + str(OK) + ")")
	
	if error != OK:
		printerr("Failed to save material: " + str(error))
		ctx.quit(1)
	
	# Optionally assign to a node in a scene
	if params.has("scene_path") and params.has("target_node_path"):
		var scene_path: String = params.scene_path
		if not scene_path.begins_with("res://"):
			scene_path = "res://" + scene_path
		
		if ctx.debug_mode:
			print("Assigning material to node in scene: " + scene_path)
		
		# Check if scene exists
		if not FileAccess.file_exists(scene_path):
			printerr("Scene file does not exist: " + scene_path)
			ctx.quit(1)
		
		# Load the scene
		var scene: PackedScene = load(scene_path)
		if not scene:
			printerr("Failed to load scene: " + scene_path)
			ctx.quit(1)
		
		var scene_root: Node = scene.instantiate()
		
		# Find the target node
		var target_path: String = params.target_node_path
		var target_node: Node = scene_root
		if target_path != "root" and target_path != "":
			var relative_path: String = target_path
			if relative_path.begins_with("root/"):
				relative_path = relative_path.substr(5)
			target_node = scene_root.get_node(relative_path)
		
		if not target_node:
			printerr("Target node not found: " + target_path)
			ctx.quit(1)
		
		if ctx.debug_mode:
			print("Target node found: " + target_node.name + " of type: " + target_node.get_class())
		
		# Load the saved material and assign it
		var loaded_material: StandardMaterial3D = load(save_path)
		if not loaded_material:
			printerr("Failed to load saved material: " + save_path)
			ctx.quit(1)
		
		# Try to assign material based on node type
		if target_node is MeshInstance3D:
			target_node.material_override = loaded_material
			if ctx.debug_mode:
				print("Assigned material_override to MeshInstance3D")
		elif target_node is CSGPrimitive3D:
			target_node.material = loaded_material
			if ctx.debug_mode:
				print("Assigned material to CSGPrimitive3D")
		elif target_node is GeometryInstance3D:
			target_node.material_override = loaded_material
			if ctx.debug_mode:
				print("Assigned material_override to GeometryInstance3D")
		else:
			printerr("Node type does not support material assignment: " + target_node.get_class())
			ctx.quit(1)
		
		# Save the scene
		var packed_scene: PackedScene = PackedScene.new()
		var pack_result: int = packed_scene.pack(scene_root)
		if pack_result == OK:
			var save_scene_error: int = ResourceSaver.save(packed_scene, scene_path)
			if save_scene_error == OK:
				print("Material assigned to node '" + target_path + "' and scene saved")
			else:
				printerr("Failed to save scene: " + str(save_scene_error))
				ctx.quit(1)
		else:
			printerr("Failed to pack scene: " + str(pack_result))
			ctx.quit(1)
	else:
		print("Material created successfully at: " + save_path)


## Get material properties from a .tres or .res file (read operation)
static func get_material_properties_op(ctx: SceneTree, params: Dictionary) -> void:
	print("Getting material properties")
	
	# Get the material path
	var material_path: String = params.material_path
	if not material_path.begins_with("res://"):
		material_path = "res://" + material_path
	
	if ctx.debug_mode:
		print("Material path: " + material_path)
	
	# Check if the material file exists
	if not FileAccess.file_exists(material_path):
		printerr("Material file does not exist at: " + material_path)
		ctx.quit(1)
	
	# Load the material
	var material: Material = load(material_path)
	if not material:
		printerr("Failed to load material: " + material_path)
		ctx.quit(1)
	
	if ctx.debug_mode:
		print("Material loaded: " + material.get_class())
	
	# Build result based on material type
	var result: Dictionary = {
		"path": material_path,
		"type": material.get_class(),
		"properties": {}
	}
	
	if material is StandardMaterial3D or material is ORMMaterial3D:
		var props: Dictionary = result["properties"]
		
		# Albedo
		props["albedo_color"] = _color_to_dict(material.albedo_color)
		props["albedo_color_hex"] = "#" + material.albedo_color.to_html(true)
		if material.albedo_texture:
			props["albedo_texture"] = material.albedo_texture.resource_path
		
		# Metallic
		props["metallic"] = material.metallic
		props["metallic_specular"] = material.metallic_specular
		if material.metallic_texture:
			props["metallic_texture"] = material.metallic_texture.resource_path
		
		# Roughness
		props["roughness"] = material.roughness
		if material.roughness_texture:
			props["roughness_texture"] = material.roughness_texture.resource_path
		
		# Emission
		props["emission_enabled"] = material.emission_enabled
		if material.emission_enabled:
			props["emission"] = _color_to_dict(material.emission)
			props["emission_hex"] = "#" + material.emission.to_html(true)
			props["emission_energy_multiplier"] = material.emission_energy_multiplier
		
		# Transparency
		var transparency_names: Dictionary = {
			BaseMaterial3D.TRANSPARENCY_DISABLED: "disabled",
			BaseMaterial3D.TRANSPARENCY_ALPHA: "alpha",
			BaseMaterial3D.TRANSPARENCY_ALPHA_SCISSOR: "alpha_scissor",
			BaseMaterial3D.TRANSPARENCY_ALPHA_HASH: "alpha_hash",
			BaseMaterial3D.TRANSPARENCY_ALPHA_DEPTH_PRE_PASS: "alpha_depth_pre_pass",
		}
		props["transparency"] = transparency_names.get(material.transparency, "unknown")
		
		# Cull mode
		var cull_names: Dictionary = {
			BaseMaterial3D.CULL_BACK: "back",
			BaseMaterial3D.CULL_FRONT: "front",
			BaseMaterial3D.CULL_DISABLED: "disabled",
		}
		props["cull_mode"] = cull_names.get(material.cull_mode, "unknown")
		
		# Normal map
		props["normal_enabled"] = material.normal_enabled
		if material.normal_enabled and material.normal_texture:
			props["normal_texture"] = material.normal_texture.resource_path
			props["normal_scale"] = material.normal_scale
		
		# Shading mode
		var shading_names: Dictionary = {
			BaseMaterial3D.SHADING_MODE_UNSHADED: "unshaded",
			BaseMaterial3D.SHADING_MODE_PER_PIXEL: "per_pixel",
			BaseMaterial3D.SHADING_MODE_PER_VERTEX: "per_vertex",
		}
		props["shading_mode"] = shading_names.get(material.shading_mode, "unknown")
		
	elif material is ShaderMaterial:
		result["properties"]["shader"] = material.shader.resource_path if material.shader else null
		# Get shader parameters
		var shader_params: Dictionary = {}
		if material.shader:
			for param in material.shader.get_shader_uniform_list():
				var param_name: String = param["name"]
				var param_value: Variant = material.get_shader_parameter(param_name)
				shader_params[param_name] = str(param_value)
		result["properties"]["shader_parameters"] = shader_params
	
	else:
		result["properties"]["note"] = "Unknown material type, limited property access"
	
	print(JSON.stringify(result))


## Helper function to convert Color to dictionary
static func _color_to_dict(color: Color) -> Dictionary:
	return {
		"r": color.r,
		"g": color.g,
		"b": color.b,
		"a": color.a
	}


## Helper function to parse color from hex string or color name
static func _parse_color(ctx: SceneTree, color_input: Variant) -> Color:
	if color_input is String:
		var color_string: String = color_input.strip_edges()
		
		# Handle hex colors
		if color_string.begins_with("#"):
			return Color.html(color_string)
		
		# Handle named colors
		var color_names: Dictionary = {
			"red": Color.RED,
			"green": Color.GREEN,
			"blue": Color.BLUE,
			"white": Color.WHITE,
			"black": Color.BLACK,
			"yellow": Color.YELLOW,
			"cyan": Color.CYAN,
			"magenta": Color.MAGENTA,
			"orange": Color.ORANGE,
			"purple": Color.PURPLE,
			"pink": Color.PINK,
			"brown": Color.BROWN,
			"gray": Color.GRAY,
			"grey": Color.GRAY,
		}
		
		var lower_name: String = color_string.to_lower()
		if color_names.has(lower_name):
			return color_names[lower_name]
		
		# Try parsing as Color constructor string (r,g,b or r,g,b,a)
		if "," in color_string:
			var parts: PackedStringArray = color_string.split(",")
			if parts.size() >= 3:
				var r: float = float(parts[0].strip_edges())
				var g: float = float(parts[1].strip_edges())
				var b: float = float(parts[2].strip_edges())
				var a: float = float(parts[3].strip_edges()) if parts.size() >= 4 else 1.0
				return Color(r, g, b, a)
		
		ctx.log_error("Could not parse color: " + color_string)
		return Color.WHITE
	elif color_input is Dictionary:
		# Handle dict format like {"r": 1.0, "g": 0.0, "b": 0.0}
		var r: float = float(color_input.get("r", 1.0))
		var g: float = float(color_input.get("g", 1.0))
		var b: float = float(color_input.get("b", 1.0))
		var a: float = float(color_input.get("a", 1.0))
		return Color(r, g, b, a)
	elif color_input is Array and color_input.size() >= 3:
		# Handle array format like [1.0, 0.0, 0.0]
		var r: float = float(color_input[0])
		var g: float = float(color_input[1])
		var b: float = float(color_input[2])
		var a: float = float(color_input[3]) if color_input.size() >= 4 else 1.0
		return Color(r, g, b, a)
	
	return Color.WHITE
