--
-- JohnDeere864Premium
--
-- @author  JoXXer
-- @date  	28/10/13
--
-- @history	v1.0 - Initial implementation
--
--

JohnDeere864Premium = {};

function JohnDeere864Premium.prerequisitesPresent(specializations)
    return true;--SpecializationUtil.hasSpecialization(Fillable, specializations);
end;

function JohnDeere864Premium:load(xmlFile)
	self.setDrawbarRotation = SpecializationUtil.callSpecializationsFunction("setDrawbarRotation");
	self.toggleSupport = SpecializationUtil.callSpecializationsFunction("toggleSupport");
	
	-- Fix the position of the belts
	self.skinBindNodes = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.skinnedNodes.skinnedNode(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		local skinBind = {};
		skinBind.node = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#skinBindNode"));
		skinBind.startX = Utils.getNoNil(getXMLFloat(xmlFile, key.."#startX"), 0);
		if skinBind.node ~= nil then
			table.insert(self.skinBindNodes, skinBind);
		end;
		i = i + 1;
	end;
	
	for _, skinBind in pairs(self.skinBindNodes) do
		local x, y, z = getTranslation(skinBind.node);
		setTranslation(skinBind.node, skinBind.startX, y, z);
	end;
	
	-- Doors
	self.doors = {};
	
	self.doors.left = {};
	self.doors.left.animation = getXMLString(xmlFile, "vehicle.doors#leftAnimation");
	if self.doors.left.animation then 
		self.doors.left.rangeIndex = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.doors#leftRangeIndex"));
		self.doors.left.inRange = false;
		self.doors.left.isOpen = false;
		self.doors.left.dirtyFlag = self:getNextDirtyFlag();
	end;
	
	self.doors.right = {};
	self.doors.right.animation = getXMLString(xmlFile, "vehicle.doors#rightAnimation");
	if self.doors.right.animation then 
		self.doors.right.rangeIndex = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.doors#rightRangeIndex"));
		self.doors.right.inRange = false;
		self.doors.right.isOpen = false;
		self.doors.right.dirtyFlag = self:getNextDirtyFlag();
	end;
	
	self.doors.rear = {};
	self.doors.rear.animation = getXMLString(xmlFile, "vehicle.doors#rearAnimation");
	if self.doors.rear.animation then 
		self.doors.rear.rangeIndex = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.doors#rearRangeIndex"));
		self.doors.rear.inRange = false;
		self.doors.rear.isOpen = false;
		self.doors.rear.dirtyFlag = self:getNextDirtyFlag();
	end;
	
	-- Rotating parts
	self.rotatingParts = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.rotatingParts.rotatingPart(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;
		local rotatingPart = {};
		rotatingPart = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#index"));
		if rotatingPart ~= nil then
			table.insert(self.rotatingParts, rotatingPart);
		end;
		i = i + 1;
	end;
	
	-- Hydraulic hoses
	self.hydraulicHoses = {};
	self.hydraulicHoses.attached = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.hydraulicHoses#attached"));
	self.hydraulicHoses.detached = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.hydraulicHoses#detached"));
	
	-- Drawbar
	self.drawbar = {};
	
    self.drawbar.index = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.drawbar#index"));
    self.drawbar.tounge = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.drawbar#tounge"));
	self.drawbar.maxRot = math.rad(Utils.getNoNil(getXMLFloat(xmlFile,  "vehicle.drawbar#maxRot"), 0));
	self.drawbar.minRot = math.rad(Utils.getNoNil(getXMLFloat(xmlFile,  "vehicle.drawbar#minRot"), 0));
	local rotX, rotY, rotZ = getRotation(self.drawbar.index);
	self.drawbar.currentRot = rotX;
	self.drawbar.doRot = false;
	
	-- Support
	self.manualSupportAnimation = getXMLString(xmlFile, "vehicle.manualSupport#animationName");
	self.supportRangeIndex = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.manualSupport#rangeIndex"));
	self.isInRangeSupport = false;
	self.isSupportDown = true;
end;

function JohnDeere864Premium:delete()
end;

function JohnDeere864Premium:readStream(streamId, connection)
	local drawBarRotX = streamReadFloat32(streamId);
	self:setDrawbarRotation(drawBarRotX, true);
	local isSupportDown = streamReadBool(streamId);
	self:toggleSupport(isSupportDown, true);
	
	self.doors.left.isOpen = streamReadBool(streamId);
	self.doors.right.isOpen = streamReadBool(streamId);
	self.doors.rear.isOpen = streamReadBool(streamId);
	
	if self.doors.left.animation ~= nil and self.playAnimation ~= nil then
		if self.doors.left.isOpen then
			self:playAnimation(self.doors.left.animation, 1, nil, true);
		end;
	end;
	
	if self.doors.right.animation ~= nil and self.playAnimation ~= nil then
		if self.doors.right.isOpen then
			self:playAnimation(self.doors.right.animation, 1, nil, true);
		end;
	end;
	
	if self.doors.rear.animation ~= nil and self.playAnimation ~= nil then
		if self.doors.rear.isOpen then
			self:playAnimation(self.doors.rear.animation, 1, nil, true);
		end;
	end;
end;

function JohnDeere864Premium:writeStream(streamId, connection)
	streamWriteFloat32(streamId, self.drawbar.currentRot);
	streamWriteBool(streamId, self.isSupportDown);
	
	streamWriteBool(streamId, self.doors.left.isOpen);
	streamWriteBool(streamId, self.doors.right.isOpen);
	streamWriteBool(streamId, self.doors.rear.isOpen);
end;

function JohnDeere864Premium:readUpdateStream(streamId, timestamp, connection)
    if connection:getIsServer() then
        local updateLeftPanel = streamReadBool(streamId);
        if updateLeftPanel then
			self.doors.left.isOpen = streamReadBool(streamId);
        end;
        local updateRightPanel = streamReadBool(streamId);
        if updateLeftPanel then
			self.doors.right.isOpen = streamReadBool(streamId);
        end;
        local updateRearDoor = streamReadBool(streamId);
        if updateLeftPanel then
			self.doors.rear.isOpen = streamReadBool(streamId);
        end;
    end;
end;

function JohnDeere864Premium:writeUpdateStream(streamId, connection, dirtyMask)
    if not connection:getIsServer() then
        if bitAND(dirtyMask, self.doors.left.dirtyFlag) ~= 0 then
            streamWriteBool(streamId, true);
            streamWriteBool(streamId, self.doors.left.isOpen);
        else
            streamWriteBool(streamId, false);
        end;
        if bitAND(dirtyMask, self.doors.right.dirtyFlag) ~= 0 then
            streamWriteBool(streamId, true);
            streamWriteBool(streamId, self.doors.right.isOpen);
        else
            streamWriteBool(streamId, false);
        end;
        if bitAND(dirtyMask, self.doors.rear.dirtyFlag) ~= 0 then
            streamWriteBool(streamId, true);
            streamWriteBool(streamId, self.doors.rear.isOpen);
        else
            streamWriteBool(streamId, false);
        end;
    end;
end;

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

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

function JohnDeere864Premium:update(dt)
	-- Make parts move, magic
	if self:getIsAnimationPlaying(self.doors.left.animation) or self:getIsAnimationPlaying(self.doors.right.animation) then
		for _, part in pairs(self.movingParts) do
			Cylindered.setDirty(self, part);
		end;
	end;
	
	if self.isTurnedOn then
		for _, part in pairs(self.rotatingParts) do
			rotate(part, -0.01 * dt, 0, 0);
		end;
	end;
	
	if self.supportRangeIndex then
		-- Manage key events for inrange support --
		if self.isInRangeSupport then
			if InputBinding.hasEvent(InputBinding.LOWER_IMPLEMENT) then
				if self.isSupportDown then
					self:toggleSupport(false);
				else
					self:toggleSupport(true);
				end;
			end;
			-- Display key when in range --
			if self.isSupportDown then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_RAISE_SUPPORT")), InputBinding.LOWER_IMPLEMENT);
			else
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_LOWER_SUPPORT")), InputBinding.LOWER_IMPLEMENT);
			end;
		end;
	end;
	
	if self.doors.left.rangeIndex then
		-- Manage key events for inrange door --
		if self.doors.left.inRange then
			if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
				if self.doors.left.isOpen then
					if self.doors.left.animation ~= nil and self.playAnimation ~= nil then
						self:playAnimation(self.doors.left.animation, -1, nil);
					end;
				else
					if self.doors.left.animation ~= nil and self.playAnimation ~= nil then
						self:playAnimation(self.doors.left.animation, 1, nil);
					end;
				end;
				
				self.doors.left.isOpen = not self.doors.left.isOpen;
				self:raiseDirtyFlags(self.doors.left.dirtyFlag);
			end;
			-- Display key when in range --
			if self.doors.left.isOpen then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_CLOSE_LEFT_PANEL")), InputBinding.IMPLEMENT_EXTRA2);
			else
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_OPEN_LEFT_PANEL")), InputBinding.IMPLEMENT_EXTRA2);
			end;
		end;
	end;
	
	if self.doors.right.rangeIndex then
		-- Manage key events for inrange door --
		if self.doors.right.inRange then
			if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
				if self.doors.right.isOpen then
					if self.doors.right.animation ~= nil and self.playAnimation ~= nil then
						self:playAnimation(self.doors.right.animation, -1, nil);
					end;
				else
					if self.doors.right.animation ~= nil and self.playAnimation ~= nil then
						self:playAnimation(self.doors.right.animation, 1, nil);
					end;
				end;
				
				self.doors.right.isOpen = not self.doors.right.isOpen;
				self:raiseDirtyFlags(self.doors.right.dirtyFlag);
			end;
			-- Display key when in range --
			if self.doors.right.isOpen then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_CLOSE_RIGHT_PANEL")), InputBinding.IMPLEMENT_EXTRA2);
			else
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_OPEN_RIGHT_PANEL")), InputBinding.IMPLEMENT_EXTRA2);
			end;
		end;
	end;
	
	if self.doors.rear.rangeIndex then
		-- Manage key events for inrange door --
		if self.doors.rear.inRange then
			if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
				if self.doors.rear.isOpen then
					if self.doors.rear.animation ~= nil and self.playAnimation ~= nil then
						self:playAnimation(self.doors.rear.animation, -1, nil);
					end;
				else
					if self.doors.rear.animation ~= nil and self.playAnimation ~= nil then
						self:playAnimation(self.doors.rear.animation, 1, nil);
					end;
				end;
				
				self.doors.rear.isOpen = not self.doors.rear.isOpen;
				self:raiseDirtyFlags(self.doors.rear.dirtyFlag);
			end;
			-- Display key when in range --
			if self.doors.rear.isOpen then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_CLOSE_REAR_DOOR")), InputBinding.IMPLEMENT_EXTRA2);
			else
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("BJR_OPEN_REAR_DOOR")), InputBinding.IMPLEMENT_EXTRA2);
			end;
		end;
	end;

	if self:getIsActive() then		
		if self.isClient and self:getIsActiveForInput(false) and not self:hasInputConflictWithSelection() then
			-- Show warning about the support being down
			if self.isSupportDown then
				g_currentMission:addWarning(string.format(g_i18n:getText("BJR_SUPPORT_WARNING")), 0.018, 0.033);
			end;
			
			local rotX, rotY, rotZ = getRotation(self.drawbar.index);
			
			if InputBinding.isPressed(InputBinding.BJR_DRAWBAR_UP) then
				if rotX > self.drawbar.minRot then
					rotX = rotX - 0.0002 * dt;
					self:setDrawbarRotation(rotX);
				end;
			elseif InputBinding.isPressed(InputBinding.BJR_DRAWBAR_DOWN) then
				if rotX < self.drawbar.maxRot then
					rotX = rotX + 0.0002 * dt;
					self:setDrawbarRotation(rotX);
				end;
			end;
		end;
	end;
	
	if self.drawbar.doRot then
		self:setDrawbarRotation(self.drawbar.currentRot);
		self.drawbar.doRot = false;
	end;
end;

function JohnDeere864Premium:updateTick(dt)
	if self.supportRangeIndex then
		if g_currentMission.player ~= nil then
			-- Getting the distance between the player and the support
			local nearestDistance = 1.5; --max distance allowed
			local px, py, pz = getWorldTranslation(self.supportRangeIndex);
			local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
			local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
			if distance < nearestDistance then
				self.isInRangeSupport = true;
			else
				self.isInRangeSupport = false;
			end;
		end;
	else
		self.isInRangeSupport = false;
	end;
	
	if self.doors.left.rangeIndex then
		if g_currentMission.player ~= nil then
			-- Getting the distance between the player and the door
			local nearestDistance = 2.0; --max distance allowed
			local px, py, pz = getWorldTranslation(self.doors.left.rangeIndex);
			local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
			local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
			if distance < nearestDistance then
				self.doors.left.inRange = true;
			else
				self.doors.left.inRange = false;
			end;
		end;
	else
		self.doors.left.inRange = false;
	end;
	
	if self.doors.right.rangeIndex then
		if g_currentMission.player ~= nil then
			-- Getting the distance between the player and the door
			local nearestDistance = 2.0; --max distance allowed
			local px, py, pz = getWorldTranslation(self.doors.right.rangeIndex);
			local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
			local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
			if distance < nearestDistance then
				self.doors.right.inRange = true;
			else
				self.doors.right.inRange = false;
			end;
		end;
	else
		self.doors.right.inRange = false;
	end;
	
	if self.doors.rear.rangeIndex then
		if g_currentMission.player ~= nil then
			-- Getting the distance between the player and the door
			local nearestDistance = 2.0; --max distance allowed
			local px, py, pz = getWorldTranslation(self.doors.rear.rangeIndex);
			local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
			local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
			if distance < nearestDistance then
				self.doors.rear.inRange = true;
			else
				self.doors.rear.inRange = false;
			end;
		end;
	else
		self.doors.rear.inRange = false;
	end;
end;

function JohnDeere864Premium:draw()
    if self.isClient then
        if self:getIsActiveForInput(true) then
			g_currentMission:addExtraPrintText(string.format(g_i18n:getText("BJR_DRAWBAR_CONTROLS")) .. " " .. InputBinding.getKeyNamesOfDigitalAction(InputBinding.BJR_DRAWBAR_UP) .. "/" .. InputBinding.getKeyNamesOfDigitalAction(InputBinding.BJR_DRAWBAR_DOWN));
		end;
	end;
end;

function JohnDeere864Premium:onAttach(attacherVehicle)
	setVisibility(self.hydraulicHoses.attached, true);
	setVisibility(self.hydraulicHoses.detached, false);

	self.attacherVehicle = attacherVehicle;
end;

function JohnDeere864Premium:onDetach()
	setVisibility(self.hydraulicHoses.attached, false);
	setVisibility(self.hydraulicHoses.detached, true);
	
	self.attacherVehicle = nil;
end;

function JohnDeere864Premium:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	local isSupportDown = Utils.getNoNil(getXMLBool(xmlFile, key .. "#isSupportDown"), false);
	self:toggleSupport(isSupportDown);
	
	self.drawbar.currentRot = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#drawbarRot"), self.drawbar.currentRot);
	self.drawbar.doRot = true;

    return BaseMission.VEHICLE_LOAD_OK;
end;

function JohnDeere864Premium:getSaveAttributesAndNodes(nodeIdent)
    local attributes = 'isSupportDown="' .. tostring(self.isSupportDown) ..'" drawbarRot="' .. tostring(self.drawbar.currentRot) .. '"';
    return attributes, nil;
end;

function JohnDeere864Premium:setDrawbarRotation(rotX, noEventSend)
	AdjustDrawbarEvent.sendEvent(self, rotX, noEventSend);	
	
	setRotation(self.drawbar.index, rotX, 0, 0);
	setRotation(self.drawbar.tounge, -rotX, 0, 0);
	
	if self.isServer and self.attacherVehicle ~= nil then
		local implement = self.attacherVehicle:getImplementByObject(self);
		if implement ~= nil then
			local jointDesc = self.attacherVehicle.attacherJoints[implement.jointDescIndex]
			if jointDesc ~= nil then
				setJointFrame(jointDesc.jointIndex, 1, self.attacherJoint.node);
			end;
		end;
	end
	
	self.drawbar.currentRot = rotX;
end;

function JohnDeere864Premium:toggleSupport(isSupportDown, noEventSend)
	ToggleSupportEvent.sendEvent(self, isSupportDown, noEventSend);	
	
	if not isSupportDown then
		if self.manualSupportAnimation ~= nil and self.playAnimation ~= nil then
			self:playAnimation(self.manualSupportAnimation, -1, nil, true);
		end;
	else
		if self.manualSupportAnimation ~= nil and self.playAnimation ~= nil then
			self:playAnimation(self.manualSupportAnimation, 1, nil, true);
		end;
	end;
	
	self.isSupportDown = isSupportDown;
end;


-- Drawbar event

AdjustDrawbarEvent = {};
AdjustDrawbarEvent_mt = Class(AdjustDrawbarEvent, Event);

InitEventClass(AdjustDrawbarEvent, "AdjustDrawbarEvent");

function AdjustDrawbarEvent:emptyNew()
    local self = Event:new(AdjustDrawbarEvent_mt);
    return self;
end;

function AdjustDrawbarEvent:new(vehicle, rotX)
    local self = AdjustDrawbarEvent:emptyNew()
    self.vehicle = vehicle;
	self.rotX = rotX;
    return self;
end;

function AdjustDrawbarEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
	self.vehicle = networkGetObject(id);
	self.rotX = streamReadFloat32(streamId);
    self:run(connection);
end;

function AdjustDrawbarEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));	
	streamWriteFloat32(streamId, self.rotX);
end;

function AdjustDrawbarEvent:run(connection)
	self.vehicle:setDrawbarRotation(self.rotX, true);
end;

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


-- Support event

ToggleSupportEvent = {};
ToggleSupportEvent_mt = Class(ToggleSupportEvent, Event);

InitEventClass(ToggleSupportEvent, "ToggleSupportEvent");

function ToggleSupportEvent:emptyNew()
    local self = Event:new(ToggleSupportEvent_mt);
    return self;
end;

function ToggleSupportEvent:new(vehicle, isSupportDown)
    local self = ToggleSupportEvent:emptyNew()
    self.vehicle = vehicle;
	self.isSupportDown = isSupportDown;
    return self;
end;

function ToggleSupportEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
	self.vehicle = networkGetObject(id);
	self.isSupportDown = streamReadBool(streamId);
    self:run(connection);
end;

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

function ToggleSupportEvent:run(connection)
	self.vehicle:toggleSupport(self.isSupportDown, true);
end;

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