--[[************************************************************************************************************************************************************
    
    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(savegame) 
    --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("textures/turtle1.dds", self.baseDirectory), 0.91, 0.775-0.0015, 0.03, 0.02);
    self.realReductionOverlay2 = Overlay:new("realReductionOverlay2", Utils.getFilename("textures/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;