--
-- MixerWagonFoodSwitch
--
-- @Interface: 1.4.1.0 b5332
-- @Author: LS-Modcompany / kevink98
-- @Date: 31.08.2019
-- @Version: 1.0.0.0
--
-- @Support: LS-Modcompany
--
-- Changelog:
--
--
-- 	v1.0.0.0 (31.08.2019):
--
-- Notes:
--      - Special Edition for Mach1--Andy
--      - Convert script from Marhu (ls17)
--      - Permissions at Mach1--Andy
--
-- ToDo:
-- 
--
--

MixerWagonFoodSwitch = {};
MixerWagonFoodSwitch.modName = g_currentModName;

function MixerWagonFoodSwitch.prerequisitesPresent(specializations)    
    return SpecializationUtil.hasSpecialization(Trailer, specializations) and SpecializationUtil.hasSpecialization(TurnOnVehicle, specializations);
end;

function MixerWagonFoodSwitch.registerFunctions(vehicleType)
	SpecializationUtil.registerFunction(vehicleType, "getSpec", MixerWagonFoodSwitch.getSpec);
end;

function MixerWagonFoodSwitch.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", MixerWagonFoodSwitch);
    SpecializationUtil.registerEventListener(vehicleType, "onReadStream", MixerWagonFoodSwitch);
    SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", MixerWagonFoodSwitch);
    SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", MixerWagonFoodSwitch);
    SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", MixerWagonFoodSwitch);
end;

function MixerWagonFoodSwitch:getSpec()
    return self[string.format("spec_%s.mixerWagonFoodSwitch", MixerWagonFoodSwitch.modName)];
end

function MixerWagonFoodSwitch:onPostLoad(savegame)
    local spec = self:getSpec();

    self.getFillUnitAllowsFillType = Utils.overwrittenFunction(self.getFillUnitAllowsFillType, MixerWagonFoodSwitch.getFillUnitAllowsFillType);
    self.addFillUnitFillLevel = Utils.overwrittenFunction(self.addFillUnitFillLevel, MixerWagonFoodSwitch.addFillUnitFillLevel);
    
    spec.mixType = FillType.FORAGE;
	local mixType = getXMLString(self.xmlFile, "vehicle.mixerWagon.mixerWagonFillTypes#mixType");
    if mixType ~= nil then
		local mixTypInt = g_fillTypeManager.nameToIndex[mixType:upper()];
        if mixTypInt ~= nil then
			spec.mixType = mixTypInt;
		else
			print("Warning: InvalidmixerWagonFoodSwitchMixType '"..mixType.."' in '"..self.configFileName.. "'.");
		end;
    end;
    
	spec.currentFood = 1;
	spec.mixerWagonFillTypesFood = {};
    spec.fillTypeToMixerWagonFillTypeFood = {};
	spec.mixerWagonFillTypesFood[1] = {};
	spec.mixerWagonFillTypesFood[1].food = spec.mixType;
	spec.mixerWagonFillTypesFood[1].types = self.spec_mixerWagon.mixerWagonFillTypes;
    spec.fillTypeToMixerWagonFillTypeFood[1] = self.spec_mixerWagon.fillTypeToMixerWagonFillType;
    spec.fillTypeToMixerWagonFillType = self.spec_mixerWagon.fillTypeToMixerWagonFillType;
    
    local i = 2;
    while true do
        local baseName = string.format("vehicle.mixerWagon.mixerWagonFillTypes%d", i);
        if not hasXMLProperty(self.xmlFile, baseName) then
            break;
        end;
		
		spec.mixerWagonFillTypesFood[i] = {};
		spec.mixerWagonFillTypesFood[i].types ={};
		spec.fillTypeToMixerWagonFillTypeFood[i] = {};
       
        spec.mixerWagonFillTypesFood[i].food = FillType.FORAGE;
		local mixType = getXMLString(self.xmlFile,  baseName.."#mixType");
		if mixType ~= nil then
			local mixTypInt = g_fillTypeManager.nameToIndex[mixType:upper()];
			if mixTypInt ~= nil then
				spec.mixerWagonFillTypesFood[i].food = mixTypInt;
			else
				print("Warning: InvalidmixerWagonFoodSwitchMixType '"..mixType.."' in '"..self.configFileName.. "'.");
			end;
		end;

        local sumRatio = 0;
        local j = 0;
        while true do
            local fillTypeKey = baseName..string.format(".mixerWagonFillType(%d)", j);
            if not hasXMLProperty(self.xmlFile, fillTypeKey) then
                break;
            end;
            local entry = {};
            entry.fillTypes = {};
            local k = 0;
            while true do
                local fillTypeKey = fillTypeKey..string.format(".fillType(%d)", k);
                if not hasXMLProperty(self.xmlFile, fillTypeKey) then
                    break;
                end;
                local fillTypeStr = getXMLString(self.xmlFile, fillTypeKey.."#fillType");
                if fillTypeStr ~= nil then
                    local fillTypeIndex = g_fillTypeManager.nameToIndex[fillTypeStr:upper()];
                    if fillTypeIndex ~= nil and fillTypeIndex ~= FillType.GRASS_WINDROW then
						if spec.fillTypeToMixerWagonFillTypeFood[i][fillTypeIndex] == nil then
                            entry.fillTypes[fillTypeIndex] = true;
                            spec.fillTypeToMixerWagonFillTypeFood[i][fillTypeIndex] = entry;
                        else
                            g_logManager:xmlWarning(self.configFileName, "MixerWagonFillType '%s' in '%s' already used! Ignoring it!", fillTypeKey, fillTypeStr);
                        end;
                    else
                        g_logManager:xmlWarning(self.configFileName, "FillType '%s' not defined for mixerWagonFillType '%s'!", fillTypeStr, fillTypeKey);
                    end;
                end;
                k = k + 1;
            end;
            entry.name = Utils.getNoNil(getXMLString(self.xmlFile, fillTypeKey.."#name"), "unknown");
            entry.minPercentage = Utils.getNoNil(getXMLFloat(self.xmlFile, fillTypeKey.."#minPercentage"), 0) * 0.01;
            entry.maxPercentage = Utils.getNoNil(getXMLFloat(self.xmlFile, fillTypeKey.."#maxPercentage"), 100) * 0.01;
            entry.ratio = entry.maxPercentage  - entry.minPercentage;
            entry.fillLevel = 0;
            if next(entry.fillTypes) ~= nil then
                sumRatio = sumRatio + entry.ratio;
                table.insert(spec.mixerWagonFillTypesFood[i].types, entry);
            end;
			j = j + 1;
        end

        for k = 1, i do
            for _, entry in ipairs(spec.mixerWagonFillTypesFood[k]) do
                entry.ratio = entry.ratio / sumRatio;
            end;
        end;
		i = i + 1;
    end;
    
    if savegame ~= nil then
        local key = string.format("%s.%s.mixerWagonFoodSwitch", savegame.key, self.customEnvironment)
		local foodType = getXMLString(savegame.xmlFile, key..".mixerWagonFoodType#name");
		local foodTypeInt = g_fillTypeManager.nameToIndex[foodType];
		if foodTypeInt ~= nil and foodTypeInt ~= spec.mixType then
			self:addFillUnitFillLevel(self:getOwnerFarmId(), self.spec_mixerWagon.fillUnitIndex, 0, FillType.UNKNOWN, true);
			for i=1, table.getn(spec.mixerWagonFillTypesFood) do
				if spec.mixerWagonFillTypesFood[i].food == foodTypeInt then
					spec.mixType = spec.mixerWagonFillTypesFood[i].food;
					spec.mixerWagonFillTypes = spec.mixerWagonFillTypesFood[i].types;
                    spec.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillTypeFood[i];
					spec.currentFood = i;
                    self.spec_mixerWagon.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillType;
                    self.spec_mixerWagon.mixerWagonFillTypes = spec.mixerWagonFillTypes;
					break;
				end
            end;
            for i, entry in ipairs(spec.mixerWagonFillTypes) do
                local fillTypeKey = savegame.key..string.format(".mixerWagon.fillType(%d)#fillLevel", i-1);
                local fillLevel = Utils.getNoNil(getXMLFloat(savegame.xmlFile, fillTypeKey), 0);
                if fillLevel > 0 then
                    self:addFillUnitFillLevel(self:getOwnerFarmId(), self.spec_mixerWagon.fillUnitIndex, fillLevel, next(entry.fillTypes), ToolType.UNDEFINED, nil);
                end;
            end;
		end;
    end;
end;

function MixerWagonFoodSwitch:saveToXMLFile(xmlFile, key, usedModNames)
    local spec = self:getSpec();
    setXMLString(xmlFile, key .. ".mixerWagonFoodType#name", g_fillTypeManager.indexToName[spec.mixType]);
end;

function MixerWagonFoodSwitch:onReadStream(streamId, connection)
    local spec = self:getSpec();
	local currentFood = streamReadInt8(streamId)
	if spec.currentFood ~= currentFood then
		spec.mixType = spec.mixerWagonFillTypesFood[currentFood].food;
		spec.mixerWagonFillTypes = spec.mixerWagonFillTypesFood[currentFood].types;
		spec.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillTypeFood[currentFood];
		spec.currentFood = currentFood;
        self.spec_mixerWagon.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillType;
        self.spec_mixerWagon.mixerWagonFillTypes = spec.mixerWagonFillTypes;
	end;
end;

function MixerWagonFoodSwitch:onWriteStream(streamId, connection)
    local spec = self:getSpec();
	streamWriteInt8(streamId, spec.currentFood);
end;

function MixerWagonFoodSwitch:onReadUpdateStream(streamId, timestamp, connection)
	if connection:getIsServer() then
        local spec = self:getSpec();
		local currentFood = streamReadInt8(streamId);
		if spec.currentFood ~= currentFood then
			self:setUnitFillLevel(self.spec_mixerWagon.fillUnitIndex, 0, FillType.UNKNOWN, true);
			spec.mixType = spec.mixerWagonFillTypesFood[currentFood].food;
			spec.mixerWagonFillTypes = spec.mixerWagonFillTypesFood[currentFood].types;
			spec.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillTypeFood[currentFood];
			spec.currentFood = currentFood;
            self.spec_mixerWagon.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillType;
            self.spec_mixerWagon.mixerWagonFillTypes = spec.mixerWagonFillTypes;
		end;
	end;
end;

function MixerWagonFoodSwitch:onWriteUpdateStream(streamId, connection, dirtyMask)
	if not connection:getIsServer() then
        local spec = self:getSpec();
		streamWriteInt8(streamId, spec.currentFood);
	end;
end;

function MixerWagonFoodSwitch:getFillUnitAllowsFillType(superFunc, fillUnitIndex, fillTypeIndex)
    local spec = self:getSpec();
    local fillLevel = self:getFillUnitFillLevel(fillUnitIndex);
    if fillLevel <= (self:getFillUnitCapacity(fillUnitIndex) * self:getFillTypeChangeThreshold()) then
		local mixerWagonFillType = spec.fillTypeToMixerWagonFillType[fillTypeIndex];
		if mixerWagonFillType == nil then
			for i=1, table.getn(spec.fillTypeToMixerWagonFillTypeFood) do
				local mixerWagonFillTypeNew = spec.fillTypeToMixerWagonFillTypeFood[i][fillTypeIndex];
				if mixerWagonFillTypeNew ~= nil then
					spec.mixType = spec.mixerWagonFillTypesFood[i].food;
					spec.mixerWagonFillTypes = spec.mixerWagonFillTypesFood[i].types;
					spec.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillTypeFood[i];
                    spec.currentFood = i;
                    self.spec_mixerWagon.fillTypeToMixerWagonFillType = spec.fillTypeToMixerWagonFillType;
                    self.spec_mixerWagon.mixerWagonFillTypes = spec.mixerWagonFillTypes;
					break;
				end;
			end;
		end;
    end;
    return superFunc(self, fillUnitIndex, fillTypeIndex);
end;

function MixerWagonFoodSwitch:addFillUnitFillLevel(superFunc, farmId, fillUnitIndex, fillLevelDelta, fillTypeIndex, toolType, fillPositionData)
    local specSwitch = self:getSpec();
    local spec = self.spec_mixerWagon;
    if fillUnitIndex ~= spec.fillUnitIndex then
        return superFunc(self, farmId, fillUnitIndex, fillLevelDelta, fillTypeIndex, toolType, fillPositionData);
    end;
    local oldFillLevel = self:getFillUnitFillLevel(fillUnitIndex);
    local mixerWagonFillType = spec.fillTypeToMixerWagonFillType[fillTypeIndex];
    if (fillTypeIndex == FillType.FORAGE or fillTypeIndex == specSwitch.mixType) and fillLevelDelta > 0 then
        for _, entry in pairs(spec.mixerWagonFillTypes) do
            local delta = fillLevelDelta * entry.ratio;
            self:addFillUnitFillLevel(farmId, fillUnitIndex, delta, next(entry.fillTypes), toolType, fillPositionData);
        end;
        return fillLevelDelta;
    end;
    if mixerWagonFillType == nil then
        if fillLevelDelta < 0 and oldFillLevel > 0 then
            fillLevelDelta = math.max(fillLevelDelta, -oldFillLevel);
            local newFillLevel = 0;
            for _, entry in pairs(spec.mixerWagonFillTypes) do
                local entryDelta = fillLevelDelta * (entry.fillLevel / oldFillLevel);
                entry.fillLevel = math.max(entry.fillLevel + entryDelta, 0);
                newFillLevel = newFillLevel + entry.fillLevel;
            end;
            if newFillLevel < 0.1 then
                for _, entry in pairs(spec.mixerWagonFillTypes) do
                    entry.fillLevel = 0;
                end;
                fillLevelDelta = -oldFillLevel;
            end;
            self:raiseDirtyFlags(spec.dirtyFlag);
            local ret = superFunc(self, farmId, fillUnitIndex, fillLevelDelta, fillTypeIndex, toolType, fillPositionData);
            return ret;
        end;
        return 0;
    end

    local capacity = self:getFillUnitCapacity(fillUnitIndex);
    local free = capacity - oldFillLevel;
    if fillLevelDelta > 0 then
        mixerWagonFillType.fillLevel = mixerWagonFillType.fillLevel + math.min(free, fillLevelDelta);
        spec.activeTimer = spec.activeTimerMax;
    else
        mixerWagonFillType.fillLevel = math.max(0, mixerWagonFillType.fillLevel + fillLevelDelta);
    end;

    local newFillLevel = 0;
    for _, mixerWagonFillType in pairs(spec.mixerWagonFillTypes) do
        newFillLevel = newFillLevel + mixerWagonFillType.fillLevel;
    end;
    newFillLevel = MathUtil.clamp(newFillLevel, 0, self:getFillUnitCapacity(fillUnitIndex));

    local newFillType = FillType.UNKNOWN;
    local isSingleFilled = false;
    local isForageOk = false;

    for _, mixerWagonFillType in pairs(spec.mixerWagonFillTypes) do
        if newFillLevel == mixerWagonFillType.fillLevel then
            isSingleFilled = true;
            newFillType = next(mixerWagonFillType.fillTypes);
            break;
        end;
    end;
    if not isSingleFilled then
        isForageOk = true;
        for _, mixerWagonFillType in pairs(spec.mixerWagonFillTypes) do
            if mixerWagonFillType.fillLevel < mixerWagonFillType.minPercentage * newFillLevel - 0.01 or mixerWagonFillType.fillLevel > mixerWagonFillType.maxPercentage * newFillLevel + 0.01 then
                isForageOk = false;
                break;
            end;
        end;
    end;
    if isForageOk then
        if specSwitch.mixType ~= nil then
            newFillType = specSwitch.mixType;
        else
            newFillType = FillType.FORAGE;
        end;
    elseif not isSingleFilled then
        newFillType = FillType.FORAGE_MIXING;
    end
    self:raiseDirtyFlags(spec.dirtyFlag);
    self:setFillUnitFillType(fillUnitIndex, newFillType);
    
    return FillUnit.addFillUnitFillLevel(self, farmId, fillUnitIndex, newFillLevel-oldFillLevel, newFillType, toolType, fillPositionData);
end;