---------------------------------------------------------------------------
-- FlightSimX New Animation Manager
---------------------------------------------------------------------------

rollout summaryRollout "Summary" width:450 height:400
(
	listBox lbxSummary "Animation Summary" pos:[5,5] width:440 height:28
	button btnSummaryDone "Close Summary" pos:[455,20]
	
	on btnSummaryDone pressed do
	(
		summaryRollout.open = false
	)
)

rollout AnimationDLG "Animation Dialog" width:600 height:398
(
	---- Local variables ----
	global rootAnimDef,curSelNodeList,curSelNodeRoot,curAnimInfo,curAnimName,curSelGroup;
	global state_animPresent, state_markerPresent, selectedMarker;
		
	---- Dialog definitions ----
	listbox animGroup "Groups" pos:[5,8] width:150 height:14 
	listbox animList "Animation List" pos:[160,8] width:200 height:14 
	button objectSelect "Update Sel" pos:[370,20] width:80 height:20 message:"" toolTip:"Click to select which objects to edit animations for."
	edittext selectedObj "" pos:[460,20] width:110 height:18
	listbox objectAnimList "Object Anims" pos:[370,45] width:200 height:7

	button createAnim "Create" pos:[370,165] width:90 height:20 toolTip:"Creates a new animation for the selected part without keyframe offsets."
	button deleteAnim "Delete" pos:[480,165] width:90 height:20
	edittext newFramesStart "Start" pos:[370,190] width:88 height:20
	edittext newFramesEnd "End" pos:[480,190] width:88 height:20


	GroupBox grp4 "Auto Rotate" pos:[400,218] width:170 height:119
	checkbutton RotateX "X" pos:[412,235] width:39 height:20
	edittext rotateStartX "" pos:[455,235] width:50 height:20
	edittext rotateEndX "" pos:[510,235] width:50 height:20
	checkbutton RotateY "Y" pos:[412,260] width:39 height:20
	edittext rotateStartY "" pos:[455,260] width:50 height:20
	edittext rotateEndY "" pos:[510,260] width:50 height:20
	checkbutton RotateZ "Z" pos:[412,285] width:39 height:20
	edittext rotateStartZ "" pos:[455,285] width:50 height:20
	edittext rotateEndZ "" pos:[510,285] width:50 height:20
	button rotateCreate "Create" pos:[410,310] width:150 height:20

	GroupBox grp3 "Markers And Events" pos:[6,218] width:388 height:150
	listbox markerList "" pos:[16,237] width:100 height:9
	button addMarker "Add Marker" pos:[127,237] width:80 height:20 toolTip:"Adds a marker to the list for this animation"
	button addEvent "Add Event" pos:[212,237] width:80 height:20 toolTip:"Adds an event to ths list for this animation"
	button deleteMarker "Delete" pos:[298,237] width:80 height:20 toolTip:"Deletes the currently selected marker/event."
	edittext markerIDEdit "ID" pos:[230,267] width:140 height:18
	spinner frameSpinner "Frame" pos:[148,268] width:75 height:16 range:[0,1000,0] type:#integer scale:1
	edittext edt5 "" pos:[158,321] width:0 height:0
	edittext markerName "Name/Command" pos:[127,294] width:250 height:20
	edittext markerParam "Parameter" pos:[127,321] width:250 height:20
	
	button convertAmbient "Tick18 -> Ambient" pos:[15,374] width:100 height:20 toolTip:"Auto-annotates tick 18 parts for ambient animation."
	button convertFS9 "FS9 -> FS10" pos:[115,374] width:100 height:20 toolTip:"Auto-annotates old animations based on animation script."
	button btnSummary "Summary" pos:[490,374] width:80 height:20

	
	---- Local functions ----

	-- Loads and parses the XML file. Will return the real root node of
	-- the animation definition file.
	global rootDocumentNodeList;
	fn LoadAnimationDef =
	(
		local xmlParser,filePath, res;
		
		-- Create XML Parser
		xmlParser = MaxXML();
		
		-- Determine where the document is located (need plugin directory)
		-- TODO : Need easy way to do this!!!
		if (MaxUtils == undefined) then
		(
			filePath = "e:\fsx\public\core\bin\art\Max6_Plugins";
			print "Failed finding plugin path!!";
		)
		else
		(
			filePath = MaxUtils.GetPluginPath();
			print ("Plugin path = " + filePath);
		)
		
		-- Load up the XML file		
		res = xmlParser.LoadDocument(filePath + "\modeldef.xml");
		
		-- Validate that the document has loaded and get the real
		-- root node for it.
		if res == false then
		(
			print ("Failed loading XML file = " + filePath + "\modeldef.xml");
		  	-- TODO Error
		)
		else
		(
			-- Take the document root and extract the real XML root node. This is essentially
			-- the second child node since the first one is the <?xml...> tag.
			print ("Loaded XML file = " + filePath + "\modeldef.xml");
			local rootNode = xmlParser.GetRootNode();
			rootDocumentNodeList = rootNode.GetChildNodes();
			
			return rootDocumentNodeList[2];
		)

		-- Null out the parser so it can safely be destroyed		
		xmlParser = undefined;
	  	return undefined;
	)

	fn Marker_ExtractName markerToken =
	(
		return markerToken[3][2];
	)

	fn Marker_ExtractParam markerToken =
	(
		return markerToken[4][2];
	)

	fn Marker_ExtractID markerToken =
	(
		return markerToken[2][2];
	)

	fn Marker_ExtractType markerToken =
	(
		return markerToken[1][2];
	)

	-- Determines and returns the animation notrack for the supplied node. If none exists,
	-- a new one will be created.		
	global GetNoteTrackInfo_Result;
	fn GetNoteTrackInfo curNode =
	(
		GetNoteTrackInfo_Result = undefined;
		
		-- First thing is to get the note track from the node, or create
		-- one if it isn't there
		if (curNode != undefined) and (curNode.controller != undefined) do
		(
			if (numNoteTracks curNode.controller) < 1 then
			(
				GetNoteTrackInfo_Result = NoteTrack "Anim";
				addNoteTrack curNode.controller GetNoteTrackInfo_Result;
			)
			else 
				GetNoteTrackInfo_Result = getNoteTrack curNode.controller 1;
		)
		return GetNoteTrackInfo_Result;
	)

	-- Because all parameters to markers and events are in the form PRAM = "VALUE", this function will
	-- parse a note key and extract all the PARAM/VALUE pairs then store them into an array
	fn TokenizeNoteKey noteKey = 
	(
		local cmdArray = #("","");
		local tokenArray = #(#("",""),#("",""),#("",""),#("",""));  -- Hardcoded order for Type,ID,Name,Param
		local cmdPos = 1;
		local str = noteKey.value as stringstream;
		while (peekToken str) != undefined do
		(
			local token = readToken str;
			if  cmdPos == 1 then
			(
				if token != "=" then
				(
					cmdArray[1] = token;
				)
				else
				(				
					cmdPos = 2;
				)
				
			)
			else
			(
				cmdArray[2] = token;

				if (cmdArray[1] == "MARKER_EVENT_TYPE") then
					(tokenArray[1] = cmdArray;)
				else
				if (cmdArray[1] == "MARKER_EVENT_ID") then
					(tokenArray[2] = cmdArray;)
				else
				if (cmdArray[1] == "MARKER_EVENT_NAME") then
					(tokenArray[3] = cmdArray;)
				else
				if (cmdArray[1] == "MARKER_EVENT_PARAM") then
					(tokenArray[4] = cmdArray;)
				else append tokenArray cmdArray;

				cmdPos = 1;
				cmdArray = #("","");
			)
		)
		return tokenArray;
	)

	fn EnumerateObjectAnimationList curNode =
	(
		local animStrList,ntrack,nkeys;
		animStrList = #();

		ntrack = GetNoteTrackInfo curNode;
		nkeys = ntrack.keys;
		
		-- Go through all notes and find the start and end frames
		-- for the specified animation
		local i = 1;
		while nkeys[i] != undefined do
		(
			local nkey = nkeys[i];
			local keyTokens = TokenizeNoteKey nkey;
			if (keyTokens[5] != undefined) do
			(
				if (keyTokens[5][1] == "ANIM_START") do
				(
					append animStrList keyTokens[5][2];
				)
			)
			i = i + 1;
		)
		return animStrList;
	)


	-- Populates the animation list and dependant dialogs
	fn PopulateObjectAnimationList =
	(
		local animStrList,animGroupNode,animNodeList,ntrack,nkeys;
		animStrList = #();
		animNodeList = #();
		if (curSelNodeList != undefined) do
		(
			for listNode in curSelNodeList do
			(
				print listNode;
				print "foo";
				local res = EnumerateObjectAnimationList(listNode);
				print res;
				print "bar";
				local merged = for name in res
					where (finditem animStrList name) == 0 collect name
				print merged;
				print "baz";
				join animStrList merged
			)
		)

		objectAnimList.items = animStrList;
	)

	fn SetSelection curNode =
	(
	    curSelNodeList = curNode;

		-- Find the selection's parent (well the first selection's)
		curSelNodeRoot = undefined;
		if (curSelNodeList != undefined) and (curSelNodeList.count > 0) do
		(
			curSelNodeRoot = curSelNodeList[1];
			while curSelNodeRoot.parent != undefined do
			(
				curSelNodeRoot = curSelNodeRoot.parent;
			)
		)
		
		-- Populate the current animation list
		PopulateObjectAnimationList();
	)

	fn FindKeyTimeSlot keys time = (
		local lastKey,i;
		while (getNoteKeyIndex keys time) != undefined do 
		(
			time += 1t;
			--print ("*Time Adjust = " + (time as string));
		)
		return time;
	)

	-- This function creates a new key, ensuring there is no overlap
	-- with existing keys by incrementing the time's tick count	
	fn CreateKey keys time = (
		return addNewNoteKey keys (FindKeyTimeSlot keys time);
	)

	-- This function is used to set up anim start/end tags. It also takes care of reserving
	-- the slots for unselected nodes
	fn CreateKeyRecursivePrefix namePrefix nameMatch bAllowNuke rootNode curNode time value = 
	(
		-- Get the note track for this guy
		local ntrack = GetNoteTrackInfo rootNode;

		
		-- Check prefix if needed
		local bCreateKey = true;
		local bNukeKeys = false;
		if namePrefix != undefined then
		(
		    local prefix = substring rootNode.name 1 namePrefix.count
		    -- print ("Name prefix = " + namePrefix + "; Prefix = " + prefix + "\n");
		    local cmpRes = stricmp prefix namePrefix
		    if cmpRes != 0 then
		    (
		        bCreateKey = false;
		    )
		    else
		    (
		        bNukeKeys = true;
                print ("  Found: "+rootNode.name+"  For: "+value);
		    )
        )
        else
        (
		    if nameMatch != undefined do
		    (
		        local cmpRes = stricmp rootNode.name nameMatch
		        if cmpRes != 0 then
		        (
		            bCreateKey = false;
		        )
		        else
		        (
		            bNukeKeys = true;
                    print ("  Found: "+rootNode.name+"  For: "+value);
		        )
		        
            )
        )

		-- Delete all keys if requested
		if bAllowNuke == true do
		(
		    if bNukeKeys == true do
		    (
		        while ntrack.keys[1] != undefined do
		        (
			        ntrack.keys[1].selected = true;
			        deleteNoteKeys ntrack.keys #selection
		        )
		    )
		)

		-- Create a key for this node
		if bCreateKey == true do
		(
		    local newKey = CreateKey ntrack.keys time;
			newKey.value = value;
	    )
	
		-- process all children
		local child = rootNode.children;
		for i = 1 to child.count do
			CreateKeyRecursivePrefix namePrefix nameMatch bAllowNuke child[i] curNode time value;
	)
	
	fn CreateKeyRecursive rootNode curNode time value = 
	(
	    CreateKeyRecursivePrefix undefined undefined false rootNode curNode time value;
	)
	-- Determine the category we have selected, i.e: All = none.
	fn DetermineAnimationGroup curSel rootNode =
	(
		if curSelGroup != undefined do
		(
			if curSelGroup != "All" do
			(
				animGroupNode = rootAnimDef.SelectSingleNode("/ModelInfo/AnimationGroup[@name=\"" + curSelGroup + "\"]");
				return animGroupNode;
			)
		)
		return undefined;
	)

	-- Populates the animation list and dependant dialogs
	fn PopulateAnimationList =
	(
		local animStrList,animGroupNode,animNodeList;
		animStrList = #();
		animNodeList = #();

		-- Check which category (or group) we are in.
		-- Populate the listbox apropriately
		animGroupNode = DetermineAnimationGroup curSelNodeList[1] curSelNodeRoot;
		if animGroupNode == undefined then
		(
			-- Select all animations
			animNodeList = rootAnimDef.SelectNodes("/ModelInfo/Animation");
		)
		else
		(
			local animRefList;

			-- Get all the references for the group
			animRefList = animGroupNode.SelectNodes("AnimationRef");
			
			-- Go through this list and extract all nodes
			for i = 1 to animRefList.count do
			(
				local guid,animRef,refedList;
				local query = "/ModelInfo/Animation[@guid=\"";
				
				animRef = animRefList[i];
				guid = animRef.GetAttributeValue("guid");
				
				query = query + guid;
				query = query + "\"]";
				refedList = rootAnimDef.SelectNodes(query)
				
				join animNodeList refedList
			)
		)		
		
		-- Take our explicit node list and turn it into a string list
		for i = 1 to animNodeList.count do
		(
			local name,animNode;
			animNode = animNodeList[i];
			name = animNode.GetAttributeValue("name");
			append animStrList name;
		)
		animList.items = animStrList;
	)


	-- Populates the animation list and dependant dialogs
	fn PopulateGroupList =
	(
		local animStrList,animNodeList;
		animStrList = #();
		animNodeList = #();

		animNodeList = rootAnimDef.SelectNodes("/ModelInfo/AnimationGroup");
		
		-- Take our explicit node list and turn it into a string list
		append animStrList "All";
		for i = 1 to animNodeList.count do
		(
			local name,animNode;
			animNode = animNodeList[i];
			name = animNode.GetAttributeValue("name");
			append animStrList name;
		)
		animGroup.items = animStrList;
	)

 	fn ReselectMarker =
	(
		for i = 1 to markerList.items.count do
		(
			if markerList.items[i] == selectedMarker do
				markerList.selection = i;
		)
	)

	fn PopulateMarkerInfo marker =
	(
		if (marker != undefined) and (curAnimInfo != undefined) then
		(
			local markerToken = TokenizeNoteKey marker;
			markerName.text = Marker_ExtractName(markerToken);
			markerParam.text = Marker_ExtractParam(markerToken);
			print ("MT : " + ((floor marker.time.frame) as string));
			print ("MV : " + ((((floor marker.time.frame) as integer)) as string));
			frameSpinner.range = [curAnimInfo[2],curAnimInfo[3],1];
			frameSpinner.value = (((floor marker.time.frame) as integer)) as float;
			markerIDEdit.text = Marker_ExtractID(markerToken);
		)
		else
		(
			markerName.text = "N/A";
			markerParam.text = "";
			frameSpinner.value = 0;
			markerIDEdit.text = "";
		)
	)

	fn FindMarkerByID markerID =
	(
		if (curAnimInfo != undefined) and (markerID != "") do
		(
			local ntrack = GetNoteTrackInfo curSelNodeList[1]
			local nkeys = ntrack.keys;
			
			-- Go through all keys and validate the marker against the supplied ID
			local i = 1;
			while nkeys[i] != undefined do
			(
				local nkey = nkeys[i];
				if  (nkey.time>=curAnimInfo[2]) and (nkey.time<=curAnimInfo[3]) do
				(
					local keyTokens = TokenizeNoteKey nkey;
					if Marker_ExtractID(keyTokens) == markerID do
						return nkey;
				)
				i = i + 1;
			)
		)
		return undefined;
	)


	-- This function updates the global state of the dialog based on a set of local variables
	-- which keep track of the state of the tool
	fn UpdateDialogState animInfo =
	(
		-- Copy the selected marker ID to keep track of the selection
		selectedMarker = markerList.selected;

		-- Find the marker/event node based on its ID
		local marker = FindMarkerByID(selectedMarker);
		PopulateMarkerInfo(marker);

		-- Update the general dialog state as needed
		if marker != undefined then 
			state_markerPresent = true;
		else
			state_markerPresent = false;

		convertAmbient.enabled = true;
		convertFS9.enabled = true;

		-- Update the enabled/disabled states as needed
		createAnim.enabled = not state_animPresent;
		--deleteAnim.enabled = state_animPresent;
		
	    -- Update the enabled/disabled states as needed for marker/event section
		markerList.enabled = state_animPresent;
		addMarker.enabled = state_animPresent;
		addEvent.enabled = state_animPresent;
		deleteMarker.enabled = state_animPresent and state_markerPresent;
		markerIDEdit.enabled = state_animPresent and state_markerPresent;
		frameSpinner.enabled = state_animPresent and state_markerPresent;
		edt5.enabled = state_animPresent and state_markerPresent;
		markerName.enabled = state_animPresent and state_markerPresent;
		markerParam.enabled = state_animPresent and state_markerPresent;
			
		-- Update current selection name
		if curSelNodeList != undefined then
		(
			if curSelNodeList.count == 1 then
				selectedObj.text = curSelNodeList[1].name;
			else
				selectedObj.text = "<multiple>";
		)
		else
			selectedObj.text = "N/A";
		
		
    )



	fn PopulateMarkerList animInfo =
	(
		-- Flush out the current marker list
		local markerStrList = #();
		
		-- Populate with all enties
		if animInfo != undefined do
		(
			local ntrack = GetNoteTrackInfo curSelNodeList[1]
			
			-- Go through all keys and validate the marker against the supplied ID
			nkeys = ntrack.keys;
			local i = 1;
			while nkeys[i] != undefined do
			(
				local nkey = nkeys[i];
				if  (nkey.time >= animInfo[2]) and (nkey.time <= animInfo[3]) do
				(
					local markerToken = TokenizeNoteKey nkey;
					local markerID = Marker_ExtractID(markerToken);
					if markerID != "" do
					(
						append markerStrList markerID;
					)
				)
				
				i = i + 1;
			)
		)
		markerList.items = markerStrList;
	)

		
	-- This function goes through a note track and extracts all relevant information to
	-- a specific animtion, including its name, start/end times and markers/events
	global GetAnimationInformation_Result;
	fn GetAnimationInformation curNode anim  =
	(
		local ntrack,sframe,eframe,nkeys;
		
		if (curNode != undefined) do
		(
			start_key = "ANIM_START = \"" + anim + "\"";
			end_key = "ANIM_END = \"" + anim + "\"";

			
			ntrack = GetNoteTrackInfo curNode
			nkeys = ntrack.keys;
			
			-- Go through all notes and find the start and end frames
			-- for the specified animation
			local i = 1;
			while nkeys[i] != undefined do
			(
				local nkey = nkeys[i];
				local keyTokens = TokenizeNoteKey nkey;
				if (keyTokens[5] != undefined) do
				(
					if (keyTokens[5][1] == "ANIM_START") and (keyTokens[5][2] == anim) do
						(sframe = nkey.time;/*print "Found Start";print keyTokens;*/)
					if (keyTokens[5][1] == "ANIM_END") and (keyTokens[5][2] == anim) do
						(eframe = nkey.time;/*print "Found End";print keyTokens;*/)
				)
				i = i + 1;
			)
			
			-- If we have a defined animation, find all marker and event keys
			-- located in this anim
			if (sframe != undefined) and (eframe != undefined) do
			(
				local markerList = #();
				
				-- Scan markers
				i = 1;
				while nkeys[i] != undefined do
				(
					local nkey = nkeys[i];
					local markerInfo = #("","","","");
					if  (nkey.time>=sframe) and (nkey.time<=eframe) do
					(
						local keyTokens = TokenizeNoteKey nkey;
						for j = 1 to keyTokens.count do
						(
							markerInfo[1] = keyTokens[1][2];
							markerInfo[2] = keyTokens[2][2];
							markerInfo[3] = keyTokens[3][2];
							markerInfo[4] = keyTokens[4][2];
					)

						if (markerInfo[1]!="") and (markerInfo[2]!="") do
							append markerList markerInfo;
					)
					i = i + 1;
				)

				-- Start building the anim info result array
				GetAnimationInformation_Result = #(animName,sframe,eframe,markerList);
	
				return GetAnimationInformation_Result;			
			)
		)
		

		return undefined;
	)
	
	fn DeleteAnimation curNode anim =
	(
		local ntrack,sframe,eframe,nkeys;
		local animInfo,i;
		
		ntrack = GetNoteTrackInfo curNode;
		nkeys = ntrack.keys;
		
		-- Go through all notes and find the start and end frames
		i = 1;
		while nkeys[i] != undefined do
		(
			local nkey = nkeys[i];
			local keyTokens = TokenizeNoteKey nkey;
			if (keyTokens[1] != undefined) do
			(
				if (keyTokens[5][1] == "ANIM_START") and (keyTokens[5][2] == anim) do
					nkey.selected = true;
				if (keyTokens[5][1] == "ANIM_END") and (keyTokens[5][2] == anim) do
					nkey.selected = true;
			)
			i = i + 1;
		)
		deleteNoteKeys ntrack.keys #selection

		return undefined;
	)
	
	fn UpdateAnimationSelection animName = 
	(
			animInfo = GetAnimationInformation curSelNodeList[1] animName;
	
			if animInfo != undefined then
				state_animPresent = true;
			else
				state_animPresent = false;
			curAnimInfo = animInfo

			PopulateMarkerList(curAnimInfo);
			UpdateDialogState(curAnimInfo);

			-- Pre populate the auto rotate boxes if possible
			animNode = rootAnimDef.SelectSingleNode("/ModelInfo/Animation[@name=\""+curAnimName+"\"]");
			if animNode != undefined do 
			(
				local startX = animNode.GetAttributeValue("autoRotateXStart");
				local startY = animNode.GetAttributeValue("autoRotateYStart");
				local startZ = animNode.GetAttributeValue("autoRotateZStart");
				local endX = animNode.GetAttributeValue("autoRotateXEnd");
				local endY = animNode.GetAttributeValue("autoRotateYEnd");
				local endZ = animNode.GetAttributeValue("autoRotateZEnd");
				if (startX != "") and (endX != "") then
				(
					RotateX.state = true;
					rotateStartX.text = startX;
					rotateEndX.text = endX;
				)
				else
				(
					RotateX.state = false;
					rotateStartX.text = "0";
					rotateEndX.text = "0";
				)
		
				if (startY != "") and (endY != "") then
				(
					RotateY.state = true;
					rotateStartY.text = startY;
					rotateEndY.text = endY;
				)
				else
				(
					RotateY.state = false;
					rotateStartY.text = "0";
					rotateEndY.text = "0";
				)
		
				if (startZ != "") and (endZ != "") then
				(
					RotateZ.state = true;
					rotateStartZ.text = startZ;
					rotateEndZ.text = endZ;
				)
				else
				(
					RotateZ.state = false;
					rotateStartZ.text = "0";
					rotateEndZ.text = "0";
				)
			)

	)
	
	fn CreateMarkerValue ID Type name param =
	(
		local keyValue = "MARKER_EVENT_ID = \"" + ID + "\"  ";
		keyValue = keyValue + "MARKER_EVENT_TYPE = \"" + Type + "\"  ";
		keyValue = keyValue + "MARKER_EVENT_NAME = \"" + name + "\"  ";
		keyValue = keyValue + "MARKER_EVENT_PARAM = \"" + param + "\"  ";
		return keyValue;
	)


	fn CreateMarker time ID name =
	(
		if (curAnimInfo != undefined) do
		(
			local ntrack = GetNoteTrackInfo curSelNodeList[1]
			local newKey = CreateKey ntrack.keys (curAnimInfo[2] + time);
			newKey.value = CreateMarkerValue ID "Marker" name "";
		)
	)

	fn CreateEvent time ID name param =
	(
		if (curAnimInfo != undefined) do
		(
			local ntrack = GetNoteTrackInfo curSelNodeList[1]
			local newKey = CreateKey ntrack.keys (curAnimInfo[2] + time);
			newKey.value = CreateMarkerValue ID "Event" name param;
		)
	)

	fn DeleteMarkerByID ID =
	(
		local animInfo = curAnimInfo;

		-- Locate our marker
		if animInfo != undefined do
		(
			local ntrack = GetNoteTrackInfo curSelNodeList[1]
			
			-- Go through all keys and validate the marker against the supplied ID
			local nkeys = ntrack.keys;
			local i = 1;local done = false;
			while (nkeys[i] != undefined) and (done == false) do
			(
				local nkey = nkeys[i];
				if (nkey.time >= animInfo[2]) and (nkey.time <= animInfo[3]) do
				(
					local markerToken = TokenizeNoteKey nkey;
					local marker = Marker_ExtractID(markerToken);
					if marker == ID do
					(
						deleteNoteKey nkeys i;
						done = true;
					)
				)
				i += 1;
			)
		)
	)

	
	-- Event handlers
	




	fn CreateNewAnimation =
	(
		local nk_start,nk_end;

		local CurSelNode;
		local i = 1;
		for i = 1 to curSelNodeList.count do
		(
			CurSelNode = curSelNodeList[i];
			
			-- Determine the end of the note tracks
			local ntrack;
			ntrack = GetNoteTrackInfo CurSelNode;
			local nkeys = ntrack.keys;
			
			-- Determine if this animation already exists or is reserved somewhere
			local animStartTime = 0f;
			local animEndTime = 0f;
			animStartTime.frame = (newFramesStart.text) as integer;
			animEndTime.frame = (newFramesEnd.text) as integer;
			
			-- Create new animation at the end
		    nk_start = CreateKey ntrack.keys animStartTime;
			nk_start.value = ("ANIM_START = \"" + curAnimName + "\"");
	
		    nk_end = CreateKey ntrack.keys animEndTime;
			nk_end.value = ("ANIM_END = \"" + curAnimName + "\"");
		)
	
		-- Update animation display
		UpdateAnimationSelection(curAnimName);
	)

	fn CreateRotation curNode curAnim startX endX startY endY startZ endZ =
	(
		print ("New rot start = " + (startX as string) + ", " + (startY as string) + ", " + (startZ as string));
		print ("New rot end = " + (endX as string) + ", " + (endY as string) + ", " + (endZ as string));
	
		-- Gather the animation information
		local animInfo = GetAnimationInformation curNode curAnim;
		
		if animInfo != undefined do (in coordsys local
		(
			-- Grab the controler
			in coordsys local (
			local RX = curNode.rotation.x_rotation;
			local RY = curNode.rotation.y_rotation;
			local RZ = curNode.rotation.z_rotation;
			print ("Rot Init = " + (curNode.rotation.x_rotation as string) + ", " + (curNode.rotation.y_rotation as string) + ", " + (curNode.rotation.z_rotation as string));
			print ("Rot Init2 = " + (RX as string) + ", " + (RY as string) + ", " + (RZ as string));
			)
			
			-- Delete all keys within the range
	        deleteTime curNode.rotation.controller animInfo[2] animInfo[3] #incLeft #incRight #noSlide 
			
			-- Create keys
	         with animate on
			(
				at time animInfo[2]
				( 
					curNode.rotation.x_rotation = startX
					curNode.rotation.y_rotation = startY
					curNode.rotation.z_rotation = startZ
--					curNode.rotation.x_rotation = RX + startX
--					curNode.rotation.y_rotation = RY + startY
--					curNode.rotation.z_rotation = RZ + startZ
					print ("Rot A = " + (curNode.rotation.x_rotation as string) + ", " + (curNode.rotation.y_rotation as string) + ", " + (curNode.rotation.z_rotation as string));
				)

				at time (animInfo[2]+(1*(animInfo[3]-animInfo[2]))/4) 
				(
					curNode.rotation.x_rotation = - (startX / 2) 
					curNode.rotation.y_rotation = - (startY / 2) 
					curNode.rotation.z_rotation = - (startZ / 2) 
--					curNode.rotation.x_rotation = RX + (startX / 2) 
--					curNode.rotation.y_rotation = RY + (startY / 2) 
--					curNode.rotation.z_rotation = RZ + (startZ / 2) 
					print ("Rot B = " + (curNode.rotation.x_rotation as string) + ", " + (curNode.rotation.y_rotation as string) + ", " + (curNode.rotation.z_rotation as string));
				)

				at time (animInfo[2]+(2*(animInfo[3]-animInfo[2]))/4) 
				(
					curNode.rotation.x_rotation = - (startX / 2) 
					curNode.rotation.y_rotation = - (startY / 2) 
					curNode.rotation.z_rotation = - (startZ / 2) 
--					curNode.rotation.x_rotation = RX 
--					curNode.rotation.y_rotation = RY 
--					curNode.rotation.z_rotation = RZ 
					print ("Rot C = " + (curNode.rotation.x_rotation as string) + ", " + (curNode.rotation.y_rotation as string) + ", " + (curNode.rotation.z_rotation as string));
				)


				at time (animInfo[2]+(3*(animInfo[3]-animInfo[2]))/4) 
				(
					curNode.rotation.x_rotation = (endX / 2) 
					curNode.rotation.y_rotation = (endY / 2) 
					curNode.rotation.z_rotation = (endZ / 2) 
--					curNode.rotation.x_rotation = RX + (endX / 2) 
--					curNode.rotation.y_rotation = RY + (endY / 2) 
--					curNode.rotation.z_rotation = RZ + (endZ / 2) 
					print ("Rot D = " + (curNode.rotation.x_rotation as string) + ", " + (curNode.rotation.y_rotation as string) + ", " + (curNode.rotation.z_rotation as string));
				)

				at time animInfo[3] 
				(
					curNode.rotation.x_rotation = (endX / 2) 
					curNode.rotation.y_rotation = (endY / 2) 
					curNode.rotation.z_rotation = (endZ / 2) 
--					curNode.rotation.x_rotation = RX + endX
--					curNode.rotation.y_rotation = RY + endY
--					curNode.rotation.z_rotation = RZ + endZ
					print ("Rot E = " + (curNode.rotation.x_rotation as string) + ", " + (curNode.rotation.y_rotation as string) + ", " + (curNode.rotation.z_rotation as string));
				)

			) 
		))
	)

	on AnimationDLG open do
	(
		local curSelList;
		
		-- Open and parse the XML file
		rootAnimDef = LoadAnimationDef();
		
		-- Assume the current node is the first selected node
		curSelNodeList = getCurrentSelection();
		curSelNodeRoot = undefined;
		--curSelList = getCurrentSelection();
		curSelGroup = "All";
		curAnimName = "";
		selectedMarker = "";
		state_animPresent = false;
		state_markerPresent = false;
		if curSelNodeList.count != 0 do 
		(
		    --SetSelection(curSelList[1]);
		    SetSelection(curSelNodeList);
		)
	
		-- Populate the category list
		PopulateGroupList();
		
		-- Populate the animation list dialog and dependant components
		PopulateAnimationList();
		
		-- Update the dialog settings
		UpdateDialogState(curAnimInfo);
	
		-- Determine if animation is defined for the selected object
		-- and populate the apropriate fields
		animName = animList.selected;
		curAnimName = animName;
		UpdateAnimationSelection(curAnimName);
	)
	on animGroup selected sel do
	(
		local selGroup;
		selGroup = animGroup.items[sel];
		curSelGroup = selGroup;
		PopulateAnimationList();
	)
	on animList selected sel do
	(
		local animInfo,animName;

		animGroup.items[sel]
		
		-- Determine if animation is defined for the selected object
		-- and populate the apropriate fields
		animName = animList.items[sel];
		curAnimName = animName;
		UpdateAnimationSelection(curAnimName);
		
	)

	on objectAnimList selected sel do
	(
		local animName = objectAnimList.items[sel];
		local animIdx = finditem animList.items animName;
		animList.selection = animIdx;
		curAnimName = animName;
		UpdateAnimationSelection(curAnimName);
	)
	
	on objectSelect pressed do
	(
		curSelNodeList = getCurrentSelection();
		curSelNodeRoot = undefined;
		if curSelNodeList.count != 0 do 
		(
		    SetSelection(curSelNodeList);
		)
	
		-- Populate the animation list dialog and dependant components
		PopulateAnimationList();
		UpdateAnimationSelection(curAnimName);
		PopulateObjectAnimationList();
		
		-- Update the dialog settings
		UpdateDialogState(curAnimInfo);
	)
	
	on createAnim pressed do
	(
	    CreateNewAnimation();
		PopulateObjectAnimationList();
	)
	on deleteAnim pressed do
	(
		local CurSelNode;
		local i = 1;
		for i = 1 to curSelNodeList.count do
		(
			CurSelNode = curSelNodeList[i];
			DeleteAnimation CurSelNode curAnimName;
		)
		UpdateAnimationSelection(curAnimName);
		PopulateObjectAnimationList();
	)
	on rotateCreate pressed do
	(
		local rotXStart = (rotateStartX.text) as integer;
		local rotYStart = (rotateStartY.text) as integer;
		local rotZStart = (rotateStartZ.text) as integer;
		local rotXEnd = (rotateEndX.text) as integer;
		local rotYEnd = (rotateEndY.text) as integer;
		local rotZEnd = (rotateEndZ.text) as integer;
	
		if RotateX.state == false do (rotateXStart = 0;rotateXEnd = 0;)
		if RotateY.state == false do (rotateYStart = 0;rotateYEnd = 0;)
		if RotateZ.state == false do (rotateZStart = 0;rotateZEnd = 0;)
		
		local CurSelNode;
		local i = 1;
		for i = 1 to curSelNodeList.count do
		(
			CurSelNode = curSelNodeList[i];
			CreateRotation CurSelNode curAnimName rotXStart rotXEnd rotYStart rotYEnd rotZStart rotZEnd;
		)
	)
	on markerList selected sel do
	(
		UpdateDialogState(curAnimInfo);
	)
	on addMarker pressed do
	(
		-- Creare new marker
		CreateMarker 0 ("NEW_MARKER_" + ((random 1 1000) as string)) "MARKER";
		
		-- Repopulate the marker list
		PopulateMarkerList(curAnimInfo);
		ReselectMarker();
	)
	on addEvent pressed do
	(
		-- Creare new marker
		CreateEvent 0 ("NEW_EVENT_" + ((random 1 1000) as string)) "VFX0" "Param"
		
		-- Repopulate the marker list
		PopulateMarkerList(curAnimInfo);
		ReselectMarker();
	)
	on deleteMarker pressed do
	(
		DeleteMarkerByID(selectedMarker);
		PopulateMarkerList(curAnimInfo);
		UpdateDialogState(curAnimInfo);
	)
	on markerIDEdit entered text do
	(
		-- Find the marker and update its time
		local marker = FindMarkerByID(selectedMarker);
		if marker != undefined do
		(
			local markerToken = TokenizeNoteKey marker;
			local newValue = CreateMarkerValue text (Marker_ExtractType(markerToken)) (Marker_ExtractName(markerToken)) (Marker_ExtractParam(markerToken));
			marker.value = newValue;
		)
			
		-- Repopulate the marker list
		PopulateMarkerList(curAnimInfo);
		
		-- Move the selection to match the change
		selectedMarker = text;
		ReselectMarker();
		PopulateMarkerInfo(marker);
	)
	on frameSpinner changed val do
	(
		if curAnimInfo != undefined do
		(
			print ("New Val = " + (val as string));
			-- Determine the requested time
			local markerTime = 0f;
			markerTime.frame = (val as integer);
			print markerTime;
			
			-- Need to go through and find the first free time slot there
			local ntrack = GetNoteTrackInfo curSelNodeList[1];
			markerTime = FindKeyTimeSlot ntrack.keys markerTime;
			print markerTime;
			
			-- Get the actual marker and update its time value
			local marker = FindMarkerByID(selectedMarker);
			if marker != undefined do
				marker.time = markerTime;
				
			-- Update the dialog...
			marker = FindMarkerByID(selectedMarker);
			PopulateMarkerList(curAnimInfo);
			ReselectMarker();
			PopulateMarkerInfo(marker);
			print ntrack.keys;
		)
	)
	on markerName entered text do
	(
		-- Find the marker and update its time
		local marker = FindMarkerByID(selectedMarker);
		if marker != undefined do
		(
			local markerToken = TokenizeNoteKey marker;
			local newValue = CreateMarkerValue (Marker_ExtractID(markerToken)) (Marker_ExtractType(markerToken)) text (Marker_ExtractParam(markerToken));
			marker.value = newValue;
		)
			
		PopulateMarkerInfo(marker);
	)
	on markerParam entered text do
	(
		-- Find the marker and update its time
		local marker = FindMarkerByID(selectedMarker);
		if marker != undefined do
		(
			local markerToken = TokenizeNoteKey marker;
			local newValue = CreateMarkerValue (Marker_ExtractID(markerToken)) (Marker_ExtractType(markerToken)) (Marker_ExtractName(markerToken)) text;
			marker.value = newValue;
		)
			
		PopulateMarkerInfo(marker);
	)
	on convertAmbient pressed do
	(
		CreateKeyRecursivePrefix "tick18_" undefined true curSelNodeRoot curSelNodeList[1] animationrange.start ("ANIM_START = \"Ambient\"");
		CreateKeyRecursivePrefix "tick18_" undefined false curSelNodeRoot curSelNodeList[1] animationrange.end ("ANIM_END = \"Ambient\"");
	)
	on convertFS9 pressed do
	(
		local nodeList;
		nodeList = rootAnimDef.SelectNodes("/ModelInfo/Animation[@type=\"Sim\"]");
		if nodeList != undefined do 
		(
			for i = 1 to nodeList.count do
			(
				local name,animNode,animLengthStr;
				local animLength = 100f;
				animNode = nodeList[i];
				name = animNode.GetAttributeValue("name");
				
				animLengthStr = animNode.GetAttributeValue("length");
				if animLengthStr != "" do
				(
				    animLength = ((animLengthStr as float) as time);
				)
	
	            print ("Converting: " + name + "  Length=" + (animLength as string));
			    CreateKeyRecursivePrefix name undefined true curSelNodeRoot curSelNodeList[1] 0f ("ANIM_START = \""+name+"\"");
			    CreateKeyRecursivePrefix name undefined false curSelNodeRoot curSelNodeList[1] animLength ("ANIM_END = \""+name+"\"");
			)
		)
	)
	fn EnumerateRecursive curNode animStrList =
	(
		if curNode != undefined do
		(
			local animStrList;
			animStrList = #();
			append animStrList curNode.name;
	
			-- Spew all related animations
			local animInfo;
			local ntrack = GetNoteTrackInfo curNode;
			if (ntrack != undefined) do
			(
				local nkeys = ntrack.keys;
				local i = 1;
				while nkeys[i] != undefined do
				(
					local nkey = nkeys[i];
					local keyTokens = TokenizeNoteKey nkey;
					if (keyTokens[5] != undefined) do
					(
						if (keyTokens[5][1] == "ANIM_START") do
						(
							local anim =  keyTokens[5][2];
							print anim;
							animInfo = GetAnimationInformation curNode anim;
							append animStrList ("    ANIM: "+anim+" (Start = "+((animInfo[2].frame) as string) + 
												"  End = "+((animInfo[3].frame) as string)+")");
						)
					)
					i = i + 1;
				)
			)
	
			-- Recurse
			local child = curNode.children;
			for i = 1 to child.count do
				join animStrList (EnumerateRecursive child[i] animStrList);
				
			return animStrList;
		)
	)

	fn updateSummary =
	(
		local animStrList;
		animStrList = #();
		if (curSelNodeRoot != undefined) then
		(
			summaryRollout.lbxSummary.items = (EnumerateRecursive curSelNodeRoot animStrList);
		)
		else
		(
			summaryRollout.lbxSummary.items = (EnumerateRecursive rootNode animStrList);
		)
	)
	
	on btnSummary pressed  do
	(
		summaryRollout.open = true
		updateSummary();
	)
)

fn StartFSXAnimationEditor = 
(
	AnimationRollout = newRolloutFloater "FSX Animation Tool" 600 455
	addRollout AnimationDLG AnimationRollout
	-- CreateDialog AnimationDLG width:580 height:400
	
	addRollout summaryRollout AnimationRollout rolledUp:true
	AnimationDLG.updateSummary();
)
