-- Debug tool for visualizing hitboxes on entities/players
-- Admin tool - no recipe, use /give command

local function print_hits(hits)
	if hits and #hits > 0 then
		print(string.format("  Total hits: %d", #hits))
		print("----------------------------------------")
		
		for i, hit in ipairs(hits) do
			print(string.format("  Hit #%d: '%s'", i, hit.name))
			print(string.format("    Distance: %.2f", hit.distance))
			print(string.format("    Priority: %d", hit.orig.priority or 0))
			print(string.format("    Hit axis: %s", hit.hit_axis or "unknown"))
			print(string.format("    Hit position (normalized): x=%.3f, y=%.3f, z=%.3f", 
				hit.hit_relative.x, hit.hit_relative.y, hit.hit_relative.z))
		end
	else
		print("  No hits detected")
	end
end

-- Compare two hit results for differences
local function compare_hits(hits1, hits2, label1, label2)
	if not hits1 and not hits2 then
		print(string.format("  ✓ Both approaches returned nil (no hits)"))
		return true
	end
	
	if not hits1 or not hits2 then
		print(string.format("  ✗ DIFFERENCE: One approach returned nil, the other didn't"))
		print(string.format("    %s: %s", label1, hits1 and "has hits" or "nil"))
		print(string.format("    %s: %s", label2, hits2 and "has hits" or "nil"))
		return false
	end
	
	if #hits1 ~= #hits2 then
		print(string.format("  ✗ DIFFERENCE: Different number of hits"))
		print(string.format("    %s: %d hits", label1, #hits1))
		print(string.format("    %s: %d hits", label2, #hits2))
		return false
	end
	
	local all_match = true
	for i = 1, #hits1 do
		local h1, h2 = hits1[i], hits2[i]
		
		if h1.name ~= h2.name then
			print(string.format("  ✗ Hit #%d: Different names ('%s' vs '%s')", i, h1.name, h2.name))
			all_match = false
		end
		
		if math.abs(h1.distance - h2.distance) > 0.001 then
			print(string.format("  ✗ Hit #%d (%s): Different distances (%.4f vs %.4f)", 
				i, h1.name, h1.distance, h2.distance))
			all_match = false
		end
		
		if h1.hit_axis ~= h2.hit_axis then
			print(string.format("  ✗ Hit #%d (%s): Different hit axes ('%s' vs '%s')", 
				i, h1.name, h1.hit_axis or "nil", h2.hit_axis or "nil"))
			all_match = false
		end
	end
	
	if all_match then
		print(string.format("  ✓ Both approaches produced identical results (%d hit%s)", 
			#hits1, #hits1 == 1 and "" or "s"))
	end
	
	return all_match
end

core.register_tool("hitboxes_lib:hitbox_visualizer_tool", {
	description = "Hitbox Visualizer Tool\n" ..
	              "Left-click: Show hitboxes visually (attached, 30s)\n" ..
	              "Right-click: Test all hit detection modes and print results\n" ..
	              "  - Raycast (direct + detect_hits)\n" ..
	              "  - Raycast with ref_pos variants (attacker vs target)\n" ..
	              "  - Box and sphere detection\n" ..
	              "  Compares direct vs top-level API approach\n" ..
	              "Shift+Left-click: Slow down target",
	inventory_image = "hitboxes_lib_hitbox_tool.png",
	range = 10.0,
	
	on_secondary_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type ~= "object" then
			return
		end
		
		local target = pointed_thing.ref
		if not target or not target:is_player() and not target:get_luaentity() then
			return
		end
		
		local player_name = user:get_player_name()
		
		-- Determine hitbox group name
		local hitgroup_name = hitboxes_lib.get_hitgroup_name(target)
		if not hitgroup_name then
			core.chat_send_player(player_name, "[Hitbox Visualizer] Invalid entity for hitbox detection")
			print("[Hitbox Visualizer] Invalid entity for hitbox detection")
			return
		end
		
		-- Check if hitboxes are registered
		if not hitboxes_lib.hitbox_groups[hitgroup_name] then
			core.chat_send_player(player_name, string.format("[Hitbox Visualizer] No hitboxes found for '%s'", hitgroup_name))
			print(string.format("[Hitbox Visualizer] No hitboxes registered for group '%s'", hitgroup_name))
			return
		end
		
		-- Calculate positions and prepare hit_data structure
		local entity_pos = target:get_pos()
		local puncher_pos = user:get_pos()
		local eye_height = user:get_properties().eye_height
		local ref_pos = puncher_pos
		local hit_from_pos = vector.add(puncher_pos, vector.new(0, eye_height, 0))
		--local ref_pos = entity_pos
		--local hit_from_pos = vector.subtract(vector.add(puncher_pos, vector.new(0, eye_height, 0)), ref_pos)
		local hit_from_dir = user:get_look_dir()
		
		-- Get target rotation
		local target_rot
		if target:is_player() then
			-- For players, use yaw only
			local look = target:get_look_horizontal()
			target_rot = {x=0, y=look, z=0}
		else
			target_rot = target:get_rotation()
		end
		
		-- Calculate relative positions (all positions relative to ref_pos)
		local entity_relpos = vector.subtract(entity_pos, ref_pos)
		local hit_from_relpos = vector.subtract(hit_from_pos, ref_pos)
		
		-- Get transformed hitboxes at relative position
		local hitboxes = hitboxes_lib.get_transformed_boxes(hitgroup_name, entity_relpos, target_rot)
		
		-- Prepare hit_data with new structure (using relative positions)
		local hit_data = {
			ref_pos = ref_pos,
			hit_from_relpos = hit_from_relpos,  -- Now using relative position
			hit_from_dir = hit_from_dir,
		}
		
		-- Perform raycast (direct approach with relative positions)
		hit_data.range = 10
		local hits = hitboxes_lib.raycast_hit(hitboxes, hit_data, 10)
		--print(dump(hit_data))
		
		-- Perform raycast using detect_hits (top-level approach for comparison)
		local hits_detect = hitboxes_lib.detect_hits({
			ref_pos = ref_pos,
			hit_from_relpos = hit_from_relpos,  -- Using relative position
			hit_from_dir = hit_from_dir,
			hitgroup_name = hitgroup_name,
			hitbox_relpos = entity_relpos,      -- Using relative position
			hitbox_rot = target_rot,
			mode = "raycast",
			range = 10
		})
		
		-- Variant: raycast with ref_pos at target (entity) position
		-- This shows how different ref_pos choice affects the calculation
		local ref_pos_target = entity_pos
		local entity_relpos_target = vector.new(0, 0, 0)  -- Entity is at ref_pos, so relative position is (0,0,0)
		local hit_from_relpos_target = vector.subtract(hit_from_pos, ref_pos_target)
		
		-- Get transformed hitboxes for target-based ref_pos
		local hitboxes_target = hitboxes_lib.get_transformed_boxes(hitgroup_name, entity_relpos_target, target_rot)
		
		-- Perform raycast with target as ref_pos
		local hit_data_target = {
			ref_pos = ref_pos_target,
			hit_from_relpos = hit_from_relpos_target,
			hit_from_dir = hit_from_dir,
			range = 10,
		}
		local hits_target_ref = hitboxes_lib.raycast_hit(hitboxes_target, hit_data_target, 10)
		
		-- Prepare hit_data for box test (box in local coordinates)
		local _box_hit_local = hitboxes_lib.collisionbox_to_box({-0.1, -0.1, -0.1, 0.1, 0.1, 0.1})
		local hit_data_box = {
			ref_pos = ref_pos,
			hit_from_relpos = hit_from_relpos,  -- Using relative position
			hit_from_dir = hit_from_dir,
			box = _box_hit_local,
			box_rot = vector.new(0, user:get_look_horizontal(), 0),
			range = 10,
		}
		local box_hits = hitboxes_lib.box_hit(hitboxes, hit_data_box)
		--print(dump(hit_data_box))

		-- Perform sphere hit test (radius 0.1)
		local hit_data_sphere = {
			ref_pos = ref_pos,
			hit_from_relpos = hit_from_relpos,  -- Using relative position
			hit_from_dir = hit_from_dir,
			sphere_radius = 0.1,
			range = 10,
		}

		local sphere_hits = hitboxes_lib.sphere_hit(hitboxes, hit_data_sphere)
		--print(dump(hit_data_sphere))

		-- Print hit information to terminal
		print("========================================")
		print(string.format("[Hitbox Visualizer] Hit detection for '%s'", hitgroup_name))
		print(string.format("  Entity position (absolute): (%.2f, %.2f, %.2f)", entity_pos.x, entity_pos.y, entity_pos.z))
		print(string.format("  Reference position: (%.2f, %.2f, %.2f)", ref_pos.x, ref_pos.y, ref_pos.z))
		print(string.format("  Hit from position (absolute): (%.2f, %.2f, %.2f)", hit_from_pos.x, hit_from_pos.y, hit_from_pos.z))
		print(string.format("  Entity position (relative): (%.2f, %.2f, %.2f)", entity_relpos.x, entity_relpos.y, entity_relpos.z))
		print(string.format("  Hit from position (relative): (%.2f, %.2f, %.2f)", hit_from_relpos.x, hit_from_relpos.y, hit_from_relpos.z))
		print(string.format("  Hit from direction: (%.2f, %.2f, %.2f)", hit_from_dir.x, hit_from_dir.y, hit_from_dir.z))
		
		print("========================================")
		print("Raycast Hits (ref_pos = attacker):")
		print(string.format("  Using ref_pos at attacker: (%.2f, %.2f, %.2f)", ref_pos.x, ref_pos.y, ref_pos.z))
		print("----------------------------------------")
		print("  Direct approach (raycast_hit):")
		print_hits(hits)
		print("----------------------------------------")
		print("  Top-level approach (detect_hits):")
		print_hits(hits_detect)
		print("----------------------------------------")
		print("  Comparison:")
		compare_hits(hits, hits_detect, "Direct (raycast_hit)", "Top-level (detect_hits)")
		print("========================================")
		print("Raycast Hits (ref_pos = target):")
		print(string.format("  Using ref_pos at target: (%.2f, %.2f, %.2f)", ref_pos_target.x, ref_pos_target.y, ref_pos_target.z))
		print(string.format("  Entity relpos: (%.2f, %.2f, %.2f)", entity_relpos_target.x, entity_relpos_target.y, entity_relpos_target.z))
		print(string.format("  Hit from relpos: (%.2f, %.2f, %.2f)", hit_from_relpos_target.x, hit_from_relpos_target.y, hit_from_relpos_target.z))
		print_hits(hits_target_ref)
		print("----------------------------------------")
		print("  Comparison with attacker-based ref_pos:")
		compare_hits(hits, hits_target_ref, "ref_pos=attacker", "ref_pos=target")
		print("========================================")
		print("Box Hits:")
		print_hits(box_hits)
		print("========================================")
		print("Sphere Hits:")
		print_hits(sphere_hits)
		
		print("========================================")
	end,
	
	on_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type ~= "object" then
			return
		end
		
		local target = pointed_thing.ref
		if not target or not target:is_player() and not target:get_luaentity() then
			return
		end
		
	-- Check if shift is pressed (slow down entity)
	local player_name = user:get_player_name()
	local player_ctrl = user:get_player_control()
	
	if player_ctrl.sneak then
		-- Slow down the target entity
		if target:is_player() then
			-- Slow down player
			target:set_physics_override({speed = 0.1})
			core.chat_send_player(player_name, "[Hitbox Visualizer] Slowed down player")
		else
			local entity = target:get_luaentity()
			if entity then
				-- Try to slow down entity by modifying walk_speed and run_speed
				local props = target:get_properties()
				local modified = false
				
				if props.stepheight then -- Check if it's a mob-like entity
					-- Store original speeds if not already stored
					if not entity._original_walk_speed then
						entity._original_walk_speed = entity.walk_velocity or 1
						entity._original_run_speed = entity.run_velocity or 2
					end
					
					-- Set very low speeds
					entity.walk_velocity = 0.1
					entity.run_velocity = 0.1
					modified = true
				end
				
				if modified then
					core.chat_send_player(player_name, "[Hitbox Visualizer] Slowed down entity")
				else
					core.chat_send_player(player_name, "[Hitbox Visualizer] Cannot slow down this entity type")
				end
			end
		end
	end	-- Determine hitbox group name
	local hitgroup_name = hitboxes_lib.get_hitgroup_name(target)
	if not hitgroup_name then
		core.chat_send_player(player_name, "[Hitbox Visualizer] Invalid entity for hitbox detection")
		return
	end
	
	-- Check if hitboxes are registered
	if not hitboxes_lib.hitbox_groups[hitgroup_name] then
		core.chat_send_player(player_name, string.format("[Hitbox Visualizer] No hitboxes registered for group '%s'", hitgroup_name))
		return
	end
	
	-- Visualize hitboxes (attached mode, 30 seconds)
	hitboxes_lib.visualize_object_hitboxes(hitgroup_name, target, 30, true)
	
	core.chat_send_player(player_name, string.format("[Hitbox Visualizer] Showing hitbox(es) for '%s' (30s, attached)", hitgroup_name))
	end,
})
