--
-- ManualAttaching script
-- author: Burner
-- date: 01.06.2012
--
-- convert to LS2013
-- date: 27.10.2013
--
-- convert to FS15
-- date: 15.07.2015
-- PTO attach/detach function
--

ManualAttaching = {};

function ManualAttaching.prerequisitesPresent(specializations)
    return true
end;

function ManualAttaching:load(xmlFile)
	self.togglePTO = SpecializationUtil.callSpecializationsFunction("togglePTO");
	self.playManualAttachSound = SpecializationUtil.callSpecializationsFunction("playManualAttachSound");
	self.playManualAttachPTOSound = SpecializationUtil.callSpecializationsFunction("playManualAttachPTOSound");
	
	if self.attacherJoints ~= nil then
		for k,jointDesc in pairs(self.attacherJoints) do
			jointDesc.ptoAttached = true;
		end;
	end;
	
	local attachSound = Utils.getFilename('attach.wav', ManualAttaching.modDir);
	if attachSound ~= nil then
		ManualAttaching.manualAttachSound = {};
		ManualAttaching.manualAttachSound.sample = createSample("manualAttachSound");
		loadSample(ManualAttaching.manualAttachSound.sample, attachSound, false);
		ManualAttaching.manualAttachSound.volume = 1;
	end;
	
	local attachPTOSound = Utils.getFilename('attach_pto.wav', ManualAttaching.modDir);
	if attachPTOSound ~= nil then
		ManualAttaching.manualAttachPTOSound = {};
		ManualAttaching.manualAttachPTOSound.sample = createSample("manualAttachPTOSound");
		loadSample(ManualAttaching.manualAttachPTOSound.sample, attachPTOSound, false);
		ManualAttaching.manualAttachPTOSound.volume = 1;
	end;
	
	self.avBackup = nil;
	self.avJointDescIndex = nil;
end;

function ManualAttaching:getSaveAttributesAndNodes(nodeIdent)
	local numToSave = 0;
	local attributes = nil;
	
	if self.attacherJoints ~= nil then
		for k, jointDesc in pairs(self.attacherJoints) do
			if jointDesc.ptoOutput ~= nil then
				numToSave = numToSave + 1;
			end;
		end;
	end;

	for k, jointDesc in pairs(self.attacherJoints) do
		if jointDesc.ptoOutput ~= nil then
			if jointDesc.ptoAttached == false then
				local saveAttributes = "attacherJoint" .. k .. "ptoAttached=\"" .. tostring(jointDesc.ptoAttached) .. "\"";
				if numToSave > 1 and attributes ~= nil then
					attributes = attributes .. " " .. saveAttributes;
				else
					attributes = saveAttributes;
				end;
			end;
		end;
	end;

	return attributes, nil;
end;

function ManualAttaching:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	for k, jointDesc in pairs(self.attacherJoints) do
		if jointDesc.ptoOutput ~= nil then
			local keyString = string.format("#attacherJoint%dptoAttached", k);
			local state = Utils.getNoNil(getXMLBool(xmlFile, key .. keyString), true);
			if state == false then
				self:togglePTO(k, state, true);
			end;
		end;
	end;
	return BaseMission.VEHICLE_LOAD_OK;
end;

function ManualAttaching:readStream(streamId, connection)
	if self.attacherJoints ~= nil then
		for k, jointDesc in pairs(self.attacherJoints) do
			if jointDesc.ptoOutput ~= nil then
				local state = streamReadBool(streamId)
				self:togglePTO(k, state, true);
			end;
		end;
	end;
end;


function ManualAttaching:writeStream(streamId, connection)
	if self.attacherJoints ~= nil then
		for k, jointDesc in pairs(self.attacherJoints) do
			if jointDesc.ptoOutput ~= nil then
				streamWriteBool(streamId, jointDesc.ptoAttached);
			end;
		end;
	end;
end;

function ManualAttaching:delete()
	Utils.deleteSample(ManualAttaching.manualAttachSound)
	Utils.deleteSample(ManualAttaching.manualAttachPTOSound)
end;

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

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

function ManualAttaching:update(dt)
	if g_currentMission.player.attacherSearchNode == nil then
		g_currentMission.player.attacherSearchNode = createTransformGroup("attacherSearchNode");
		link(g_currentMission.player.lightNode, g_currentMission.player.attacherSearchNode);
		setTranslation(g_currentMission.player.attacherSearchNode, 0, 0, -2);
	end;
	
	if not self:getIsActiveForInput() then
		local nearestFarmerDistance = 1.1;
		local nearestAttachersDistance = 2;
		local foundImplement = nil;
		for attacherJointIndex, jointDesc in pairs(self.attacherJoints) do
			local xNew = jointDesc.jointOrigTrans[1];
			local yNew = jointDesc.jointOrigTrans[2];
			local zNew = jointDesc.jointOrigTrans[3];
			local px, py, pz = localToWorld(getParent(jointDesc.jointTransform), xNew, yNew, zNew);
			for _, attachable in pairs(g_currentMission.attachables) do
				for inputAttacherIndex, attacherJoint in pairs(attachable.inputAttacherJoints) do
					if attachable.attacherVehicle == nil and attacherJoint.jointType == jointDesc.jointType then
						local vx, vy, vz = getWorldTranslation(attacherJoint.node);
						local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
						
						if distance < nearestAttachersDistance then
							foundImplement = attachable;
							
							if g_currentMission.player ~= nil then
								local pvx, pvy, pvz = getWorldTranslation(g_currentMission.player.attacherSearchNode);
								local farmerDistance = Utils.vector3Length(pvx-px, pvy-py, pvz-pz);
								if farmerDistance < nearestFarmerDistance and foundImplement ~= nil then								
									local sx, sy, sz = project(px, py, pz);
									local sx1, sy1, sz1 = project(vx, vy, vz);
									
									setTextAlignment(RenderText.ALIGN_CENTER);
									setTextBold(false);		
									setTextColor(0.235, 0.6, 0.137, 1.0);
									
									for i=1, 10 do
										renderText(sx, sy, 0.1, ".");
										renderText(sx1, sy1, 0.1, ".");
									end;									
									
									setTextAlignment(RenderText.ALIGN_LEFT);
									
									g_currentMission:addHelpButtonText(string.format(g_i18n:getText("Attach"), self.typeDesc), InputBinding.ATTACH);
									if InputBinding.hasEvent(InputBinding.ATTACH) then
										self:attachImplement(attachable, inputAttacherIndex, attacherJointIndex);
										if jointDesc.ptoOutput ~= nil and attacherJoint.ptoInput ~= nil then
											self:togglePTO(attacherJointIndex, false);
										end;
										self:playManualAttachSound();
									end;
								end;
							end;
						end;
						
					elseif attachable.attacherVehicle == self and attacherJoint.jointType == jointDesc.jointType then
						local vx, vy, vz = getWorldTranslation(attacherJoint.node);
						local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
						if distance < nearestAttachersDistance then
							foundImplement = attachable;
							if g_currentMission.player ~= nil then
								local pvx, pvy, pvz = getWorldTranslation(g_currentMission.player.attacherSearchNode);
								local farmerDistance = Utils.vector3Length(pvx-px, pvy-py, pvz-pz);
								if farmerDistance < nearestFarmerDistance and foundImplement ~= nil then
									for implementIndex, implement in pairs(self.attachedImplements) do
										if implement.object == attachable then

											if (jointDesc.ptoOutput ~= nil and jointDesc.ptoAttached == false) or attacherJoint.ptoInput == nil then
												g_currentMission:addHelpButtonText(string.format(g_i18n:getText("Detach"), self.typeDesc), InputBinding.ATTACH);
												
												local sx, sy, sz = project(px, py, pz);
												setTextAlignment(RenderText.ALIGN_CENTER);
												setTextBold(false);		
												setTextColor(0.73, 0.05, 0.05, 1.0); 
												for i=1, 10 do
													renderText(sx, sy, 0.1, ".");
												end;												
												setTextAlignment(RenderText.ALIGN_LEFT);
												
												if InputBinding.hasEvent(InputBinding.ATTACH) then
													self:detachImplement(implementIndex);
													self:playManualAttachSound();
												end;

												if jointDesc.ptoOutput ~= nil and jointDesc.ptoAttached == false and attacherJoint.ptoInput ~= nil then
													g_currentMission:addHelpButtonText(string.format(g_i18n:getText("PTO_Attach")), InputBinding.IMPLEMENT_EXTRA2);
													
													local qvx, qvy, qvz = getWorldTranslation(attacherJoint.ptoInput.node);
													local rvx, rvy, rvz = getWorldTranslation(jointDesc.ptoOutput.node);
													local sx, sy, sz = project(qvx, qvy, qvz);
													local sx1, sy1, sz1 = project(rvx, rvy, rvz);
												
													setTextAlignment(RenderText.ALIGN_CENTER);
													setTextBold(false);		
													setTextColor(1, 1, 0, 1.0);
													for i=1, 10 do
														renderText(sx, sy, 0.1, ".");
														renderText(sx1, sy1, 0.1, ".");
													end;													
													setTextAlignment(RenderText.ALIGN_LEFT);
													if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
														self:togglePTO(attacherJointIndex, true);
														self:playManualAttachPTOSound();
													end;
												end;
												
											elseif jointDesc.ptoOutput ~= nil and jointDesc.ptoAttached == true and attacherJoint.ptoInput ~= nil then
												g_currentMission:addHelpButtonText(string.format(g_i18n:getText("PTO_Detach")), InputBinding.IMPLEMENT_EXTRA2);
												local qvx, qvy, qvz = getWorldTranslation(attacherJoint.ptoInput.node);
												local rvx, rvy, rvz = getWorldTranslation(jointDesc.ptoOutput.node);
												local sx, sy, sz = project(qvx, qvy, qvz);
												local sx1, sy1, sz1 = project(rvx, rvy, rvz);
											
												setTextAlignment(RenderText.ALIGN_CENTER);
												setTextBold(false);		
												setTextColor(1, 0.23, 0, 1.0);
												
												for i=1, 10 do
													renderText(sx, sy, 0.1, ".");
													renderText(sx1, sy1, 0.1, ".");
												end;												
												
												setTextAlignment(RenderText.ALIGN_LEFT);
												
												if InputBinding.hasEvent(InputBinding.IMPLEMENT_EXTRA2) then
													self:togglePTO(attacherJointIndex, false);
													self:playManualAttachPTOSound();
												end;
											end;
										end;
									end;
								end;
							end;
						end;
					end;
					if foundImplement ~= nil then 
						break;
					end;
				end;
			end;
		end;
		
		for _, implement in pairs(self.attachedImplements) do
			self:updateAttacherJointGraphics(implement, dt)
		end;
	end;
	

	if self.isSteerable then
		for _, implement in pairs(self.attachedImplements) do
			local object = implement.object;
			if object ~= nil then
				local jointDescIndex = implement.jointDescIndex;
				local jointDesc = self.attacherJoints[jointDescIndex];
				local inputJointDescIndex = object.inputAttacherJointDescIndex;
				local attacherJoint = object.inputAttacherJoints[inputJointDescIndex];
				
				if jointDesc.ptoOutput ~= nil and jointDesc.ptoAttached == false then
					local vehicle = self;
					local tipVehicle = nil;
					
					if self.isTurnedOn == nil then
						vehicle = object;
					end;
					
					if object.tipState ~= nil then
						tipVehicle = object;
					end;
					
					for _, childImplement in pairs(object.attachedImplements) do
						local childObject = childImplement.object;
						if childObject ~= nil then
							if childObject.isTurnedOn ~= nil then
								vehicle = childObject;
							end;
							
							if childObject.tipState ~= nil then
								tipVehicle = childObject;
							end;
						end;
					end;

					if vehicle.isTurnedOn then
						if self.isAIThreshing ~= nil then
							if self.isAIThreshing then
								self:stopAIThreshing();
							end;
						end;
						if self.isAITractorActivated ~= nil then
							if self.isAITractorActivated then
								self:stopAITractor();
							end;
						end;
						vehicle:setIsTurnedOn(false);
						g_currentMission:showBlinkingWarning(g_i18n:getText("PTO_Attach_Warning"), 2000);
					end;
					
					if tipVehicle ~= nil then
						if tipVehicle.tipState == Trailer.TIPSTATE_OPENING or tipVehicle.tipState == Trailer.TIPSTATE_OPEN then
							tipVehicle:onEndTip();
							g_currentMission:showBlinkingWarning(g_i18n:getText("PTO_Attach_Warning"), 2000);
						end;
					end;
				end;
			end;

		end;
	end;
end;

function ManualAttaching:onAttach(attacherVehicle, jointDescIndex)
    self.avBackup = attacherVehicle;
	self.avJointDescIndex = jointDescIndex;
end;

function ManualAttaching:onDetach()
	if self.avBackup ~= nil then
		self.avBackup:togglePTO(self.avJointDescIndex, true, true);
		self.avBackup = nil;
		self.avJointDescIndex = nil;
	end;
end;

function ManualAttaching:draw()
end;

function ManualAttaching:playManualAttachSound()
    if self.isClient then
		Utils.playSample(ManualAttaching.manualAttachSound, 1, 0, nil);
    end;
end;

function ManualAttaching:playManualAttachPTOSound()
    if self.isClient then
		Utils.playSample(ManualAttaching.manualAttachPTOSound, 1, 0, nil);
    end;
end;

function ManualAttaching:togglePTO(attacherJointIndex, state, noEventSend)
	TogglePTOEvent.sendEvent(self,attacherJointIndex,state,noEventSend);
	local jointDesc = self.attacherJoints[attacherJointIndex];
	if jointDesc ~= nil then
		if jointDesc.ptoOutput ~= nil then
			setVisibility(jointDesc.ptoOutput.rootNode, state);
			setVisibility(jointDesc.ptoOutput.attachNode, state);
			jointDesc.ptoAttached = state;
		end;
	end;
end;


TogglePTOEvent = {};
TogglePTOEvent_mt = Class(TogglePTOEvent, Event);

InitEventClass(TogglePTOEvent, "TogglePTOEvent");

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

function TogglePTOEvent:new(object, attacherInd, state)
	local self = TogglePTOEvent:emptyNew()
	self.object = object;
	self.attacherInd = attacherInd;
	self.state = state;
	return self;
end;

function TogglePTOEvent:readStream(streamId, connection)
    self.object = networkGetObject(streamReadInt32(streamId));
	self.attacherInd = streamReadInt8(streamId);
    self.state = streamReadBool(streamId);
    self:run(connection);
end;

function TogglePTOEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.object));
	streamWriteInt8(streamId, self.attacherInd);
	streamWriteBool(streamId, self.state);
end;

function TogglePTOEvent:run(connection)
	self.object:togglePTO(self.attacherInd,self.state, true);
	if not connection:getIsServer() then
		g_server:broadcastEvent(TogglePTOEvent:new(self.object, self.attacherInd, self.state), nil, connection, self.object);
	end;
end;

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