--
-- VariableBody
-- This is a specialization for trailers, which have a extra bodywork for loading chaff/grass?
--
--
-- @author:        fruktor (wwww.modding-society.de)
-- @version:    v2.0
-- @date:        22/11/16
-- @history:    v0.1 - inital implementation
--                v2.0 - edit to FS17 by igor29381
-- Copyright (C)
--

VariableBody = {};

function VariableBody.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Trailer, specializations) and
        SpecializationUtil.hasSpecialization(Fillable, specializations)
end;

function VariableBody:load(savegame)
    self.changeVariableBody = SpecializationUtil.callSpecializationsFunction("changeVariableBody");
    local bulk2Category = FillUtil.registerFillTypeCategory("bulk2");
    local fillTypes = {FillUtil.FILLTYPE_POTATO, FillUtil.FILLTYPE_SUGARBEET, FillUtil.FILLTYPE_MANURE, FillUtil.FILLTYPE_CHAFF, FillUtil.FILLTYPE_SILAGE, FillUtil.FILLTYPE_WOODCHIPS};
    for i=1, #fillTypes do
        FillUtil.addFillTypeToCategory(bulk2Category, fillTypes[i]);
    end;
    self.varBody = {};
    local activeIdx = getXMLInt(self.xmlFile, "vehicle.variableBody#activeIdx");
    if activeIdx ~= nil then
        self.varBody.activeIdx = activeIdx;
    else
        self.varBody.activeIdx = 1;
    end;
    local activeIdx = getXMLInt(self.xmlFile, "vehicle.variableBody#activeSubIdx");
    if activeIdx ~= nil then
        self.varBody.activeSubIdx = activeSubIdx;
    else
        self.varBody.activeSubIdx = 1;
    end;
    self.varBody.nodes = {};
    local i=0;
    local j=0;
    while true do
        local path = string.format("vehicle.variableBody.node(%d)", i);
        local name = getXMLString(self.xmlFile, path .. "#name");
        if name == nil then
            break;
        end;
        local capacity = getXMLInt(self.xmlFile, path .. "#capacity");
        local fillTypeCategory = getXMLString(self.xmlFile, path .. "#fillTypeCategories");
        if fillTypeCategory then
            fillTypeCategory = FillUtil.fillTypeCategoryNameToInt[fillTypeCategory];
        end;
        local fillTypes = FillUtil.categoryToFillTypes[fillTypeCategory];
        --####
        local parts = {};
        j=0;
        local k=1;
        local str = getXMLString(self.xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#index%d", i, j, k));
        while str ~= nil do
            local subparts = {};
            local allowFill = getXMLInt(self.xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#allowFill", i, j));
            if allowFill ~= nil then
                subparts.allowFill = allowFill;
            else
                subparts.allowFill = 1;
            end;
            subparts.objects = {};
            while str ~= nil do
                local tmpIdx = Utils.indexToObject(self.components, str)
                k=k+1;
                table.insert(subparts.objects, tmpIdx);
                str = getXMLString(self.xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#index%d", i, j, k));
            end;
            subparts.subPartCount = k-1;
            if subparts ~= nil then
                table.insert(parts, subparts);
            else
                break;
            end;
            j=j+1;
            k=1;
            str = getXMLString(self.xmlFile, string.format("vehicle.variableBody.node(%d).part(%d)#index%d", i, j, k));
        end;
        if j == 0 then
            local subparts = {};
            subparts.objects = nil;
            subparts.allowFill = 1;
            subparts.subPartCount = 0;
            table.insert(parts, subparts);
        end;
        local entry={};
        entry.name = name;
        entry.capacity = capacity;
        entry.index = index;
        entry.parts = parts;
        entry.partCount = j;
        entry.fillTypes = fillTypes;
        table.insert(self.varBody.nodes, entry);
        i=i+1;
    end;
    for i=1, #self.fillVolumes do
        local fillVolume = self.fillVolumes[i];
        fillVolume.volume = createFillPlaneShape(fillVolume.baseNode, "fillPlane", self.varBody.nodes[i].capacity, fillVolume.maxDelta, fillVolume.maxSurfaceAngle, math.rad(35), fillVolume.maxSubDivEdgeLength, fillVolume.allSidePlanes);
        setVisibility(fillVolume.volume, false);
        link(fillVolume.baseNode, fillVolume.volume);
    end;
    if self.varBody.activeIdx > i then
        self.varBody.activeIdx = i;
    end;
    self.varBody.nodeCount=table.getn(self.varBody.nodes);
    if savegame then
        local idx = Utils.getNoNil(getXMLInt(savegame.xmlFile, savegame.key.."#varBody.activeIdx"),1);
        if idx > table.getn(self.varBody.nodes) then
            idx = 1;
        end;
        local idx2 = Utils.getNoNil(getXMLInt(savegame.xmlFile, savegame.key.."#varBody.activeSubIdx"),1);
        if idx2 > table.getn(self.varBody.nodes[idx].parts) then
            idx2 = 1;
        end;
        self:changeVariableBody(idx, idx2, true);
        local fillLevel = getXMLFloat(savegame.xmlFile, savegame.key.."#fillLevels");
        local currentFillType = getXMLString(savegame.xmlFile, savegame.key.."#fillTypes");
        currentFillType = FillUtil.fillTypeNameToInt[currentFillType];
        if fillLevel ~= nil and currentFillType ~= nil then
            self:setFillLevel(fillLevel, currentFillType);
            self:resetFillLevelIfNeeded(currentFillType);
        end;
    end;
end;

function VariableBody:readStream(streamId, connection)
    local state = streamReadInt8(streamId);
    local state2 = streamReadInt8(streamId);
    self:changeVariableBody(state, state2, true);
    local fillLevel = streamReadFloat32(streamId);
    local currentFillType = self.fillUnits[1].currentFillType;
    if fillLevel > 0 and currentFillType > 0 then
        self:setFillLevel(fillLevel, currentFillType);
        self:resetFillLevelIfNeeded(currentFillType);
    end;
end;

function VariableBody:writeStream(streamId, connection)
    streamWriteInt8(streamId, self.varBody.activeIdx);
    streamWriteInt8(streamId, self.varBody.activeSubIdx);
    streamWriteFloat32(streamId, self.fillUnits[1].fillLevel);
end;

function VariableBody:delete()
end;

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

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

function VariableBody:update(dt)
    if self.varTip ~= nil then
        if self.varTip.activeTrailerIdx ~= self.varTip.trailerNr then
            return;
        end;
    end;
    if self:getIsActive() then
        if self:getIsActiveForInput() then
            if self.tipState == Trailer.TIPSTATE_CLOSED then
                local nodes = self.varBody.nodes;
                if self.fillUnits[1].fillLevel == 0 then
                    if InputBinding.hasEvent(InputBinding.VARIABLEBODY_change) then
                        local idx = self.varBody.activeIdx + 1;
                        if idx > self.varBody.nodeCount then
                            idx = 1
                        end;
                        local idx2 = self.varBody.activeSubIdx;
                        if idx2 > nodes[idx].partCount then
                            idx2 = 1;
                        end
                        self:changeVariableBody(idx, idx2);
                    end;
                end;
                if string.match("roof", nodes[self.varBody.activeIdx].name) then
                    if InputBinding.hasEvent(InputBinding.VARIABLEBODY_changePart) then
                        local idx = self.varBody.activeSubIdx + 1;
                        if idx > nodes[self.varBody.activeIdx].partCount then
                            idx = 1
                        end;
                        self:changeVariableBody(self.varBody.activeIdx, idx);
                    end;
                end;
            end;
        end;
    end;
end;

function VariableBody:updateTick(dt)
end;
-- (888)
function VariableBody:draw()
    if self.varTip ~= nil then
        if self.varTip.activeTrailerIdx ~= self.varTip.trailerNr then
            return;
        end;
    end;
    if self.tipState == Trailer.TIPSTATE_CLOSED then
        if self.fillUnits[1].fillLevel == 0 then
            if self.varBody.nodeCount> 1 then
                g_currentMission:addHelpButtonText(string.format(g_i18n:getText("VARIABLEBODY_display"), self.varBody.activeIdx, self.varBody.nodeCount),
                InputBinding.VARIABLEBODY_change);
            end;
        end;
        if string.match("roof", self.varBody.nodes[self.varBody.activeIdx].name) then
            g_currentMission:addHelpButtonText(g_i18n:getText("VARIABLEBODY_displayPart"), InputBinding.VARIABLEBODY_changePart );
        end;
    end;
end;

function VariableBody:getSaveAttributesAndNodes(nodeIdent)
    local attributes = ' varBody.activeIdx="'..tostring(self.varBody.activeIdx)..'" varBody.activeSubIdx="'..tostring(self.varBody.activeSubIdx)..'"';
    local node = nil;
    return attributes, node;
end;

function VariableBody:changeVariableBody(state, state2, noEventSend)
    VariableBodyEvent.sendEvent(self, state, state2, noEventSend);
    for i=1, table.getn(self.varBody.nodes) do
        if self.varBody.nodes[i].partCount > 0 then
            for j=1, table.getn(self.varBody.nodes[i].parts) do
                if i ~= state or j ~= state2 then
                    for k=1,table.getn(self.varBody.nodes[i].parts[j].objects) do
                        setVisibility(self.varBody.nodes[i].parts[j].objects[k], false);
                    end;
                end
            end;
        end;
    end;
    local stateNodes = self.varBody.nodes[state];
    if stateNodes ~= nil then
        if stateNodes.partCount > 0 then
            if stateNodes.parts[state2].subPartCount > 0 then
                for k=1,table.getn(stateNodes.parts[state2].objects) do
                    setVisibility(stateNodes.parts[state2].objects[k], true);
                end;
            end;
        end;
    end;
    self.fillUnits[1].fillVolumeIndex = state;
    self.fillUnits[1].fillTypes = stateNodes.fillTypes;
    self.fillUnits[1].capacity = stateNodes.capacity;
    if self.cp and self.cp.workTools and self.cp.workTools[1] then
        self.cp.workTools[1].fillTypes = stateNodes.fillTypes;
    end;
    if stateNodes.parts[state2].allowFill == 1 then
        self.allowFillFromAir = true;
    else
        self.allowFillFromAir = false;
    end;
    self.varBody.activeIdx = state;
    self.varBody.activeSubIdx = state2;
end;


---------------------------------------------------------------------------------------

VariableBodyEvent = {};
VariableBodyEvent_mt = Class(VariableBodyEvent, Event);

InitEventClass(VariableBodyEvent, "VariableBodyEvent");

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

function VariableBodyEvent:new(vehicle, state, state2)
    local self = VariableBodyEvent:emptyNew()
    self.vehicle = vehicle;
    self.state = state;
    self.state2 = state2;
    return self;
end;

function VariableBodyEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
    self.state = streamReadInt8(streamId);
    self.state2 = streamReadInt8(streamId);
    self.vehicle = networkGetObject(id);
    self:run(connection);
end;

function VariableBodyEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
    streamWriteInt8(streamId, self.state);
    streamWriteInt8(streamId, self.state2);
end;

function VariableBodyEvent:run(connection)
    self.vehicle:changeVariableBody(self.state, self.state2, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(VariableBodyEvent:new(self.vehicle, self.state, self.state2), nil, connection, self.vehicle);
    end;
end;

function VariableBodyEvent.sendEvent(vehicle, state, state2, noEventSend)
    if vehicle == nil then
        print("--------> VariableBody erRoR :/ ");
    end;
    if state ~= vehicle.varBody.activeIdx or state2 ~= vehicle.varBody.activeSubIdx then
        if noEventSend == nil or noEventSend == false then
            if g_server ~= nil then
                g_server:broadcastEvent(VariableBodyEvent:new(vehicle, state, state2), nil, nil, vehicle);
            else
                g_client:getServerConnection():sendEvent(VariableBodyEvent:new(vehicle, state, state2));
            end;
        end;
    end;
end;
