dofile("Scripts/utils.lua");

function create_entry(entry_name, entry_tag, frame_normal, frame_debris, locator_pos, frame_black)
	local e = { name = entry_name,
			tag = entry_tag,
			normal = frame_normal,
			locator = locator_pos,
			black = frame_black,
			health = 1,
			broken = false,
			point = nil,
		}
	if frame_debris then e.debris = frame_debris end;
	return e;
end

function fetch_locator(part_name)
-- log("fetching locator "..part_name);
	local id = object.GetLocatorId("locator_debris_"..part_name);
	if id ~= -1 then
-- log("debris locator found, #"..id);
		return id
	end
	id = object.GetLocatorId("locator_"..part_name);
	if id ~= -1 then
-- log("WARNING: short name for locator found, #"..id);
		return id
	end
	id = object.GetLocatorId("locator_roof_"..part_name);
	if id ~= -1 then
-- log("roof locator found, #"..id);
		return id
	end
	id = object.GetLocatorId("locator_debris_roof_"..part_name);
	if id ~= -1 then
-- log("roof debris locator found, #"..id);
		return id
	end
end

function create_part(prefix, name, tag)
-- log("\tcreate_part for "..prefix..name);
	local part = utils.AssignFrames(prefix..name), dz, debris, loc_id, loc_pos;
	if part then
		debris = utils.AssignFrames("debris_"..name);
		black = object.GetGroupDamage(part);
-- log("\tblack="..black);
		loc_id = fetch_locator(name);
		if loc_id then loc_pos = object.GetLocatorPosition(loc_id) else loc_pos = nil end
		if prefix == "roof" then roof = true;
			dz = utils.AssignDz("damagezone_roof")
		elseif prefix == "roof_" then roof = true;
			dz = utils.AssignDz("damagezone_roof_"..name)
		else
			dz = utils.AssignDz("damagezone_"..name);
		end

		if dz ~= nil then
			frames[dz] = create_entry(prefix.."part_"..name, tag, part, debris, loc_pos, black);
			frames[dz].point = object.GetDamageZoneCenter(dz);
-- log("\tpart "..prefix..name.." found, frame "..part..", d/z "..dz);
		else
log("\nERROR: can't find damagezone_"..name.."for object "..object.desc.DescFileName.."\n");
		return false
		end
	else
-- log("no part "..prefix..name.." was found");
		return false
	end

	if tag == "C" then
		corner_parts = corner_parts + 1;
	elseif tag == "W" then
		wall_parts = wall_parts + 1;
	elseif tag == "P" then
		part_parts = part_parts +1
	end
	total_parts = total_parts + 1;
	return true
end

function Initialize()
-- log("\n\ninitializing "..object.GetDescName());
	local roof = false;
	corner_parts = 0;
	part_parts = 0;
	wall_parts = 0;
	total_parts = 0;

	health = object.GetHealth();
-- log("health = "..health);
--	AIparts = utils.CreateSubsystems();
	local corners, walls, part, name;
	frames = { }

	for i = string.byte("a"), string.byte("z") - 1 do
		name = string.char(i); walls = 1;

		if create_part("wall_", name, "W") then
-- log("single wall for side "..name)
		else
-- log("multiple walls for side "..name)
			repeat
				name = string.char(i)..walls;
-- log(name);
				part = create_part("wall_", name, "W");
				walls = walls + 1;
			until not part
		end

		create_part("roof", "", "R");
		create_part("part_", string.char(i), "P");
		create_part("roof_", string.char(i), "R");
		if not create_part("corner_", string.char(i)..string.char(i + 1), "C") then
			corners = i;
			break
		end
	end

	if not create_part("corner_", string.char(corners).."a", "C") then
-- log("FATAL: unable to load the last corner"..corner_name)
	end
	create_part("", "basement", "B");
	create_part("", "interior", "I");

-- give health to all parts (30% to corners, 70% to walls)

	local corner_hp = health * 0.3 / corner_parts;
	local wall_hp = health * 0.7 / wall_parts;
	local part_hp = health / part_parts;

-- log(total_parts.." parts in total, "..wall_parts.." walls, "..corner_parts.." corners");
-- log("health per corner: "..corner_hp..", per wall: "..wall_hp);
	for k,v in pairs(frames) do
		if v.tag == "C" then
			v.health = corner_hp;
		elseif v.tag == "W" then
			v.health = wall_hp;
		elseif v.tag == "P" then
			v.health = part_hp;
		elseif v.tag == "I" then
			v.health = health * 0.333;
		elseif v.tag == "B" then
			v.health = health;
		end
-- log("["..k.."]".."="..v.name.." ,frame "..v.normal.." hp="..v.health)
--		if v.debris then log("\tdebris:"..v.debris) end;
	end

	local rules = object.desc.DestructionRules;
	if rules ~= "" then
		local r = "{%s*(%w+)%s*=%s*([^}]+)%s*}";
-- log("rules="..rules);
		for k,v in string.gmatch(rules, r) do
			local f = frames[tonumber(k)];
			if f then
				f.connected_parts = { }
				for i in string.gmatch(v, "(%w+)") do
					table.insert(f.connected_parts, tonumber(i));
				end
			else
-- log("invalid rules: frames["..k.."] does not exist!");
			end
		end
	end
-- log(object.desc.Name.." initialized");
end

function CheckZoneDamage(ZoneId, Damage)
	local f = frames[ZoneId];
	assert(f, "(3) nil value in _house_.lua: check " .. object.desc.DescFileName);
	if f.broken then 
log("ERROR: part is already broken, ".. f.name .. " in " .. object.desc.DescFileName) 
	end
	if f.health > Damage then f.health = f.health - Damage else f.health = 0 end
-- log(f.name.." , health="..f.health);
	f.black = f.black + Damage * 0.0005;
	object.SetGroupDamage(f.black, frames[ZoneId].normal);
-- log("f.black="..f.black);
	if f.tag == "B" then return end
	if f.health == 0 then
		BreakPart(ZoneId);
		if roof then
			roof = false;
			for k,v in pairs(frames) do
				if v.tag == "R" then
					BreakPart(k)
				end
			end
		end
		local cp_list = f.connected_parts;
		if cp_list then
			for k,v in pairs(cp_list) do
				BreakPart(v)
			end
		end
	end
end

function CheckSplashDamageZones(DetonationDamage, Caliber, Zones)
	for ZoneId,val in pairs(Zones) do
		local f = frames[ZoneId];
-- log("\t\tsplash hit to "..f.normal.." ("..f.name..")");
		assert(f, "(1) nil value in _house_.lua: check " .. object.desc.DescFileName);
		f.point = val.Point;
		CheckZoneDamage(ZoneId, DetonationDamage / 10 * val.DistanceFactor * val.DistanceFactor);
	end
end

function CheckDirectDamage(WeaponDamage, Caliber, Point, Distance, ZoneId)
	local f = frames[ZoneId];
	assert(f, "(2) nil value in _house_.lua: check " .. object.desc.DescFileName);
-- log("direct hit to "..f.name);
	f.point = Point;
	CheckZoneDamage(ZoneId, WeaponDamage);
end

function BreakPart(ZoneId)
	local f = frames[ZoneId];
	assert(f, "BreakPart in _house_.lua, object " .. object.desc.DescFileName);
	if f.broken then return end;
	if f.tag == "R" then
-- log("placing effects for roof");
		CreateObject("Effects/buildings/roof_break.dsc", f.point);
	else
		CreateObject("Effects/buildings/stone_brick_big.dsc", f.point);
	end

	f.broken = true;
	object.ActivateDamageZone(false, ZoneId);
-- log("breaking part "..f.name);
	object.SetGroupVisible(false, f.normal);
	if f.debris ~= nil then
		object.SetGroupDamage(f.black, f.debris);
		object.SetGroupVisible(true, f.debris)
	end
	if f.locator then
		CreateObject("Effects/smoke/house_bigger.dsc", f.point);
	end
end

function Debug()
ScreenWarning("Name:"..object.desc.Name);
ScreenWarning("part#\tnormal\tdebris\thealth\tname");
	local debris, part_hp;
	for k,v in pairs(frames) do
		if v.debris then debris = string.format("%02i",v.debris) else debris = "---" end;
		if v.health > 0 then part_hp = string.format("%05i",v.health) else part_hp = "--------" end
ScreenWarning("["..string.format("%02i",k).."]\t \t"..
		string.format("%02i",v.normal).."\t \t"..debris.."\t "..part_hp.."\t"..v.name)
	end
end
