
-- ================================================================

-- 	Yoda Export functions

-- ================================================================

-- Globals
g_worldObjects = {}; -- internal current state of overall detected targets (Doesn't time out, but cannot have more updated info than g_memTargets)
g_memTargets = {}; -- memory of detected targets, times out after 6 seconds
g_dTargetObjects = {}; -- updated from memTargets every frame
g_dTargets={}; -- updated from memTargets every frame
g_time = 0; -- current time, updates every frame from LoGetModelTime()


-- ================================
-- 	Main loop
-- ================================
if LuaExportBeforeNextFrame then
	local origLuaExportBeforeNextFrame = LuaExportBeforeNextFrame;
	function LuaExportBeforeNextFrame()
		origLuaExportBeforeNextFrame();
		dataExport();
	end
else
	function LuaExportBeforeNextFrame()
		dataExport();
	end	
end

function dataExport()

	-- Get game time
	g_time = LoGetModelTime() or 0;

	g_MeObject = LoGetSelfData();

	if g_MeObject then
	
		--g_MeObject.ID 	= 	LoGetPlayerPlaneId() 		or 	0;
		g_MeObject.ID 	= 	6; -- dirty hack for F-16 support
		g_route 		= 	LoGetRoute() 			or 	g_route;
		g_stations 		= 	LoGetPayloadInfo() 		or 	g_stations;
		g_acMode 		= 	LoGetNavigationInfo() 		or 	g_acMode;
		g_targets 		= 	get_radar_data()			or	g_targets;
		g_dTargets 	=	get_designated_targets()	or 	g_dTargets;
		g_radarData	=	LoGetSightingSystemInfo() 	or	g_radarData;
		g_rwrData 		=	LoGetTWSInfo()			or 	g_rwrData;
		g_acVectors 	= 	getAcVectors()			or	g_acVectors;

		updateMemTargets();
		makeWorldObjects();

	end
		
end

function getAcVectors()	
	local out = g_acVectors or {};	
	local pitch,bank,yaw=LoGetADIPitchBankYaw();
	local GSP3D = LoGetVectorVelocity() or {x=0,y=0,z=0};
	out.GSP3D = GSP3D;
	out.CAS = LoGetIndicatedAirSpeed() or 0;
	out.TAS = LoGetTrueAirSpeed() or 0;
	out.VV = LoGetVerticalVelocity() or 0;	
	out.GSP = math.sqrt( GSP3D.x^2 + GSP3D.z^2 );	
	out.PITCH = pitch or 0;
	out.BANK = bank or 0;
	out.YAW = yaw or 0;
	return out;
end

function memTargetExistsAt(trg)
	for i=1,#g_memTargets do
		if (trg.ID==g_memTargets[i].target.ID) then
			return i;
		end
	end
	return 0;
end

function updateMemTargets()

	-- Clean out old and set all locked contacts to scan contacts ( they will be updated anyway if they are still locked )
	for i=#g_memTargets,1,-1 do
		if g_time-g_memTargets[i].timer>6.0 then
			table.remove(g_memTargets,i);
		elseif lockNotSearch(g_memTargets[i].target.flags) then
			g_memTargets[i].usingMem = true;
			g_memTargets[i].target.flags = 2;
		end
	end

	-- Insert new or update
	for i=1,#g_targets do

		local ind = memTargetExistsAt(g_targets[i]);
		if ind>0 then
			-- update
			g_memTargets[ind].usingMem = false;
			g_memTargets[ind].target = g_targets[i];
			g_memTargets[ind].timer = g_time;
		else
			-- or insert
			local newInd = #g_memTargets+1;
			g_memTargets[newInd] = {};
			g_memTargets[newInd].usingMem = false;
			g_memTargets[newInd].target = g_targets[i];
			g_memTargets[newInd].timer = g_time;
			g_memTargets[newInd].createdAt = g_time;
		end

	end
end

function hashID(IDn)
	if not IDn then
		return nil
	end
	return string.format('%i', IDn);
end

function getObjectById(IDn)	
	return g_worldObjects[hashID(IDn)];
end

function newWorldObject(key)
	local object = {	LatLongAlt = {}, Type={}	};
	g_worldObjects[key] = object;
	return object;
end

function makeWorldObjects()

	for i=1,#g_memTargets do

		local target = g_memTargets[i].target;
		local key = hashID(target.ID);
		local object = g_worldObjects[key] or newWorldObject(key);

		local latLong = LoLoCoordinatesToGeoCoordinates(target.position.p.x, target.position.p.z);
		
		object.ID = target.ID;
		object.Name = "target";
		object.Coalition = "unknown";
		object.CoalitionID = target.country;
		object.LatLongAlt.Lat = latLong.latitude;
		object.LatLongAlt.Long = latLong.longitude;
		object.LatLongAlt.Alt = target.position.p.y;
		object.Heading = 0;
		object.Type = target.type;
		object.createdAt = g_memTargets[i].createdAt;
		object.gotHeadingAt = target.gotHeadingAt;
		object.hasHeading = target.hasHeading;

	end
	
end

function targetPresent(ID, table_to_return)
	for i=1,#table_to_return do
		if table_to_return[i].ID==ID then
			return true;
		end
	end
	return false;
end


function get_radar_data()

	-- First get the normal radar data present in all planes
	local table_to_return = {};

	-- First get locked targets
	local tbl_ = LoGetLockedTargetInformation();
	if tbl_ then
		for i=1,#tbl_ do
			local item = tbl_[i]
			if item.target then
				item.target.hasHeading = true;
				item.target.gotHeadingAt = g_time;
				table_to_return[1+#table_to_return] = item.target;
			end
		end
	end

	-- Get TWS search contacts (F-15 only)
	tbl_ = LoGetF15_TWS_Contacts();
	if tbl_ then
		for i=1,#tbl_ do
			local item = tbl_[i]
			if item.target and not targetPresent(item.target.ID, table_to_return) then
				item.target.hasHeading = true;
				item.target.gotHeadingAt = g_time;
				table_to_return[1+#table_to_return] = item.target;
			end
		end
	end

	-- Get normal search contacts
	tbl_ = LoGetTargetInformation();
	if tbl_ then
		for i=1,#tbl_ do
			local T = tbl_[i];
			if not targetPresent(T.ID, table_to_return) then
				
				-- Workaround due to export bug reporting rws scan
				local Told = getObjectById(T.ID);
				if Told and Told.hasHeading and g_time-Told.gotHeadingAt<1 then
					T.hasHeading = true;
					T.gotHeadingAt = Told.gotHeadingAt;
				else
					T.hasHeading = false;
					T.gotHeadingAt = 0;
				end
				
				table_to_return[1+#table_to_return] = T;
			end
		end
	end

	for i=1,#table_to_return do
		table_to_return[i].scanTime = g_time;
	end

	return table_to_return;

end

-- Makes a true copy of arbitrary table
function deepcopy(object)
	local lookup_table={};
	local function _copy(object)
		if type(object)~="table" then return object;
		elseif lookup_table[object] then return lookup_table[object]; end
		local new_table={}; lookup_table[object]=new_table;
		for index, value in pairs(object) do new_table[_copy(index)]=_copy(value); end
		return setmetatable(new_table, getmetatable(object));
	end
	return _copy(object);
end

function num2hex(num)
	local hexstr = '0123456789abcdef'
	local s = ''
	while num > 0 do
		local mod = math.fmod(num, 16)
		s = string.sub(hexstr, mod+1, mod+1) .. s
		num = math.floor(num / 16)
	end
	if s == '' then s = '0' end
	return s
end

function lockNotSearch(flags)
	-- Determines from lock flags if target is lock or search contact.
	-- Only tested for F-15

	local hexNumber = num2hex(flags);
	local length = string.len(hexNumber);

	if string.sub(hexNumber,length)~='2' and string.sub(hexNumber,length)~='4' then
		if length==1 then
			return true;
		elseif length>1 and string.sub(hexNumber,length-1,length-1)~='2' and string.sub(hexNumber,length-1,length-1)~='4' then
			return true
		end
	end
	return false;

end


function get_designated_targets()

	-- Global state of targets
	g_dTargets={};
	g_dTargetObjects = {};
	g_PdtBT = true;
	g_PdtIsJamming = false;
	g_dTargetsCount = 0;

	if g_targets then
		for i,cur in pairs(g_targets) do
			local target=getObjectById(cur.ID);
			if target and lockNotSearch(cur.flags) then
				g_dTargets[1+#g_dTargets]=cur.ID;
				g_dTargetObjects[1+#g_dTargetObjects] = target;
				if cur.isjamming>0 and i==1 then
					g_PdtIsJamming = true;
					if not cur.jammer_burned then
						g_PdtBT = false;
					end
				end
			end
		end
	end

	g_dTargetsCount = #g_dTargets;
	return g_dTargets;

end