--[[************************************************************************************************************************************************************
	
	mrFullPowershift v1.1 (with gear reduction)
	
	11/2013 by Saty / http://forum.lsczech.cz
	
****************************************************************************************************************************************************************
	
	Very thanks for :
	
	Dural - author of MoreRealistic mod & this specialization v1.0
	
****************************************************************************************************************************************************************
	
	(EN) INFO
	
	Free for use on mods. Modifications only with my permission !
	
****************************************************************************************************************************************************************
	
	(CZ) INFO
	
	Pro volne pouziti v modech. ZAKAZ modifikaci bez meho souhlasu !
	
************************************************************************************************************************************************************]]--

mrFullPowershift = {};

function mrFullPowershift.prerequisitesPresent(specializations) 
	return SpecializationUtil.hasSpecialization(RealisticMotorized, specializations);
end;


function mrFullPowershift:load(xmlFile) 
	--print(" ----- ******  loading mrFullPowershift ****** ------");
	
	self.isRealistic = true;
	
	self.setNextGear = SpecializationUtil.callSpecializationsFunction("setNextGear");
	self.setGear = SpecializationUtil.callSpecializationsFunction("setGear");
	
	self.setReduction = SpecializationUtil.callSpecializationsFunction("setReduction");
	
	self.doGearShift = SpecializationUtil.callSpecializationsFunction("doGearShift");
	
	self.setFullPowershiftModeOn = SpecializationUtil.callSpecializationsFunction("setFullPowershiftModeOn");
	self.setFullPowershiftModeOff = SpecializationUtil.callSpecializationsFunction("setFullPowershiftModeOff");
	
	self.setManualGear = SpecializationUtil.callSpecializationsFunction("setManualGear");
	
	self.realReductionOverlay1 = Overlay:new("realReductionOverlay1", Utils.getFilename("Scripts/turtle1.dds", self.baseDirectory), 0.91, 0.775-0.0015, 0.03, 0.02);
	self.realReductionOverlay2 = Overlay:new("realReductionOverlay2", Utils.getFilename("Scripts/turtle2.dds", self.baseDirectory), 0.91, 0.775, 0.03, 0.02);
	
	local xmlString = "vehicle.fullPowershift";
	
	self.mrFpGearTimeToShiftGear = Utils.getNoNil(getXMLFloat(xmlFile, xmlString .. "#gearShiftTimeMs"), 200);
	
	self.mrFpMaxRpm = Utils.getNoNil(getXMLFloat(xmlFile, xmlString .. "#motorMaxRpm"), 2100);
	self.mrFpMinRpm = Utils.getNoNil(getXMLFloat(xmlFile, xmlString .. "#motorMinRpm"), 700);
	self.mrFpMinRpmAtMaxPower = Utils.getNoNil(getXMLFloat(xmlFile, xmlString .. "#motorMinRpmForMaxPower"), 1750);
	self.mrFpUsedReduction = Utils.getNoNil(getXMLBool(xmlFile, xmlString .. "#usedReduction"), false);
	
	self.mrFpMinMaxRpmRatio = self.mrFpMinRpm / self.mrFpMaxRpm;
	
	self.mrFpFwdDefaultGear = Utils.getNoNil(getXMLFloat(xmlFile, xmlString .. ".fwdGears#defaultGear"), 1);
	self.mrFpFwdGears = {};
	local i = 0;
	while true do
		local fwdGearString = xmlString .. string.format(".fwdGears.gear(%d)", i);
		local newGear = getXMLFloat(xmlFile, fwdGearString .. "#speed");
		if newGear == nil then
			break;
		end;
		local newRedGear = Utils.getNoNil(getXMLFloat(xmlFile, fwdGearString .. "#redSpeed"), 0);
		table.insert(self.mrFpFwdGears, {speed = newGear/3.6, redSpeed = newRedGear/3.6}); -- m/s
		i = i + 1;
	end;
	
	self.mrFpRevDefaultGear = Utils.getNoNil(getXMLFloat(xmlFile, xmlString .. ".revGears#defaultGear"), 1);
	self.mrFpRevGears = {};
	i = 0;
	while true do
		local revGearString = xmlString .. string.format(".revGears.gear(%d)", i);
		local newGear = getXMLFloat(xmlFile, revGearString .. "#speed");
		if newGear == nil then
			break;
		end;
		local newRedGear = Utils.getNoNil(getXMLFloat(xmlFile, revGearString .. "#redSpeed"), 0);
		table.insert(self.mrFpRevGears, {speed = newGear/3.6, redSpeed = newRedGear/3.6}); -- m/s
		i = i + 1;
	end;
	
	self.mrFpIsOn = false;
	
	self.mrFpCurrentGear = 0;
	self.mrFpRedGear = false;
	self.mrFpMinGear = -table.getn(self.mrFpRevGears);
	self.mrFpMaxGear = table.getn(self.mrFpFwdGears);
	
	self.mrFpGearShiftingNeeded = false;
	self.mrFpGearShiftingTime = 0;
	
	--self:setFullPowershiftModeOn();
	
	--adding the T4 mode
	self.realTransmissionMode.maxMode = 4;
	self.realTransmissionMode.modes[4] = {};
	self.realTransmissionMode.modes[4].enabled = true; -- full powershift mode
	self.realTransmissionMode.modes[4].text = "T4";
end;

function mrFullPowershift:delete()    
end;

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

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

function mrFullPowershift:readStream(streamId, connection)
	self.mrFpIsOn = streamReadBool(streamId);
	self.mrFpCurrentGear = streamReadInt32(streamId);
end;
 
function mrFullPowershift:writeStream(streamId, connection)
	streamWriteBool(streamId, self.mrFpIsOn);
	streamWriteInt32(streamId, self.mrFpCurrentGear);
end;

function mrFullPowershift:updateTick(dt)
	if self.isAITractorActivated then
		self:setFullPowershiftModeOff();
	else -- not ai controlled
		if self.realIsMotorStarted then
			--new powershift mode = T4
			if self.realTransmissionMode.currentMode == 4 and self.motor.speedLevel == 0 then
				if not self.mrFpIsOn then
					self:setFullPowershiftModeOn();
					self:setGear(0);
				end;
			else
				if self.mrFpIsOn then
					self:setFullPowershiftModeOff();
				end;
			end;
		end;
	end;
end;

function mrFullPowershift:update(dt)
	if self:getIsActiveForInput(false) then
		if self.mrFpIsOn and self.realIsMotorStarted then
			if g_gui.currentGui == nil and (self.isMouseActive == nil or not self.isMouseActive) then
				--print(self.time .. " mrFullPowershift:update - g_gui.currentGuiName = " .. tostring(g_gui.currentGuiName));
				if InputBinding.hasEvent(InputBinding.MRFULLPOWERSHIFTGEARUP) then
					--RealisticUtils.testClass("g_currentMission.inGameMessage", g_currentMission.inGameMessage);
					--RealisticUtils.testClass("g_currentMission.messages", g_currentMission.messages);
					self:setNextGear(false);
				elseif InputBinding.hasEvent(InputBinding.MRFULLPOWERSHIFTGEARDOWN) then
					self:setNextGear(true);
				elseif InputBinding.hasEvent(InputBinding.MRFULLPOWERSHIFTNEUTRAL) then
					self:setGear(0);
				end;
				if self.mrFpUsedReduction and InputBinding.hasEvent(InputBinding.ACTIVATE_OBJECT) then
					self:setReduction(not self.mrFpRedGear);
					self:setGear(self.mrFpCurrentGear);
				end;
			end;
		end;
	end;
	
	if self.isServer and self.isActive then
		if self.mrFpGearShiftingNeeded then
			if self.time >= self.mrFpGearShiftingTime then
				self.mrFpGearShiftingNeeded = false;
				self:doGearShift();
			end;
		end;
	end;
end;

function mrFullPowershift:draw()
	if self.mrFpIsOn then
		setTextBold(true);
		setTextAlignment(RenderText.ALIGN_RIGHT);
		-- current gear
		setTextColor(0.0, 0.0, 0.0, 0.75);
		renderText(0.95, 0.77-0.0015, 0.033, string.format("%1.0f", math.abs(self.mrFpCurrentGear)));
		setTextColor(1, 0.89, 0.267, 1);
		renderText(0.95, 0.77, 0.033, string.format("%1.0f", math.abs(self.mrFpCurrentGear)));
		-- current rpm
		setTextColor(0.0, 0.0, 0.0, 0.75);
		renderText(0.9828, 0.71-0.0015, 0.021, string.format("%1.0f rpm", RealisticUtils.linearFx(self.realSoundEngineRevFx, self.mrFpMinRpm, self.mrFpMaxRpm)));
		setTextColor(1, 1, 1, 1);
		renderText(0.9828, 0.71, 0.021, string.format("%1.0f rpm", RealisticUtils.linearFx(self.realSoundEngineRevFx, self.mrFpMinRpm, self.mrFpMaxRpm)));
		-- gear reduction
		if self.mrFpUsedReduction then
			g_currentMission:addHelpButtonText(g_i18n:getText("MRFULLPOWERSHIFTREDUCTION"), InputBinding.ACTIVATE_OBJECT);
			if self.mrFpRedGear then
				self.realReductionOverlay1:render();
				self.realReductionOverlay2:render();
			end;
		end;
	end;
end;

function mrFullPowershift:setNextGear(gearDown, noEventSend)
	if self.isServer then
		--server side : compute the new gear
		--print(self.time .. " mrFullPowershift:setNextGear - gearDown=" .. tostring(gearDown) .. " / self.mrFpCurrentGear=" .. tostring(self.mrFpCurrentGear));
		local wantedGear = nil;
		
		if gearDown then
			if self.mrFpCurrentGear > self.mrFpMinGear then
				wantedGear = self.mrFpCurrentGear - 1;
			end;
		else
			if self.mrFpCurrentGear < self.mrFpMaxGear then
				wantedGear = self.mrFpCurrentGear + 1;
			end;
		end;
		
		if wantedGear ~= nil then
			self:setGear(wantedGear);
		end;
	end;
	
	if noEventSend == nil or noEventSend == false then
		if g_server == nil then
			--client side : tell the server what we want to do (gear up or down)
			g_client:getServerConnection():sendEvent(mrFullPowershiftSetNextGearEvent:new(self, gearDown));
		end;
	end;
end;

function mrFullPowershift:setGear(gearNum, noEventSend)
	--print(self.time .. " mrFullPowershift - setGear - gearNum=" .. tostring(gearNum));
	self.mrFpCurrentGear = gearNum;
	
	if self.isServer then
		if gearNum == 0 then
			--neutral
			self.realShuttleDirection = 0;
			self.mrFpGearShiftingNeeded = false;
		else
			--timer to set the gear
			self.mrFpGearShiftingNeeded = true;
			self.mrFpGearShiftingTime = self.time + self.mrFpGearTimeToShiftGear;
			
			--disengage the clutch during the gear shifting
			self.realClutchEngaged = false;
		end;
	end;
	
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			-- server side :  send the new "mrFpCurrentGear" to all the clients
			g_server:broadcastEvent(mrFullPowershiftSetGearEvent:new(self, gearNum), nil, nil, self);
		else
			-- client side : tell the server the gearNum we want it to set
			g_client:getServerConnection():sendEvent(mrFullPowershiftSetGearEvent:new(self, gearNum));
		end;
	end;
end;

function mrFullPowershift:setReduction(reducedGear, noEventSend)
	--print(self.time .. " mrFullPowershift - setReduction - reducedGear=" .. tostring(reducedGear));
	self.mrFpRedGear = reducedGear;
	
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			-- server side :  send the new "mrFpRedGear" to all the clients
			g_server:broadcastEvent(mrFullPowershiftSetReductionEvent:new(self, reducedGear), nil, nil, self);
		else
			-- client side : tell the server the reducedGear we want it to set
			g_client:getServerConnection():sendEvent(mrFullPowershiftSetReductionEvent:new(self, reducedGear));
		end;
	end;
end;

function mrFullPowershift:setFullPowershiftModeOn()
	--print(self.time .. " mrFullPowershift:setFullPowershiftModeOn");
	self.mrFpIsOn = true;
	
	if self.isServer then
		self.realDirectInverser = false;
		self.realShuttleIsManual = true;
		self.realClutchEngaged = true;
		if self.realShuttleDirection < 0 then
			self:setGear(self.mrFpRevDefaultGear);
		else
			self:setGear(self.mrFpFwdDefaultGear);
		end;
	end;
end;

function mrFullPowershift:setFullPowershiftModeOff()
	--print(self.time .. " mrFullPowershift:setFullPowershiftModeOff");
	self.mrFpIsOn = false;
	
	if self.isServer then
		self.realClutchEngaged = true;
		self:setManualGear(false);
		self.mrFpGearShiftingNeeded = false;
	end;
end;

function mrFullPowershift:doGearShift()
	--print(self.time .. " mrFullPowershift:doGearShift");
	--only the server should call this
	
	self.realClutchEngaged = true;
	
	if self.mrFpCurrentGear < 0 then
		--reverse
		self.realShuttleDirection = -1;
		local gearMaxSpeed = {};
		if self.mrFpRedGear and self.mrFpRevGears[math.abs(self.mrFpCurrentGear)].redSpeed ~= 0 then
			gearMaxSpeed = self.mrFpRevGears[math.abs(self.mrFpCurrentGear)].redSpeed; --m/s
		else
			gearMaxSpeed = self.mrFpRevGears[math.abs(self.mrFpCurrentGear)].speed; --m/s
		end;
		local gearMinSpeedForMaxPower = gearMaxSpeed * self.mrFpMinRpmAtMaxPower / self.mrFpMaxRpm; --m/s
		self:setManualGear(true, self.mrFpMinMaxRpmRatio, gearMinSpeedForMaxPower, gearMaxSpeed);
	else
		--forward
		self.realShuttleDirection = 1;
		local gearMaxSpeed = {};
		if self.mrFpRedGear and self.mrFpFwdGears[self.mrFpCurrentGear].redSpeed ~= 0 then
			gearMaxSpeed = self.mrFpFwdGears[self.mrFpCurrentGear].redSpeed; --m/s
		else
			gearMaxSpeed = self.mrFpFwdGears[self.mrFpCurrentGear].speed; --m/s
		end;
		local gearMinSpeedForMaxPower = gearMaxSpeed * self.mrFpMinRpmAtMaxPower / self.mrFpMaxRpm; --m/s
		self:setManualGear(true, self.mrFpMinMaxRpmRatio, gearMinSpeedForMaxPower, gearMaxSpeed);
		--print(self.time .. " mrFullPowershift:doGearShift - forward : mrFpMinMaxRpmRatio / gearMinSpeedForMaxPower / gearMaxSpeed = "..tostring(self.mrFpMinMaxRpmRatio) .. " / " .. tostring(gearMinSpeedForMaxPower) .. " / " .. tostring(gearMaxSpeed));
	end;
end;


mrFullPowershiftSetGearEvent = {};
mrFullPowershiftSetGearEvent_mt = Class(mrFullPowershiftSetGearEvent, Event);

InitEventClass(mrFullPowershiftSetGearEvent, "mrFullPowershiftSetGearEvent");

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

function mrFullPowershiftSetGearEvent:new(object, gearNum)
	local self = mrFullPowershiftSetGearEvent:emptyNew();
	self.gearNum = gearNum;
	self.object = object;
	return self;
end;

function mrFullPowershiftSetGearEvent:readStream(streamId, connection)
	--both clients and server can receive this event
	local id = streamReadInt32(streamId);
	self.gearNum = streamReadInt32(streamId);
	self.object = networkGetObject(id);
	self:run(connection);
end;

function mrFullPowershiftSetGearEvent:writeStream(streamId, connection)
	--both clients and server can send this event
	streamWriteInt32(streamId, networkGetObjectId(self.object));
	streamWriteInt32(streamId, self.gearNum);
end;

function mrFullPowershiftSetGearEvent:run(connection)
	----both clients and server can "run" this event (after reading it)	
	
	if not connection:getIsServer() then
		--we are the server, and so, we need to broadcast the new gearnum to all the other clients
		self.object:setGear(self.gearNum, false);	
	else
		--we are the client, and so we just receive the new gearnum
		self.object:setGear(self.gearNum, true);	
	end;
end;


mrFullPowershiftSetReductionEvent = {};
mrFullPowershiftSetReductionEvent_mt = Class(mrFullPowershiftSetReductionEvent, Event);

InitEventClass(mrFullPowershiftSetReductionEvent, "mrFullPowershiftSetReductionEvent");

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

function mrFullPowershiftSetReductionEvent:new(object, reducedGear)
	local self = mrFullPowershiftSetReductionEvent:emptyNew();
	self.reducedGear = reducedGear;
	self.object = object;
	return self;
end;

function mrFullPowershiftSetReductionEvent:readStream(streamId, connection)
	--both clients and server can receive this event
	local id = streamReadInt32(streamId);
	self.reducedGear = streamReadBool(streamId);
	self.object = networkGetObject(id);
	self:run(connection);
end;

function mrFullPowershiftSetReductionEvent:writeStream(streamId, connection)
	--both clients and server can send this event
	streamWriteInt32(streamId, networkGetObjectId(self.object));
	streamWriteBool(streamId, self.reducedGear);
end;

function mrFullPowershiftSetReductionEvent:run(connection)
	----both clients and server can "run" this event (after reading it)
	
	if not connection:getIsServer() then
		--we are the server, and so, we need to broadcast the new reducedGear to all the other clients
		self.object:setReduction(self.reducedGear, false);
	else
		--we are the client, and so we just receive the new reducedGear
		self.object:setReduction(self.reducedGear, true);
	end;
end;


mrFullPowershiftSetNextGearEvent = {};
mrFullPowershiftSetNextGearEvent_mt = Class(mrFullPowershiftSetNextGearEvent, Event);

InitEventClass(mrFullPowershiftSetNextGearEvent, "mrFullPowershiftSetNextGearEvent");

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

function mrFullPowershiftSetNextGearEvent:new(object, gearDown)
	local self = mrFullPowershiftSetNextGearEvent:emptyNew();
	self.gearDown = gearDown;
	self.object = object;
	return self;
end;

function mrFullPowershiftSetNextGearEvent:readStream(streamId, connection)
	--only the server receive the event
	local id = streamReadInt32(streamId);
	self.gearDown = streamReadBool(streamId);
	self.object = networkGetObject(id);
	self:run(connection);
end;

function mrFullPowershiftSetNextGearEvent:writeStream(streamId, connection)
	--only the clients send this event
	streamWriteInt32(streamId, networkGetObjectId(self.object));
	streamWriteBool(streamId, self.gearDown);
end;

function mrFullPowershiftSetNextGearEvent:run(connection)
	--only the server "run" this event (after reading it)	
	self.object:setNextGear(self.gearDown, true);	
end;