r2.Version = {}

local version = r2.Version


function r2.Version.checkVersion(scenarioVersionList, currentVersionList)
	local undef = {}
	local older = {}
	local newer = {}
	local ok = true

	local k,v = next(scenarioVersionList, nil)
	while k do
		if (v ~= currentVersionList[k]) then
			ok = false
			if currentVersionList[k] == nil then
				table.insert(undef, k)
			elseif v < currentVersionList[k] then
				table.insert(older, k)
			else
				table.insert(newer, k)
			end
		end
		k,v = next(scenarioVersionList, k)
	end
	return ok, undef, older, newer
	
end

function r2.Version.getUndefComponent(scenarioVersionList, currentVersionList)
	local undef = {}
	local k,v = next(scenarioVersionList, nil)
	while k do
		if (v ~= currentVersionList[k] and currentVersionList[k] == nil ) then
			table.insert(undef, k)
		end
		k,v = next(scenarioVersionList, k)
	end
	return true
	
end

local levelToString = 
{
	[0] = "20",
	[1] = "50",
	[2] = "100",
	[3] = "150",
	[4] = "200",
	[5] = "250"
}

function r2.Version.save(filename)

	local scenario = r2.Scenario

	if scenario then
		local scenarioList = r2.Version.getCurrentVersionList()
		local update = false

		local k,v = next(scenarioList, nil)
		while k do
			if (r2.Scenario.Versions[k] ~= v) then update = true end			
			k,v = next(scenarioList, k)
		end

		k,v = next(r2.Scenario.Versions, nil)
		while k do
			if (scenarioList[k] ~= v) then update = true end			
			k,v = next(r2.Scenario.Versions, k)
		end

		if update then
			r2.requestSetGhostNode(scenario.InstanceId, "Versions", scenarioList)		
		end
		local accessList = {}


		local ok, level, err = r2.RingAccess.verifyScenario()
		if not r2.RingAccess.LoadAnimation and not r2.getIsAnimationSession() then	
			r2.updateScenarioAck(ok, level, err.What)
		end
		accessList = r2.RingAccess.getAccessListAsString(level)
		

		local values = {}



		local date = os.date()

		local firstLocationName = "" 
		local shortDescription = ""
		local title = ""
		local name = ""
		local userName = r2:getUserEntityName()
		local modifierMD5 = r2.getCharIdMd5()
		local creatorName = ""
		local rules = ""
		local level = ""
		local language = ""
		local type = ""
		local creatorMD5 =""
		local createUserName = userName
		local createDate = date
		local otherCharAccess = "Full"
		local nevraxScenario = "0"
		local trialAllowed = "0"
		local scenarioTag = ""


		if r2.Scenario and r2.Scenario.Locations 
			and table.getn(r2.Scenario.Locations) > 0 and r2.Scenario.Locations[0].IslandName then
			firstLocationName = r2.Scenario.Locations[0].IslandName
		end

		if r2.Scenario and r2.Scenario.Description then
			--shortDescription =string.gsub(r2.Scenario.Description.ShortDescription, "\n", "\\n")
			shortDescription = r2.Scenario.Description.ShortDescription
			level = string.gsub(r2.Scenario.Description.LevelId, "\n", "\\n")
			level = levelToString[tonumber(level)]
			rules = string.gsub(r2.Scenario.AccessRules, "\n", "\\n")
			if rules=="liberal" then 
				rules=i18n.get("uiR2EDliberal"):toUtf8()
			elseif rules == "strict" then
				rules=i18n.get("uiR2EDstrict"):toUtf8()
			end
			title = string.gsub(r2.Scenario.Description.Title, "\n", "\\n")
			language = r2.Scenario.Language 
			type = r2.Scenario.Type  			
			name = r2.Scenario.Name
			if r2.Scenario.Ghost_Name then
				name = r2.Scenario.Ghost_Name
			end
			if r2.Scenario.Description.Creator then
				createUserName = r2.Scenario.Description.Creator 
			end
			if r2.Scenario.Description.CreatorMD5 then
				creatorMD5 = r2.Scenario.Description.CreatorMD5
			end
			if r2.Scenario.Description.CreationDate then
				createDate = r2.Scenario.Description.CreationDate 
			end			
			if table.getn(r2.Scenario.Locations) > 0 then
				initialIslandLocation = r2.Scenario.Locations[0].IslandName
				initialEntryPoint = r2.Scenario.Locations[0].EntryPoint
				initialSeason =  r2.Scenario.Locations[0].Season
			end

			if r2.Scenario.Description.OtherCharAccess then
				otherCharAccess = r2.Scenario.Description.OtherCharAccess
				if otherCharAccess == "RoSOnly" then
					if config.R2EDExtendedDebug ~= 1 then					
						if filename == "data/r2_buffer.dat" then
							return false
						else
							r2.onSystemMessageReceived("BC_ML", "", "uiR2EDErrorRefuseToSaveRoS")
							r2.requestNewAction(i18n.get("uiR2EDUpdatingScenarioToDefaultAccess"))
							r2.requestSetNode(r2.Scenario.Description.InstanceId, "OtherCharAccess", "Full")
							r2.requestEndAction()						
							return false
						end
					else
						r2.onSystemMessageReceived("BC_ML", "", "Updating the system of Trial limitation")
						r2.requestNewAction(i18n.get("uiR2EDUpdatingScenarioAccess"))
						r2.requestSetNode(r2.Scenario.Description.InstanceId, "OtherCharAccess", "Full")
						r2.requestSetNode(r2.Scenario.Description.InstanceId, "NevraxScenario", "1")
						r2.requestSetNode(r2.Scenario.Description.InstanceId, "TrialAllowed", "1")
						r2.requestSetNode(r2.Scenario.Description.InstanceId, "ScenarioTag", "")
						r2.requestEndAction()
						return false
					end


				end
			end

			if r2.Scenario.Description.NevraxScenario == tostring(1) then
				nevraxScenario = r2.Scenario.Description.NevraxScenario
				trialAllowed = r2.Scenario.Description.TrialAllowed
				scenarioTag = r2.Scenario.Description.ScenarioTag
				userName = "Ring(Nevrax)"
				createUserName = userName

				if config.R2EDExtendedDebug ~= 1  then
					
					if filename == "data/r2_buffer.dat" then
						return false
					else
						r2.onSystemMessageReceived("BC_ML", "", "uiR2EDErrorRefuseToSaveRoS")
						r2.requestNewAction(i18n.get("uiR2EDUpdatingScenarioAccess"))
						r2.requestSetNode(r2.Scenario.Description.InstanceId, "NevraxScenario", "0")
						r2.requestSetNode(r2.Scenario.Description.InstanceId, "TrialAllowed", "0")
						r2.requestSetNode(r2.Scenario.Description.InstanceId, "ScenarioTag", "")
						r2.requestEndAction()
						
						return false
					end


				end
			end

		end

		table.insert(values, {Title = title})
		table.insert(values, {Name = name})
		table.insert(values, {ShortDescription = shortDescription})
		table.insert(values, {FirstLocation = firstLocationName})
		table.insert(values, {RingPointLevel = accessList})
		table.insert(values, {CreateBy = createUserName})
		table.insert(values, {CreatorMD5 = creatorMD5})
		table.insert(values, {CreationDate = createDate})
		table.insert(values, {ModifiedBy = userName})
		table.insert(values, {ModifierMD5 = modifierMD5})
		table.insert(values, {OtherCharAccess = otherCharAccess})

		table.insert(values, {ModificationDate = date})
		table.insert(values, {Rules = rules})
		table.insert(values, {Level = level})
		table.insert(values, {Type = type})
		table.insert(values, {Language = language})
		table.insert(values, {InitialIsland = initialIslandLocation})
		table.insert(values, {InitialEntryPoint = initialEntryPoint})
		table.insert(values, {InitialSeason = initialSeason})
		if nevraxScenario == tostring(1) then
			table.insert(values, {NevraxScenario = nevraxScenario})
			table.insert(values, {TrialAllowed = trialAllowed})
			table.insert(values, {ScenarioTag = scenarioTag})
		end

		
		r2.save(filename, values)
	end
	return true
end


local function updateVersionImpl(nodeList, scenarioVersionList, currentVersionFullList)
	assert(nodeList)
	assert(scenarioVersionList)
	assert(currentVersionFullList)

	local k,v = next(nodeList, nil)
	while k do


		-- go up in the class hierarchy, calling each redefinition of 'onUpdate' function			
		-- when a version change is detected
		local currClassName = v.Class

		-- look at a update function for this component or one of it's ancester
		while currClassName ~= "" and currClassName ~= nil do				
			local currClass = r2.Classes[currClassName]
			if not currClass then
				debugInfo(colorTag(255, 0, 0) .. "Error can not update your scenario: the component"..currClassName.." seems to be obsolete")
				return false
			end
			local scenarioVersionNode = defaulting(scenarioVersionList[currClassName], 0)
			local currentVersionNode = defaulting(currentVersionFullList[currClassName], 0)
			if currentVersionNode ~= scenarioVersionNode then
				--
				if scenarioVersionNode == nil then
					debugInfo(colorTag(0, 255 ,255) .. "The component  (".. v.Class .. ") does not exist anymore")
					return false
				elseif currentVersionNode == nil then
					debugInfo(colorTag(0, 255 ,255) .. "The component  (".. v.Class .. ") does not exist anymore")
					return false
				else
					debugInfo(colorTag(0, 255 ,255) .. "Updating the component  " .. v.InstanceId .. "(".. v.Class .. "/"..currClassName..") from "..  scenarioVersionNode .. " to " .. currentVersionNode ..".")
				
					local updateFunc = currClass.updateVersion
					if not updateFunc then
						debugInfo( "Your scenario can not be updated. Because the component " .. v.Class .. " can not be updated (no update Function?).\n")
						return false
					end
					local ok1, ok2 = pcall(updateFunc, v, scenarioVersionNode, currentVersionNode)
					if not ok1  or not ok2 then 
						debugInfo( "Your scenario can not be updated. Because the component " .. v.Class .. " can not be updated.\n")
						if not ok1 then 
							debugInfo(ok2)
						end
						return false
					end
				
				end
			end
			currClassName = currClass.BaseClass
		end
		
		k,v = next(nodeList, k)
	end
	return true

end

function r2.Version.updateVersion()
	if r2.Translator == nil then
		debugInfo("Syntax error")
		return false
	end
	local scenarioInstance = r2.Scenario

	if not scenarioInstance then return true end

	local currentVersionFullList = r2.Version.getCurrentVersionFullList()
	local scenarioVersionList = r2.Version.getScenarioVersionList()
	
	local versionOk, undef, older, newer = r2.Version.checkVersion(scenarioVersionList, currentVersionFullList)

	local modified = false
	if not versionOk then

		debugInfo(colorTag(0,255,25).."Begin Update")
		
		local scenarioVersionName = scenarioInstance.VersionName
		local currentVersionName = r2.Classes["Scenario"].VersionName

		if scenarioVersionName == nil then scenarioVersionName = "0.0.0" end
		if currentVersionName == nil then currentVersionName = "0.0.0" end
		
		local str = "Updating the scenario from '"..scenarioVersionName.."' to '".. currentVersionName .."'.\nThe obsolete scenario will be saved in 'old_scenario.r2'"
		printMsg(str)
		local nodeList = r2.Version.getScenarioNodes(scenarioInstance)
		assert(nodeList)

		do
			local k,v = next(undef, nil)
			while k do
				debugInfo(colorTag(0, 255 ,255) .. "The component ".. v .. " used in this scenario does not exist.")
				k,v = next(undef, k)
			end
		end

		do
			local k,v = next(older, nil)
			while k do
				debugInfo(colorTag(0, 255 ,255) .. "The component ".. v .. " is too old (we will try to update it)")
				k,v = next(older, k)
			end
		end

		do
			local k,v = next(newer, nil)
			while k do
				debugInfo(colorTag(0, 255 ,255) .. "The component ".. v .. " is too new (maybe wrong version?)")
				k,v = next(newer, k)
			end
		end
		if table.getn(undef) > 0 then
			debugInfo(colorTag(255, 0, 0) .. "Error can not update your scenario: the scenario containse components that are not defined")
			return true
		end
		local oldState = scenarioInstance.Ghost
		scenarioInstance.Ghost = false
		r2.save("old_scenario.r2")
		scenarioInstance = r2.Scenario

		scenarioInstance.Ghost = true

		local syntaxOk, ok = pcall(updateVersionImpl, nodeList, scenarioVersionList, currentVersionFullList)	
		if not syntaxOk then
			r2.print(ok)
			ok = false
		end
		if  ok then
		-- ok
			debugInfo(colorTag(0,255,25).."Update succced")
			local currentVersionList = r2.Version.getCurrentVersionList()
			r2.requestSetNode(scenarioInstance.InstanceId, "VersionName", currentVersionName)
			r2.requestSetNode(scenarioInstance.InstanceId, "Versions", currentVersionList)
			scenarioInstance.Ghost = oldState
			r2.requestUploadCurrentScenario()
			r2.clearActionHistoric() -- undo invalidated after a version update !!	
			modified = true
		else
			debugInfo(colorTag(255, 0, 0) .. "Errors occurs while updateing your scenario")
		
		end
		scenarioInstance.Ghost = oldState
		debugInfo(colorTag(0,255,25).."End Update")

	end	
	return not modified;
end




-- return Scenario Nodes - leaf first
function r2.Version.getScenarioNodes(node, nodeListParam)
	assert(node)
	if nodeListParam then nodeList = nodeListParam else nodeList = {} end
	
	if  not r2.isTable(node)  then return nodeList end	
	local k,v = next(node, nil)
	while k do
		r2.Version.getScenarioNodes(v, nodeList)		
		k,v = next(node, k)
	end
	if node.InstanceId then
		table.insert(nodeList, node)
		if not node.Class then assert(nil) end
	end
	return nodeList
end


-- get the Current Version of sceanrio
function r2.Version.getCurrentVersionList()
	local scenarionInstance = r2.Scenario
	local versionList = {}
	r2.Version._getCurrentVersionListImpl(scenarionInstance, versionList)
	return versionList;
end

function r2.Version.getCurrentVersionFullList()
	local versionList = {}
	local classes = r2.Classes
	local k, v = next(classes, nil)
	while k do
		if v.Version then 
			versionList[k] = v.Version 
		else
			versionList[k] = 0
		end
		k, v = next(classes, k)
	end
	return versionList
end

function r2.Version._getCurrentVersionListImpl(node, versionList)
	if ( type (node) == "string" or type(node) == "number")  then return end
	local k,v = next(node, nil)
	if node.Class and not versionList[node.Class] then
		if r2.Classes[node.Class] and r2.Classes[node.Class].Version then
			versionList[node.Class] = r2.Classes[node.Class].Version
		else
			versionList[node.Class] = 0
		end
	end
	while k do
		r2.Version._getCurrentVersionListImpl(v, versionList);
		k,v = next(node, k)
	end
end


function r2.Version.getScenarioVersionList(scenarioNode)
	local scenario = scenarioNode
	if not scenario then scenario = r2.Scenario end
	assert(scenario)
	local versions = scenario.Versions
	local versionList = {}
	if versions == nil then
		return r2.Version._getScenarioVersionListImpl(scenario, versionList)
	else
		local k,v = next(versions, nil)
		while k do
			versionList[k] = v
			k,v = next(versions, k)
		end
	end
	return versionList	

end

function r2.Version._getScenarioVersionListImpl(node, versionList)
	if ( type (node) == "string" or type(node) == "number")  then return end
	local k,v = next(node, nil)
	if node.Class and not versionList[node.Class] then
		versionList[node.Class] = 0
	end	
	while k do
		r2.Version._getScenarioVersionListImpl(v, versionList);
		k,v = next(node, k)
	end
	return versionList
end

-- Version 0.0.0 -> prior to 7 dec 2005

-- version 0.0.1 prior to 7 dec 2005
-- Act.Version = 1
-- Act.ManualWeather
-- Act.WeatherValue

-- version 0.0.2 19 dec 2005
-- Act.Version = 2
-- Act.Behavior (Because Act changes type from BaseType to LogicAction
--
-- version 0.0.3 1 janv 2006
-- ActivityStep.Version=1
-- "Inactive" -> "Stand Still"