--[[
Interface: 1.6.0.0 b9173

Copyright (C) GtX (Andy), 2018

Author: GtX | Andy
Date: 09.12.2018
Version: 1.0.0.0

Contact:
https://forum.giants-software.com
https://github.com/GtX-Andy

History:
V 1.1.0.0 @ 17.07.2019 - Added support to position 'waterAddon' parts on unsupported mods.
V 1.2.0.0 @ 25.09.2020 - Code optimisations, added support to position 'milkAddon' parts on unsupported mods, added new setting options.

Important:
Not to be added to any mods / maps or modified from its current release form.
No changes are to be made to this script without permission from GtX | Andy

Darf nicht zu Mods / Maps hinzugefügt oder von der aktuellen Release-Form geändert werden.
An diesem Skript dürfen ohne Genehmigung von GtX | Andy keine Änderungen vorgenommen werden
]]


AnimalPenExtensionManager = {}
local AnimalPenExtensionManager_mt = Class(AnimalPenExtensionManager)

function AnimalPenExtensionManager:new(isServer, isClient, buildId, versionString, customEnvironment, baseDirectory, xmlFilename)
    if g_animalPenExtensionManager ~= nil then
        return
    end

    local self = {}
    setmetatable(self, AnimalPenExtensionManager_mt)

    self.isServer = isServer
    self.isClient = isClient

    self.buildId = buildId
    self.versionString = versionString

    self.customEnvironment = customEnvironment
    self.baseDirectory = baseDirectory

    self.baseBuildings = {}
    self.baseBuildingsLoaded = 0
    self.baseBuildingsXMLFilename = xmlFilename

    self.universalPartsXMLFilename = baseDirectory .. "xml/UniversalParts.xml"

    self.texts = {
        water = g_i18n:getText("fillType_water", customEnvironment),
        waterFillLevel = g_i18n:getText("info_waterFillLevel", customEnvironment),
        stopFilling = g_i18n:getText("action_siloStopFilling", customEnvironment),
        startFilling = g_i18n:getText("action_siloStartFilling", customEnvironment),
        noAnimalsWarning = g_i18n:getText("animalPenExtension_noAnimalsWarning", customEnvironment),
        callMilkContractor = g_i18n:getText("animalPenExtension_callMilkContractor", customEnvironment),
        dialogHeader = g_i18n:getText("animalPenExtension_dialogHeader", customEnvironment),
        milkCapacity = g_i18n:getText("animalPenExtension_milkCapacity", customEnvironment),
        marketValue = g_i18n:getText("animalPenExtension_marketValue", customEnvironment),
        description = g_i18n:getText("animalPenExtension_description", customEnvironment),
        amount = g_i18n:getText("animalPenExtension_amount", customEnvironment),
        salesClosed = g_i18n:getText("animalPenExtension_salesClosed", customEnvironment),
        noMilkSellPointWarning = g_i18n:getText("animalPenExtension_noMilkSellPointWarning", customEnvironment),
        requiredPermissions = g_i18n:getText("animalPenExtension_requiredPermissions", customEnvironment),
        doPurchase = g_i18n:getText("animalPenExtension_doPurchase", customEnvironment),
        checkBuildState= g_i18n:getText("animalPenExtension_checkBuildState", customEnvironment),
        bought = g_i18n:getText("animalPenExtension_bought", customEnvironment),
        startedBuild = g_i18n:getText("animalPenExtension_startedBuild", customEnvironment),
        startedBuildNoPlumber = g_i18n:getText("animalPenExtension_startedBuildNoPlumber", customEnvironment),
        purchase = g_i18n:getText("animalPenExtension_purchase", customEnvironment),
        chickenWaterAddon = g_i18n:getText("animalPenExtension_chickenWaterAddon", customEnvironment)
    }

    self.randomDelayTexts = {}
    self.randomDelayTextsUsed = {}

    for i = 1, 6 do
        local text = string.format("animalPenExtension_randomDelay_%d", i)

        if g_i18n:hasText(text, customEnvironment) then
            self.randomDelayTextsUsed[text] = true
            table.insert(self.randomDelayTexts, g_i18n:getText(text, customEnvironment))
        end
    end

    self.totalDelays = 0
    self.previousDelayIds = {0, 0, 0}

    self.showFirstStart = false
    self.firstStartUpdateTime = 2000

    self.partsColours = {}

    self.debugActive = false

    return self
end

function AnimalPenExtensionManager:load()
    local success, numLoaded = self:loadBaseBuildings()

    if success then
        local filename = self.baseDirectory .. "sharedParts/universalParts.i3d"
        local i3dNode = g_i3DManager:loadSharedI3DFile(filename, "", false, false)

        if i3dNode ~= 0 then
            local refMaterials = getChild(i3dNode, "refMaterials")

            if refMaterials ~= nil then
                for i = 0, getNumOfChildren(refMaterials) - 1 do
                    local node = getChildAt(refMaterials, i)

                    if getHasClassId(node, ClassIds.SHAPE) then
                        local name = getName(node)
                        local material = getMaterial(node, 0)
                        local title = name:sub(1, 1):upper() .. name:sub(2)

                        table.insert(self.partsColours, {
                            material = material,
                            title = title,
                            index = i + 1,
                            name = name
                        })
                    end
                end
            end

            delete(i3dNode)
        else
            self:logPrint(3, "Failed to load i3d '%s'!", filename)
        end

        if self.isServer then
            if g_currentMission ~= nil then
                if g_currentMission.environment ~= nil then
                    g_currentMission.environment:addDayChangeListener(self)
                end

                local firstTimeRun = true

                if g_currentMission.missionInfo ~= nil then
                    if g_currentMission.missionInfo.savegameDirectory ~= nil then
                        filename = g_currentMission.missionInfo.savegameDirectory .. "/animalPenExtension.xml"

                        if fileExists(filename) then
                            local xmlFile = loadXMLFile("AnimalPenExtensionXML", filename)

                            if xmlFile ~= nil then
                                self:loadFromXML(xmlFile)

                                delete(xmlFile)
                                xmlFile = nil

                                firstTimeRun = false
                            end
                        end
                    end
                end

                if firstTimeRun then
                    self:updateSettings({}, false)
                    self.showFirstStart = not g_currentMission.missionDynamicInfo.isMultiplayer

                    if self.showFirstStart and g_dedicatedServerInfo == nil then
                        g_currentMission:addUpdateable(self)
                    end
                end
            end
        end

        if self.chickenAddonActive then
            if self:initChickenWaterSupport() then
                if InGameMenuAnimalsFrame.getFoodDescription ~= nil then
                    InGameMenuAnimalsFrame.getFoodDescription = Utils.overwrittenFunction(InGameMenuAnimalsFrame.getFoodDescription, function (self, superFunc, animalType)
                        if animalType == "CHICKEN" then
                            if self.selectedHusbandry ~= nil then
                                local waterModule = self.selectedHusbandry:getModuleByName("water")

                                if waterModule ~= nil then
                                    if g_animalPenExtensionManager ~= nil then
                                        local extraText = g_animalPenExtensionManager.texts.chickenWaterAddon
                                        local foodDescription = superFunc(self, animalType)

                                        return string.format("%s\n\n%s", foodDescription, extraText)
                                    end
                                end
                            end
                        end

                        return superFunc(self, animalType)
                    end)
                end
            else
                self.chickenAddonActive = false
                self:logPrint(1, "Chicken Addon failed to initiate! This mode is now unavailable.")
            end
        end

        if self.consoleCommands ~= nil then
            self.consoleCommands:load()
        end

        self:logPrint(1, "Version: %s loaded into %s Default Animal Husbandry building type(s).", self.versionString, numLoaded)

        return true
    end
end

function AnimalPenExtensionManager:delete()
    g_currentMission:removeUpdateable(self)
end

function AnimalPenExtensionManager:update(dt)
    if self.isServer then
        if self.showFirstStart then
            if self.firstStartUpdateTime > 0 then
                self.firstStartUpdateTime = self.firstStartUpdateTime - dt
            else
                if not g_gui:getIsGuiVisible() then
                    self.showFirstStart = false

                    if g_currentMission.hud ~= nil and g_currentMission.missionInfo ~= nil then
                        local savegameDirectory = g_currentMission.missionInfo:getSavegameDirectory(g_currentMission.missionInfo.savegameIndex)

                        local title = "Animal Pen Extension - Version " .. tostring(self.versionString)
                        local message = string.format(g_i18n:getText("animalPenExtension_firstTimeRun", self.customEnvironment), savegameDirectory)

                        g_currentMission.hud:showInGameMessage(title, message, -1)
                    end

                    g_currentMission:removeUpdateable(self)
                end
            end
        end
    end
end

function AnimalPenExtensionManager:finalDelete()
    if self.universalPlacement ~= nil then
        self.universalPlacement:delete()
        self.universalPlacement = nil
    end

    if self.isServer then
        if g_currentMission ~= nil and g_currentMission.environment ~= nil then
            g_currentMission.environment:removeDayChangeListener(self)
        end
    end

    if self.consoleCommands ~= nil then
        self.consoleCommands:delete()
        self.consoleCommands = nil
    end

    if self.partsColours ~= nil and #self.partsColours > 0 then
        g_i3DManager:releaseSharedI3DFile("sharedParts/universalParts.i3d", self.baseDirectory, true)

        self.partsColours = {}
    end
end

function AnimalPenExtensionManager:loadFromXML(xmlFile)
    local settings = {}

    settings.chickenAddonActive = getXMLBool(xmlFile, "animalPenExtension.settings.chickenAddon#active")
    settings.milkAddonActive = getXMLBool(xmlFile, "animalPenExtension.settings.milkAddon#active")
    settings.chanceOfDelay = getXMLInt(xmlFile, "animalPenExtension.settings.milkAddon#chanceOfDelayPercent")
    settings.milkContractorPercent = getXMLInt(xmlFile, "animalPenExtension.settings.milkAddon#contractorTransportPercent")

    settings.waterAddonActive = getXMLBool(xmlFile, "animalPenExtension.settings.waterAddon#active")
    settings.purchaseOverride = getXMLBool(xmlFile, "animalPenExtension.settings.waterAddon#purchaseOverride")
    settings.waterPriceScaleOverride = getXMLFloat(xmlFile, "animalPenExtension.settings.waterAddon#waterPriceScaleOverride")
    settings.maintenancePerDayOverride = getXMLInt(xmlFile, "animalPenExtension.settings.waterAddon#maintenancePerDayOverride")

    settings.randomDelayActive = getXMLBool(xmlFile, "animalPenExtension.milkAddon.randomDelay#isActive")
    settings.delayTextId = getXMLInt(xmlFile, "animalPenExtension.milkAddon.randomDelay#textId")

    self.totalDelays = math.max(Utils.getNoNil(getXMLInt(xmlFile, "animalPenExtension.milkAddon.randomDelay#delayCounter"), 0), 0)
    local previousDelayIds = StringUtil.splitString(" ", getXMLString(xmlFile, "animalPenExtension.milkAddon.randomDelay#previousDelayIds"))

    for i = 1, 3 do
        self.previousDelayIds[i] = tonumber(Utils.getNoNil(previousDelayIds[i], "0"))
    end

    self:updateSettings(settings, true)
end

function AnimalPenExtensionManager:saveToXMLFile(xmlFilename)
    local xmlFile = io.open (xmlFilename, "w")

    if xmlFile ~= nil then
        local language = g_languageShort
        local link = "mods.php?lang=en&country=be&title=fs2019&filter=org&org_id=129652&page=0"

        if language == "de" or language == "fr" then
            link = "mods.php?lang=" .. language .. "&country=be&title=fs2019&filter=org&org_id=129652&page=0"
        end

        local settings = self:getCurrentSettings(true)

        local totalDelays = tostring(math.max(Utils.getNoNil(self.totalDelays, 0), 0))
        local previousDelayIds = ' previousDelayIds="' .. table.concat(self.previousDelayIds, " ") .. '"'

        local saveGameHelp = g_i18n:getText("animalPenExtension_saveGameHelp", self.customEnvironment)

        local xmlText = '<?xml version="1.0" encoding="utf-8" standalone="no" ?>\n\n' ..
            '<!-- [INFORMATION]\n    Support: https://forum.giants-software.com  or  https://github.com/GtX-Andy \n' .. saveGameHelp .. '\n-->\n\n' ..
            '<animalPenExtension build="' .. tostring(self.buildId) .. '" version="' .. tostring(self.versionString) .. '">\n' ..
            '    <settings>\n        <waterAddon active="' .. settings.waterAddonActive .. '" purchaseOverride="' .. settings.purchaseOverride .. '" waterPriceScaleOverride="' .. settings.waterPriceScaleOverride .. '" maintenancePerDayOverride="' .. settings.maintenancePerDayOverride .. '"/>\n' ..
            '        <milkAddon active="' .. settings.milkAddonActive .. '" chanceOfDelayPercent="' .. settings.chanceOfDelay .. '" contractorTransportPercent="' .. settings.milkContractorPercent .. '"/>\n' ..
            '        <chickenAddon active="' .. settings.chickenAddonActive .. '"/>\n    </settings>\n\n' ..
            '    <!-- No changes required past this point! This is save game data only. -->\n' ..
            '    <milkAddon>\n        <randomDelay isActive="' .. settings.randomDelayActive .. '" textId="' .. settings.delayTextId .. '" delayCounter="' .. totalDelays .. '"' .. previousDelayIds .. '/>\n    </milkAddon>\n' ..
            '</animalPenExtension>\n'

        xmlFile:write(xmlText)
        xmlFile:close()
        xmlText = nil
    end
end

function AnimalPenExtensionManager:syncSettings(connection)
    connection:sendEvent(AnimalPenExtensionJoinGameEvent:new())
end

function AnimalPenExtensionManager:updateSettings(settings, updateRandomDelay)
    if settings == nil then
        settings = {}
    end

    self.purchaseOverride = Utils.getNoNil(settings.purchaseOverride, false)
    self.waterPriceScaleOverride = Utils.getNoNil(settings.waterPriceScaleOverride, -1)
    self.maintenancePerDayOverride = Utils.getNoNil(settings.maintenancePerDayOverride, -1)
    self.chanceOfDelay = MathUtil.clamp(math.floor(Utils.getNoNil(settings.chanceOfDelay, self:getDefaultDelayChance())), 0, 100)
    self.waterAddonActive = Utils.getNoNil(settings.waterAddonActive, true)
    self.milkAddonActive = Utils.getNoNil(settings.milkAddonActive, true)
    self.chickenAddonActive = Utils.getNoNil(settings.chickenAddonActive, true)
    self.randomDelayActive = Utils.getNoNil(settings.randomDelayActive, false)
    self.delayTextId = MathUtil.clamp(Utils.getNoNil(settings.delayTextId, 1), 1, #self.randomDelayTexts)
    self.milkContractorPercent = MathUtil.clamp(math.floor(Utils.getNoNil(settings.milkContractorPercent, 5)), 0, 100)

    if updateRandomDelay then
        self:setDelayData(self.randomDelayActive, self.delayTextId, false, true)
    end
end

function AnimalPenExtensionManager:getCurrentSettings(valueToString)
    local settings = {}

    settings.purchaseOverride = Utils.getNoNil(self.purchaseOverride, false)
    settings.waterPriceScaleOverride = Utils.getNoNil(self.waterPriceScaleOverride, -1)
    settings.maintenancePerDayOverride = Utils.getNoNil(self.maintenancePerDayOverride, -1)
    settings.chanceOfDelay = MathUtil.clamp(Utils.getNoNil(math.floor(self.chanceOfDelay, self:getDefaultDelayChance())), 0, 100)
    settings.waterAddonActive = Utils.getNoNil(self.waterAddonActive, true)
    settings.milkAddonActive = Utils.getNoNil(self.milkAddonActive, true)
    settings.chickenAddonActive = Utils.getNoNil(self.chickenAddonActive, true)
    settings.randomDelayActive = Utils.getNoNil(self.randomDelayActive, false)
    settings.delayTextId = MathUtil.clamp(Utils.getNoNil(self.delayTextId, 1), 1, #self.randomDelayTexts)
    settings.milkContractorPercent = MathUtil.clamp(math.floor(Utils.getNoNil(self.milkContractorPercent, 5)), 0, 100)

    if valueToString then
        for i, settingValue in pairs (settings) do
            settings[i] = tostring(settingValue)
        end
    end

    return settings
end

function AnimalPenExtensionManager:loadBaseBuildings()
    if self.baseBuildingsLoaded > 0 then
        self:logPrint(0, "Base game buildings are already loaded!")
        return false, self.baseBuildingsLoaded
    end

    local xmlFile = loadXMLFile("BaseBuildingsXML", self.baseBuildingsXMLFilename)
    if xmlFile == 0 then
        self:logPrint(0, "Failed to load BaseBuildings XML!")
        return false, 0
    end

    if hasXMLProperty(xmlFile, "animalPenExtension") then
        local i = 0
        while true do
            local key = string.format("animalPenExtension.baseBuilding(%d)", i)
            if not hasXMLProperty(xmlFile, key) then
                break
            end

            local i3dFilename = getXMLString(xmlFile, key .. "#filename")

            if i3dFilename ~= nil then
                if string.sub(i3dFilename, 1, 4) == "data" then
                    local i3dFilenameLower = i3dFilename:lower()
                    if self.baseBuildings[i3dFilenameLower] == nil then
                        self.baseBuildings[i3dFilenameLower] = key
                        self.baseBuildingsLoaded = self.baseBuildingsLoaded + 1
                    else
                        self:logPrint(0, "Trying to load duplicate 'baseBuilding' at ", key)
                    end
                else
                    self:logPrint(3, "Trying to load building that is not part of Giants Base Game! ( %s )", key)
                end
            else
                self:logPrint(0, "Filename is missing at %s", key)
            end

            i = i + 1
        end

        delete(xmlFile)

        if self.baseBuildingsLoaded > 0 then
            return true, self.baseBuildingsLoaded
        else
            self:logPrint(0, "Failed to load any base buildings!")
            return false, 0
        end
    else
        self:logPrint(0, "XML key 'animalPenExtension' was not found in %s", self.baseBuildingsXMLFilename)

        delete(xmlFile)
        return false, 0
    end
end

function AnimalPenExtensionManager:initChickenWaterSupport(waterPerDay)
    local subTypeAdded = 0

    -- Chickens eat 5 litres a day so 7 litres of water is a good number.
    -- Animals drink more than they eat! Also accounts for evaporation and cleaning ;-)
    -- Only adjust if no other mod has done this already and water usage is 0.

    if g_animalManager ~= nil and g_animalManager.typeToAnimal.CHICKEN ~= nil then
        if g_animalManager.typeToAnimal.CHICKEN.subTypes ~= nil then
            for _, subType in pairs (g_animalManager.typeToAnimal.CHICKEN.subTypes) do
                if subType.input ~= nil and subType.input.waterPerDay <= 0 then
                    subType.input.waterPerDay = 7
                    subTypeAdded = subTypeAdded + 1
                end
            end
        end
    end

    return subTypeAdded >= 4
end

function AnimalPenExtensionManager:setPartsMaterial(parts, index)
    if self:getNumPartsColours() > 0 and parts ~= nil and index ~= nil and index > 0 then
        if index > #self.partsColours then
            index = 1
        end

        local partsColour = self.partsColours[index]

        for _, part in pairs (parts) do
            if part.node ~= nil and part.allowMaterialChange and index ~= part.lastColourIndex then
                part.lastColourIndex = index
                setMaterial(part.node, partsColour.material, 0)
            end
        end

        return index, partsColour.title
    end

    return 0, "Blue"
end

function AnimalPenExtensionManager:getNumPartsColours()
    if self.partsColours ~= nil then
        return #self.partsColours
    end

    return 0
end

function AnimalPenExtensionManager:getPartColourFromIndex(index)
    if index ~= nil and index > 0 then
        if index <= self:getNumPartsColours() then
            return self.partsColours[index].title
        end
    end

    return "Blue"
end

function AnimalPenExtensionManager:getRandomTextId()
    local delyIndex = 1 + (self.totalDelays % 3)
    local availableDelayTexts = {}

    for i = 1, #self.randomDelayTexts do
        if self.previousDelayIds[1] ~= i and
            self.previousDelayIds[2] ~= i and
            self.previousDelayIds[3] ~= i then

            table.insert(availableDelayTexts, i)
        end
    end

    local delayTextId = math.random(1, #availableDelayTexts)
    self.previousDelayIds[delyIndex] = availableDelayTexts[delayTextId]

    return self.previousDelayIds[delyIndex] or 1
end

function AnimalPenExtensionManager:getNextRandomText()
    local delayTextId = 1

    if self.delayTextId ~= nil then
        delayTextId = math.max(math.min(self.delayTextId, #self.randomDelayTexts), 1)
    end

    return self.randomDelayTexts[delayTextId]
end

function AnimalPenExtensionManager:dayChanged()
    if self.isServer then
        local randomDelayActive = false
        local delayTextId = 1

        if not self.randomDelayActive and self.chanceOfDelay > 0 then
            if math.random(1, 100) <= self.chanceOfDelay then
                randomDelayActive = true
                delayTextId = self:getRandomTextId()
                self.totalDelays = self.totalDelays + 1
            end
        end

        if randomDelayActive ~= self.randomDelayActive or (delayTextId ~= self.delayTextId) then
            self:setDelayData(randomDelayActive, delayTextId, false)
        end
    end
end

function AnimalPenExtensionManager:setDelayData(randomDelayActive, delayTextId, forceUpdate, noEventSend)
    AnimalPenExtensionDelayEvent.sendEvent(randomDelayActive, delayTextId, forceUpdate, noEventSend)

    self.randomDelayActive = randomDelayActive
    self.delayTextId = delayTextId

    if forceUpdate then
        for _, husbandry in pairs (g_currentMission.husbandries) do
            if husbandry.animalPenExtension ~= nil and husbandry.animalPenExtension.milkAddon ~= nil then
                local state = not husbandry.animalPenExtension:getRandomDelayActive() and husbandry.animalPenExtension:getIsSalesOpen()

                if state ~= husbandry.animalPenExtension.milkAddon.salesSignState then
                    husbandry.animalPenExtension:setMilkSaleSigns(state)
                end
            end
        end
    end
end

function AnimalPenExtensionManager:getDefaultDelayChance()
    if g_currentMission ~= nil and g_currentMission.missionInfo ~= nil then
        return 10 + (10 * g_currentMission.missionInfo.difficulty)
    end

    return 20
end

function AnimalPenExtensionManager:logPrint(printLevel, text, ...)
    if printLevel > 0 then
        if printLevel == 1 then
            g_logManager:info(" [FS19_AnimalPenExtension]  " .. text, ...)
        elseif printLevel == 2 then
            g_logManager:warning(" [FS19_AnimalPenExtension]  " .. text, ...)
        elseif printLevel == 3 then
            g_logManager:error(" [FS19_AnimalPenExtension]  " .. text, ...)
        end
    else
        if g_animalPenExtensionManager.debugActive then
            print("  DEBUG: [FS19_AnimalPenExtension]  " .. string.format(text, ...))
        end
    end
end
