--
-- VariableBody
-- This is a specialization for trailers, which have a extra bodywork for loading chaff/grass?
-- 
--
-- @author:		fruktor (wwww.modding-society.de)
-- @version:	v0.1
-- @date:		08/12/10
-- @history:	v0.1 - inital implementation
--
-- Copyright (C) 
--

VariableBody = {};

function VariableBody.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Trailer, specializations) and 
		SpecializationUtil.hasSpecialization(Attachable, specializations) and
		SpecializationUtil.hasSpecialization(Fillable, specializations) 
end;

function VariableBody:load(xmlFile)
	
	self.changeVariableBody = SpecializationUtil.callSpecializationsFunction("changeVariableBody");
	
	--get VariableBody attributes
	self.varBody = {};

	local activeIdx = getXMLInt(xmlFile, "vehicle.variableBody#activeIdx");
	if activeIdx ~= nil then
		self.varBody.activeIdx = activeIdx;
	else
		self.varBody.activeIdx = 1;
	end;
	
	local activeIdx = getXMLInt(xmlFile, "vehicle.variableBody#activeSubIdx");
	if activeIdx ~= nil then
		self.varBody.activeSubIdx = activeSubIdx;
	else
		self.varBody.activeSubIdx = 1;
	end;

	self.varBody.nodes = {};
	local i=0;
	local j=0;
	while true do

		local path = string.format("vehicle.variableBody.node(%d)", i);
	
		local name = getXMLString(xmlFile, path .. "#name");
		if name == nil then
			break;
		end;
		local capacity = getXMLInt(xmlFile, path .. "#capacity");
		--print("VariableBody:load(). name="..name.." cap="..capacity);
	
		--####
		local fillTypes = {};
		local fruitTypes = getXMLString(xmlFile, path .. "#fruitTypes");
		if fruitTypes ~= nil then
			local types = Utils.splitString(" ", fruitTypes);
			for k,v in pairs(types) do
				--print(v);
				local fillType = Fillable.fillTypeNameToInt[v];
				if fillType ~= nil then
					fillTypes[fillType] = true;
				else
					print("Warning: '"..self.configFileName.. "' has invalid fillType '"..v.."'.");
				end;
			end;
		end;

		--#### 
		local parts = {};
		
		j=0;
		local k=1;
		local str = getXMLString(xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#index%d", i, j, k));
		
		--[[if str == nil and string.match("roof", name) then
			local subparts = {};
			subparts.allowFill = 1;
			subparts.objects = {};
			subparts.subPartCount = 0;
			table.insert(parts, subparts);
			j=j+1;
		else]]--		
			while str ~= nil do
				
				local subparts = {};
				
				local allowFill = getXMLInt(xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#allowFill", i, j));
				if allowFill ~= nil then
					subparts.allowFill = allowFill;
				else
					subparts.allowFill = 1;
				end;			
				
				--local objects = {};
				subparts.objects = {};
				while str ~= nil do 
					local tmpIdx = Utils.indexToObject(self.components, str)
					--print("------------>str="..str.."   tmpIdx="..tmpIdx);
					k=k+1;
					table.insert(subparts.objects, tmpIdx);
					str = getXMLString(xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#index%d", i, j, k));
				end;
				--table.insert(, objects);
				
				subparts.subPartCount = k-1;
				if subparts ~= nil then
					table.insert(parts, subparts);
				else	
					break;
				end;
				j=j+1;
				k=1;
				str = getXMLString(xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#index%d", i, j, k));
				
			end;
		--end;
		
		if j == 0 then
			local subparts = {};
			subparts.objects = nil;
			subparts.allowFill = 1;
			subparts.subPartCount = 0;
			table.insert(parts, subparts);
		end;

		
		--####
		
		fillPlanes = {};
        k = 0;
		while true do
			local key = string.format("vehicle.variableBody.node(%d).fillPlanes.fillPlane(%d)", i, k);
			if not hasXMLProperty(xmlFile, key) then
				--print("k="..k);
				break;
			end;
			local fillPlane = {};
            fillPlane.nodes = {};
            local fillType = getXMLString(xmlFile, key.."#type");
            if fillType ~= nil then
                local nodeI = 0;
                while true do
                    local nodeKey = key..string.format(".node(%d)", nodeI);
					if not hasXMLProperty(xmlFile, nodeKey) then
                         break;
                    end;
					local node = Utils.indexToObject(self.components, getXMLString(xmlFile, nodeKey.."#index"));
                    if node ~= nil then
						local defaultX, defaultY, defaultZ = getTranslation(node);
						local defaultRX, defaultRY, defaultRZ = getRotation(node);
                        setVisibility(node, false);

                        local animCurve = AnimCurve:new(linearInterpolatorTransRotScale);
                        local keyI = 0;
                        while true do
							local animKey = nodeKey..string.format(".key(%d)", keyI);
                            local keyTime = getXMLFloat(xmlFile, animKey.."#time");
                            local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#translation"));
                            if y == nil then
								y = getXMLFloat(xmlFile, animKey.."#y");
							end;
							local rx,ry,rz = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#rotation"));
							local sx,sy,sz = Utils.getVectorFromString(getXMLString(xmlFile, animKey.."#scale"));
							if keyTime == nil then
								break;
							end;
                            local x = Utils.getNoNil(x, defaultX);
							local y = Utils.getNoNil(y, defaultY);
							local z = Utils.getNoNil(z, defaultZ);
                            local rx = Utils.getNoNil(rx, defaultRX);
                            local ry = Utils.getNoNil(ry, defaultRY);
                            local rz = Utils.getNoNil(rz, defaultRZ);
                            local sx = Utils.getNoNil(sx, 1);
                            local sy = Utils.getNoNil(sy, 1);
                            local sz = Utils.getNoNil(sz, 1);
                            animCurve:addKeyframe({x=x, y=y, z=z, rx=rx, ry=ry, rz=rz, sx=sx, sy=sy, sz=sz, time = keyTime});
                            keyI = keyI +1;
						end;
						if keyI == 0 then
							local minY, maxY = Utils.getVectorFromString(getXMLString(xmlFile, nodeKey.."#minMaxY"));
							local minY = Utils.getNoNil(minY, defaultY);
							local maxY = Utils.getNoNil(maxY, defaultY);
							animCurve:addKeyframe({x=defaultX, y=minY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=1, sy=1, sz=1, time = 0});
							animCurve:addKeyframe({x=defaultX, y=maxY, z=defaultZ, rx=defaultRX, ry=defaultRY, rz=defaultRZ, sx=1, sy=1, sz=1, time = 1});
						end;
						table.insert(fillPlane.nodes, {node=node, animCurve = animCurve});
					end;
					nodeI = nodeI +1;
				end;
				fillPlanes[fillType] = fillPlane;
			end;
			k = k +1;
			
		end;
		
		--####
		local entry={};
		entry.name = name;
		entry.capacity = capacity;
		entry.index = index; 
		entry.parts = parts;
		entry.partCount = j;
		entry.fillTypes = fillTypes;
		entry.fillPlanes = fillPlanes;
		table.insert(self.varBody.nodes, entry);
		i=i+1;
	end;
	--print("i="..i);
	
	if self.varBody.activeIdx > i then
		self.varBody.activeIdx = i;
	end;
	self.varBody.nodeCount=table.getn(self.varBody.nodes);
	
	self.varBody.origFillPlanes = {};
	for i,j in pairs(self.fillPlanes) do
		self.varBody.origFillPlanes[i] = j;
	end;
	self.previousFillLevel = self.fillLevel;	
	-- self:changeVariableBody(self.varBody.activeIdx, self.varBody.activeSubIdx, true);

end;

function VariableBody:readStream(streamId, connection)
	local state = streamReadInt8(streamId);
	local state2 = streamReadInt8(streamId);
	self:changeVariableBody(state, state2, true);
end;

function VariableBody:writeStream(streamId, connection)
	streamWriteInt8(streamId, self.varBody.activeIdx);
	streamWriteInt8(streamId, self.varBody.activeSubIdx);
end;

function VariableBody:delete()
end;

function VariableBody:mouseEvent(posX, posY, isDown, isUp, button)
end;

function VariableBody:keyEvent(unicode, sym, modifier, isDown)
end;

function VariableBody:update(dt)
	-- if self:getIsActive() then	
		-- self:changeVariableBody(self.varBody.activeIdx, self.varBody.activeSubIdx, true);
	-- end;
	if self.varTip ~= nil then
		if self.varTip.activeTrailerIdx ~= self.varTip.trailerNr then
			return;
		end;
	end;
	

	if self:getIsActive() then
		if self:getIsActiveForInput() then
			if self.tipState == Trailer.TIPSTATE_CLOSED then
				--and self.varBody.nodeCount > 1 then
			
				if self.fillLevel == 0 then
					if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
						if self.fillLevel == 0 then
							local idx = self.varBody.activeIdx + 1;
							if idx > self.varBody.nodeCount then
								idx = 1
							end;
							local idx2 = self.varBody.activeSubIdx;
							if idx2 > self.varBody.nodes[idx].partCount then
								idx2 = 1;
							end
							self:changeVariableBody(idx, idx2);
						end;
					end;
				end;
				
				if string.match("roof", self.varBody.nodes[self.varBody.activeIdx].name) then 
					if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA3) then
						local idx = self.varBody.activeSubIdx + 1;
						if idx > self.varBody.nodes[self.varBody.activeIdx].partCount then
							idx = 1
						end;
						self:changeVariableBody(self.varBody.activeIdx, idx);
					end;
				end;
			else
				--print("trailer not closed");
			end;				
		end;
	end;

		
end;

function VariableBody:updateTick(dt)
	if self:getIsActive() then
		if self.fillLevel <= 0 then
			 self.fillLevel = 0;
			 self.currentFillType = Fillable.FILLTYPE_UNKNOWN;
              for _, node in ipairs(self.currentFillPlane.nodes) do
                  setVisibility(node.node, false);
              end;			 
		end;
		self.previousFillLevel = self.fillLevel;		
	end;
end;

function VariableBody:draw()

	if self.varTip ~= nil then
		if self.varTip.activeTrailerIdx ~= self.varTip.trailerNr then
			return;
		end;
	end;

	if self.tipState == Trailer.TIPSTATE_CLOSED then
			--self.varBody.nodeCount> 1 then
			
		if self.fillLevel == 0 then
			if self.varBody.nodeCount> 1 then
				g_currentMission:addHelpButtonText( 
					string.format(g_i18n:getText("VARIABLEBODY_display"),
					self.varBody.activeIdx, 
					self.varBody.nodeCount ),
					InputBinding.IMPLEMENT_EXTRA2 );
			end;
		end;
		
		if string.match("roof", self.varBody.nodes[self.varBody.activeIdx].name) then 
			g_currentMission:addHelpButtonText( 
			g_i18n:getText("VARIABLEBODY_displayPart"), InputBinding.IMPLEMENT_EXTRA3 ) ;					
		end;
		
	end;

end;

function VariableBody:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
    if self.synchronizeFillLevel then
		local idx = Utils.getNoNil(getXMLInt(xmlFile,key.."#varBody.activeIdx"),1);
		if idx > table.getn(self.varBody.nodes) then
			idx = 1;
		end;
		local idx2 = Utils.getNoNil(getXMLInt(xmlFile,key.."#varBody.activeSubIdx"),1);
		if idx2 > table.getn(self.varBody.nodes[idx].parts) then
			idx2 = 1;
		end;
        self.fillLevel = Utils.getNoNil(getXMLFloat(xmlFile, key .."#fillLevel"));	
		self.fillType = Utils.getNoNil(getXMLString(xmlFile, key.."#fillType"));	
		self:changeVariableBody(idx, idx2, true);
	end;
    return BaseMission.VEHICLE_LOAD_OK;
end;

function VariableBody:getSaveAttributesAndNodes(nodeIdent)
	local attributes = ' varBody.activeIdx="'..tostring(self.varBody.activeIdx)..'" varBody.activeSubIdx="'..tostring(self.varBody.activeSubIdx)..'"';
    local node = nil;
    return attributes, node;
end;

function VariableBody:changeVariableBody(state, state2, noEventSend)

	VariableBodyEvent.sendEvent(self, state, state2, noEventSend);
	
	for i=1, table.getn(self.varBody.nodes) do 
		if self.varBody.nodes[i].partCount > 0 then
			for j=1, table.getn(self.varBody.nodes[i].parts) do 
				if i ~= state or j ~= state2 then
					for k=1,table.getn(self.varBody.nodes[i].parts[j].objects) do
						setVisibility(self.varBody.nodes[i].parts[j].objects[k], false);
					end;
				end
			end;
		end;
	end;	
	
	if self.varBody.nodes[state] ~= nil then
		if self.varBody.nodes[state].partCount > 0 then
			if self.varBody.nodes[state].parts[state2].subPartCount > 0 then
				for k=1,table.getn(self.varBody.nodes[state].parts[state2].objects) do
					setVisibility(self.varBody.nodes[state].parts[state2].objects[k], true);
				end;	
			end;
		end;
	end;

	--if state ~= self.varBody.activeIdx then
		for i,j in pairs(self.fillTypes) do
			if self.varBody.nodes[state].fillTypes[i] == true then
				self.fillTypes[i] = true;
			else
				self.fillTypes[i] = false;
			end;
		end;
		--self:setFillLevel(self.fillLevel, self.currentFillType);
		
		for i,j in pairs(self.fillPlanes) do
			if self.varBody.nodes[state].fillPlanes[i] ~= nil then
				self.fillPlanes[i] = self.varBody.nodes[state].fillPlanes[i];
			else
				self.fillPlanes[i] = self.varBody.origFillPlanes[i];
			end;
		end;
		---self:setFillLevel(self.fillLevel, self.currentFillType);
	--end;
	
	if self.varBody.nodes[state].parts[state2].allowFill == 1 then
		self.allowFillFromAir = true;
	else
		self.allowFillFromAir = false;
	end;
	
	self.capacity = self.varBody.nodes[state].capacity;
	self:setFillLevel(self.fillLevel, self.currentFillType);

	self.varBody.activeIdx = state;
	self.varBody.activeSubIdx = state2;	
	-- print("function called  ", state, state2, self.capacity);
end;

function VariableBody:onAttach(attacherVehicle)
	if self.isServer then
		self:changeVariableBody(self.varBody.activeIdx, self.varBody.activeSubIdx, true);
	end;
end;

VariableBodyEvent = {};
VariableBodyEvent_mt = Class(VariableBodyEvent, Event);

InitEventClass(VariableBodyEvent, "VariableBodyEvent");

function VariableBodyEvent:emptyNew()
    local self = Event:new(VariableBodyEvent_mt);
    self.className="VariableBodyEvent";
    return self;
end;

function VariableBodyEvent:new(vehicle, state, state2)
    local self = VariableBodyEvent:emptyNew()
    self.vehicle = vehicle;
	self.state = state;
	self.state2 = state2;
    return self;
end;

function VariableBodyEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
	self.state = streamReadInt8(streamId);
	self.state2 = streamReadInt8(streamId);
    self.vehicle = networkGetObject(id);
    self:run(connection);
end;

function VariableBodyEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
	streamWriteInt8(streamId, self.state);
	streamWriteInt8(streamId, self.state2);
end;

function VariableBodyEvent:run(connection)
	--print("VariableBodyEvent:run(connection)");
    self.vehicle:changeVariableBody(self.state, self.state2, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(VariableBodyEvent:new(self.vehicle, self.state, self.state2), nil, connection, self.vehicle);
    end;
end;

function VariableBodyEvent.sendEvent(vehicle, state, state2, noEventSend)
	--print("VariableBodyEvent.sendEvent(vehicle, state, noEventSend)");
	if vehicle == nil then
		print("--------> VariableBody erRoR :/ ");
	end;
    if state ~= vehicle.varBody.activeIdx or state2 ~= vehicle.varBody.activeSubIdx then
	    if noEventSend == nil or noEventSend == false then
            if g_server ~= nil then
                g_server:broadcastEvent(VariableBodyEvent:new(vehicle, state, state2), nil, nil, vehicle);
            else
                g_client:getServerConnection():sendEvent(VariableBodyEvent:new(vehicle, state, state2));
            end;
        end;
    end;
end;

