--
-- Cultivator
-- Class for all Cultivators
-- 
-- @author  Stefan Geiger
-- @date  04/02/08
--
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.

Wiesenwalze = {};

function Wiesenwalze.prerequisitesPresent(specializations)
    return true;
end;

function Wiesenwalze:load(xmlFile)
	self.setNewSupportAnimationState = SpecializationUtil.callSpecializationsFunction("setNewSupportAnimationState"); 

    self.groundContactReport = SpecializationUtil.callSpecializationsFunction("groundContactReport");

    self.contactReportNodes = {};
    local contactReportNodeFound = false;
    local i=0;
    while true do
        local baseName = string.format("vehicle.contactReportNodes.contactReportNode(%d)", i);
        local index = getXMLString(xmlFile, baseName.. "#index");
        if index == nil then
            break;
        end;
        local node = Utils.indexToObject(self.components, index);
        if node ~= nil then
            local entry = {};
            entry.node = node;
            entry.hasGroundContact = false;

            self.contactReportNodes[node] = entry;
            contactReportNodeFound = true;
        end;
        i = i+1;
    end;
    if not contactReportNodeFound then
        local entry = {};
        entry.node = self.components[1].node;
        entry.hasGroundContact = false;
        self.contactReportNodes[entry.node] = entry;
    end;

    self.groundReferenceThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.groundReferenceNode#threshold"), 10);
    self.groundReferenceNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.groundReferenceNode#index"));

    local numCuttingAreas = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.cuttingAreas#count"), 0);
    for i=1, numCuttingAreas do
        local areanamei = string.format("vehicle.cuttingAreas.cuttingArea%d", i);
        self.cuttingAreas[i].foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, areanamei .. "#foldMinLimit"), 0);
        self.cuttingAreas[i].foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, areanamei .. "#foldMaxLimit"), 1);
    end;


    self.groundParticleSystems = {};
    local psName = "vehicle.groundParticleSystem";
    Utils.loadParticleSystem(xmlFile, self.groundParticleSystems, psName, self.components, false, nil, self.baseDirectory)

    self.newGroundParticleSystems = {};
    local i=0;
    while true do
        local baseName = string.format("vehicle.groundParticleSystems.groundParticleSystem(%d)", i);
        if not hasXMLProperty(xmlFile, baseName) then
            break;
        end;
        local entry = {};
        entry.ps = {};
        Utils.loadParticleSystem(xmlFile, entry.ps, baseName, self.components, false, nil, self.baseDirectory);
        if table.getn(entry.ps) > 0 then
            entry.isActive = false;
            table.insert(self.newGroundParticleSystems, entry);
        end;
        i = i+1;
    end;

    self.aiTerrainDetailChannel1 = g_currentMission.ploughChannel;
    self.aiTerrainDetailChannel2 = g_currentMission.sowingChannel;

    self.maxSpeedLevel = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.maxSpeedLevel#value"), 1);
    self.speedViolationMaxTime = 2500;
    self.speedViolationTimer = self.speedViolationMaxTime;
    self.cultivatorContactReportsActive = false;
    self.startActivationTimeout = 2000;
    self.startActivationTime = 0;

    self.cultivatorHasGroundContact = false;

    self.cultivatorLimitToField = false;
    self.cultivatorForceLimitToField = true;

    self.cultivatorGroundContactFlag = self.nextDirtyFlag;
    self.nextDirtyFlag = self.cultivatorGroundContactFlag*2;

    self.cuttingAreasSend = {};
	
	
	self.showPS = false;
	self.particleSystemSchaber = {};
    local i = 0;
    while true do
        local system1i = string.format("vehicle.particleSystemSchaber.system(%d)", i);
		local index1i = Utils.indexToObject(self.components, getXMLString(xmlFile, system1i .. "#index"));
		if index1i == nil then
			break;
		end; 
        Utils.loadParticleSystem(xmlFile, self.particleSystemSchaber, system1i, index1i, false, nil, self.baseDirectory)		
		i = i +1;		
    end;
    self.startIndexSpray = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.DuengeArea#startIndex"));
    self.widthIndexSpray = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.DuengeArea#widthIndex"));
    self.heightIndexSpray = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.DuengeArea#heightIndex"));
	
    local newSupportAnimation = {};
    newSupportAnimation.name = getXMLString(xmlFile, "vehicle.newSupportAnimation#name");
    newSupportAnimation.openSpeedScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.newSupportAnimation#openSpeedScale"), 1);
    newSupportAnimation.closeSpeedScale = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.newSupportAnimation#closeSpeedScale"), -newSupportAnimation.openSpeedScale);
	if newSupportAnimation.name ~= nil then
        self.newSupportAnimation = newSupportAnimation;
    end;  
    self.supportPlayerPoint = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.newSupportAnimation#supportPlayerPoint"));
	self.newSupportAnimationState = false;
	self.playerInRange = false;
	self.automaticSupport = false;
end;

function Wiesenwalze:delete()

    Utils.deleteParticleSystem(self.groundParticleSystems);
    
    for _, entry in ipairs(self.newGroundParticleSystems) do
        Utils.deleteParticleSystem(entry.ps);
    end;
    
    Wiesenwalze.removeContactReports(self);
end;

function Wiesenwalze:readUpdateStream(streamId, timestamp, connection)
    if connection:getIsServer() then
        self.cultivatorHasGroundContact = streamReadBool(streamId);
    end;
	self:setNewSupportAnimationState(streamReadBool(streamId), true);		
end;

function Wiesenwalze:writeUpdateStream(streamId, connection, dirtyMask)
    if not connection:getIsServer() then
        --if bitAND(dirtyMask, self.cultivatorGroundContactFlag) ~= 0 then
        --    streamWriteBool(streamId, true);
        streamWriteBool(streamId, self.cultivatorHasGroundContact);
    end;
	streamWriteBool(streamId, self.newSupportAnimationState);		
end;

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

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

function Wiesenwalze:setNewSupportAnimationState(bool, noEventSend)
	self.newSupportAnimationState = bool;
	SetNewSupportAnimationStateEvent.sendEvent(self, bool, noEventSend);
end;

function Wiesenwalze:update(dt)	
	if self.playerInRange then
			if InputBinding.hasEvent(InputBinding.newSupportAnimation) then
				self:setNewSupportAnimationState(not self.newSupportAnimationState);
			end;
			if self.newSupportAnimationState then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("LowerSupport"), self.typeDesc), InputBinding.newSupportAnimation);
			else
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("LiftSupport"), self.typeDesc), InputBinding.newSupportAnimation);
			end;
	end;
	
	if self.newSupportAnimationState then
		self:playAnimation(self.newSupportAnimation.name, self.newSupportAnimation.closeSpeedScale, self:getAnimationTime(self.newSupportAnimation.name), true); --hoch			
	else
		self:playAnimation(self.newSupportAnimation.name, self.newSupportAnimation.openSpeedScale, self:getAnimationTime(self.newSupportAnimation.name), true);	--runter	
	end;	
end;

function Wiesenwalze:updateTick(dt)
	if g_currentMission.player ~= nil then
		local nearestDistance = 1;
		local x1,y1,z1 = getWorldTranslation(self.supportPlayerPoint);
		local x2,y2,z2 = getWorldTranslation(g_currentMission.player.rootNode);
		local distance = Utils.vector3Length(x1-x2,y1-y2,z1-z2);
		if distance < nearestDistance then
			self.playerInRange = true; 
		else
			self.playerInRange = false; 
		end;
	end;
	
    if self:getIsActive() then
        local hasGroundContact = false;
        if self.isServer then
            for k, v in pairs(self.contactReportNodes) do
                if v.hasGroundContact then
                    hasGroundContact = true;
                    break;
                end;
            end;

            if not hasGroundContact then
                if self.groundReferenceNode ~= nil then
                    local x,y,z = getWorldTranslation(self.groundReferenceNode);
                    local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z);
                    if terrainHeight+self.groundReferenceThreshold >= y then
                        hasGroundContact = true;					
                    end;
                end;
            end;

            if self.cultivatorHasGroundContact ~= hasGroundContact then
                self:raiseDirtyFlags(self.cultivatorGroundContactFlag);
            end;
            self.cultivatorHasGroundContact = hasGroundContact;
        end;
        local hasGroundContact = self.cultivatorHasGroundContact;

        local rotateRotatingParts = false;

        local foldAnimTime = self.foldAnimTime;
        if hasGroundContact or not hasGroundContact then
            rotateRotatingParts = true;
            local enableGroundParticleSystems = (self.lastSpeed*3600 > 5);
            if self.isClient then
				
				local kmh = math.min(100, math.max(0, self.lastSpeed*self.speedDisplayScale*3600));
				if kmh > 5 then
					Utils.setEmittingState(self.particleSystemSchaber, true);	
				end;
				
		        local Sx,Sy,Sz = getWorldTranslation(self.startIndexSpray);
                local Sx1,Sy1,Sz1 = getWorldTranslation(self.widthIndexSpray);
                local Sx2,Sy2,Sz2 = getWorldTranslation(self.heightIndexSpray);
                Utils.updateSprayArea(Sx, Sz, Sx1, Sz1, Sx2, Sz2);
	
                if enableGroundParticleSystems then
                    for k, cuttingArea in pairs(self.cuttingAreas) do
                        local ps = self.newGroundParticleSystems[k];
                        if self:getIsAreaActive(cuttingArea) then
                            if ps ~= nil then
                                if not ps.isActive then
                                    ps.isActive = true;
                                    Utils.setEmittingState(ps.ps, true);
                                end;
                            end;
                        else
                            if ps ~= nil and ps.isActive then
                                ps.isActive = false;
                                Utils.setEmittingState(ps.ps, false);
                            end;
                        end;
                    end;
                else
                    for k, ps in pairs(self.newGroundParticleSystems) do
                        if ps.isActive then
                            ps.isActive = false;
                            Utils.setEmittingState(ps.ps, false);
                        end;
                    end;
                end;
            end;

            if self.startActivationTime <= self.time then
                if self.isServer then
                    local cuttingAreasSend = {};
                    for k, cuttingArea in pairs(self.cuttingAreas) do
                        if self:getIsAreaActive(cuttingArea) then
                            local x,y,z = getWorldTranslation(cuttingArea.start);
                            local x1,y1,z1 = getWorldTranslation(cuttingArea.width);
                            local x2,y2,z2 = getWorldTranslation(cuttingArea.height);
                            --Utils.updateCultivatorArea(x, z, x1, z1, x2, z2, not self.cultivatorLimitToField);
                            table.insert(cuttingAreasSend, {x,z,x1,z1,x2,z2});
                        end;
                    end;
                    if table.getn(cuttingAreasSend) > 0 then
                        local limitToField = self.cultivatorLimitToField or self.cultivatorForceLimitToField;
                        if not g_currentMission.allowClientsCreateFields then
                            local owner = self:getOwner();
                            if owner ~= nil and not owner:getIsLocal() then
                                limitToField = true;
                            end;
                        end;

                        WiesenwalzeEvent.runLocally(cuttingAreasSend, limitToField);
                        g_server:broadcastEvent(WiesenwalzeEvent:new(cuttingAreasSend, limitToField));
                    end;
                end;

                local speedLimit = 20;
                if self.maxSpeedLevel == 2 then
                    speedLimit = 30;
                elseif self.maxSpeedLevel == 3 then
                    speedLimit = 100;
                end;
                if self:doCheckSpeedLimit() and self.lastSpeed*3600 > speedLimit then
                    self.speedViolationTimer = self.speedViolationTimer - dt;
                    if self.isServer then
                        if self.speedViolationTimer < 0 then
                            if self.attacherVehicle then
                                self.attacherVehicle:detachImplementByObject(self);
                            end;
                        end;
                    end;
                else
                    self.speedViolationTimer = self.speedViolationMaxTime;
                end;
            end;
        else
            self.speedViolationTimer = self.speedViolationMaxTime;

            if self.isClient then

					Utils.setEmittingState(self.particleSystemSchaber, false);	
                for k, ps in pairs(self.newGroundParticleSystems) do
                    if ps.isActive then
                        ps.isActive = false;
                        Utils.setEmittingState(ps.ps, false);
                    end;
                end;
            end;
        end;
    end;

end;

function Wiesenwalze:draw()
    if math.abs(self.speedViolationTimer - self.speedViolationMaxTime) > 2 then
        local buttonName = InputBinding.SPEED_LEVEL1;
        if self.maxSpeedLevel == 2 then
            buttonName = InputBinding.SPEED_LEVEL2;
        elseif self.maxSpeedLevel == 3 then
            buttonName = InputBinding.SPEED_LEVEL3;
        end;
        g_currentMission:addWarning(g_i18n:getText("Dont_drive_to_fast") .. "\n" .. string.format(g_i18n:getText("Cruise_control_levelN"), tostring(self.maxSpeedLevel), InputBinding.getKeyNamesOfDigitalAction(buttonName)), 0.07+0.022, 0.019+0.029);
    end;
end;

function Wiesenwalze:onAttach(attacherVehicle)
    Wiesenwalze.onActivate(self);
    Wiesenwalze.addContactReports(self);
    self.startActivationTime = self.time + self.startActivationTimeout;	
	if self.automaticSupport then
		self.newSupportAnimationState = true;				
	end;	
end;

function Wiesenwalze:onDetach()
	Utils.setEmittingState(self.particleSystemSchaber, false);	
    self.cultivatorLimitToField = false;
    if self.deactivateOnDetach then
        Wiesenwalze.onDeactivate(self);
        Wiesenwalze.removeContactReports(self);
    else
        Wiesenwalze.onDeactivateSounds(self);
    end;
	if self.automaticSupport then
		self.newSupportAnimationState = false;				
	end;		
end;

function Wiesenwalze:onEnter(isControlling)
    if isControlling then
        Wiesenwalze.onActivate(self);
        Wiesenwalze.addContactReports(self);
    end;
end;

function Wiesenwalze:onLeave()
    if self.deactivateOnLeave then
        Wiesenwalze.onDeactivate(self);
        Wiesenwalze.removeContactReports(self);
    else
        Wiesenwalze.onDeactivateSounds(self);
    end;
	Utils.setEmittingState(self.particleSystemSchaber, false);	
end;

function Wiesenwalze:onActivate()
end;

function Wiesenwalze:onDeactivate()
    self.speedViolationTimer = self.speedViolationMaxTime;

    Utils.setEmittingState(self.groundParticleSystems, false);
    for k, ps in pairs(self.newGroundParticleSystems) do
        if ps.isActive then
            ps.isActive = false;
            Utils.setEmittingState(ps.ps, false);
        end;
    end;

    Wiesenwalze.onDeactivateSounds(self);
end;

function Wiesenwalze:onDeactivateSounds()
end;

function Wiesenwalze:aiTurnOn()
    self.cultivatorLimitToField = true;
end;

function Wiesenwalze:aiTurnOff()
    self.cultivatorLimitToField = false;
end;

function Wiesenwalze:addContactReports()
    if not self.cultivatorContactReportsActive then
        for k, v in pairs(self.contactReportNodes) do
            addContactReport(v.node, 0.0001, "groundContactReport", self);
        end;

        self.cultivatorContactReportsActive = true;
    end;
end;

function Wiesenwalze:removeContactReports()
    if self.cultivatorContactReportsActive then
        for k, v in pairs(self.contactReportNodes) do
            removeContactReport(v.node);
            v.hasGroundContact = false;
        end;
        self.cultivatorContactReportsActive = false;
    end;
end;

function Wiesenwalze:groundContactReport(objectId, otherObjectId, isStart, normalForce, tangentialForce)

    if otherObjectId == g_currentMission.terrainRootNode then
        local entry = self.contactReportNodes[objectId];
        if entry ~= nil then
            entry.hasGroundContact = isStart or normalForce > 0 or tangentialForce > 0;
        end;
    end;

end;

--*****EVENT*****--

SetNewSupportAnimationStateEvent = {};
SetNewSupportAnimationStateEvent_mt = Class(SetNewSupportAnimationStateEvent, Event);

InitEventClass(SetNewSupportAnimationStateEvent, "SetNewSupportAnimationStateEvent");

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

function SetNewSupportAnimationStateEvent:new(vehicle, newSupportAnimationState)
    local self = SetNewSupportAnimationStateEvent:emptyNew()
    self.vehicle = vehicle;
	self.newSupportAnimationState = newSupportAnimationState;
    return self;
end;

function SetNewSupportAnimationStateEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
    self.vehicle = networkGetObject(id);

	self.newSupportAnimationState = streamReadBool(streamId);
	self.vehicle:setNewSupportAnimationState(self.newSupportAnimationState, true);
	
	if not connection:getIsServer() then
        g_server:broadcastEvent(SetNewSupportAnimationStateEvent:new(self.vehicle, self.newSupportAnimationState), nil, connection, self.vehicle);
    end;
end;

function SetNewSupportAnimationStateEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
	streamWriteBool(streamId, self.newSupportAnimationState);
end;


function SetNewSupportAnimationStateEvent.sendEvent(vehicle, newSupportAnimationState, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(SetNewSupportAnimationStateEvent:new(vehicle, newSupportAnimationState), nil, nil, vehicle);
		else
			g_client:getServerConnection():sendEvent(SetNewSupportAnimationStateEvent:new(vehicle, newSupportAnimationState));
		end;
	end;
end;