--
-- YieldMap
--
-- @author Stefan Maurus
-- @date 07/08/2020
--
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.


YieldMap = {}

YieldMap.MOD_NAME = g_currentModName

local YieldMap_mt = Class(YieldMap, ValueMap)

function YieldMap.new(pfModule, customMt)
    local self = ValueMap.new(pfModule, customMt or YieldMap_mt)

    self.filename = "precisionFarming_yieldMap.grle"
    self.name = "yieldMap"
    self.id = "YIELD_MAP"
    self.label = "ui_mapOverviewYieldPA"

    self.densityMapModifiersYield = {}
    self.densityMapModifiersReset = {}

    self.minimapGradientUVs = {getNormalizedUVs({194, 27, 252, 12})}
    self.minimapGradientColorBlindUVs = {getNormalizedUVs({194, 43, 252, 12})}
    self.minimapLabelName = g_i18n:getText("ui_mapOverviewYieldPA", YieldMap.MOD_NAME)

    self.yieldMapSelected = false
    self.selectedFarmland = nil
    self.selectedField = nil
    self.selectedFieldArea = nil

    return self
end

function YieldMap:initialize()
    YieldMap:superClass().initialize(self)

    self.densityMapModifiersYield = {}
    self.densityMapModifiersReset = {}

    self.yieldMapSelected = false
    self.selectedFarmland = nil
    self.selectedField = nil
    self.selectedFieldArea = nil
end

function YieldMap:delete()
    g_farmlandManager:removeStateChangeListener(self)
end

function YieldMap:getGlobalI18N(list)
    table.insert(list, "ui_precisionFarming_resetYield")
    table.insert(list, "ui_precisionFarming_resetYieldAdditionalField")
end

function YieldMap:loadFromXML(xmlFile, key, baseDirectory, configFileName, mapFilename)
    key = key .. ".yieldMap"

    self.numChannels = getXMLInt(xmlFile, key .. ".bitVectorMap#numChannels") or 4
    self.maxValue = 2 ^ self.numChannels - 1
    self.sizeX = getXMLInt(xmlFile, key .. ".bitVectorMap#sizeX") or 1024
    self.sizeY = getXMLInt(xmlFile, key .. ".bitVectorMap#sizeY") or 1024

    self.lockChannel = getXMLInt(xmlFile, key .. ".soilStateMap#lockChannel") or 4

    self.bitVectorMap = self:loadSavedBitVectorMap("YieldMap", self.filename, self.numChannels, self.sizeX)
    self:addBitVectorMapToSync(self.bitVectorMap)
    self:addBitVectorMapToSave(self.bitVectorMap, self.filename)

    self.yieldValues = {}
    local i = 0
    while true do
        local baseKey = string.format("%s.yieldValues.yieldValue(%d)", key, i)
        if not hasXMLProperty(xmlFile, baseKey) then
            break
        end

        local yieldValue = {}
        yieldValue.value = getXMLInt(xmlFile, baseKey .. "#value") or 0
        yieldValue.displayValue = getXMLInt(xmlFile, baseKey .. "#displayValue") or 100

        yieldValue.color = StringUtil.getVectorNFromString(getXMLString(xmlFile, baseKey.."#color"), 3) or {0, 0, 0}
        yieldValue.colorBlind = StringUtil.getVectorNFromString(getXMLString(xmlFile, baseKey.."#colorBlind"), 3)

        table.insert(self.yieldValues, yieldValue)

        i = i + 1
    end

    self.minimapGradientLabelName = string.format("%d%% - %d%%", self:getMinMaxValue())

    self.sharedSoilStateMap = self.pfModule.sharedSoilStateMap

    g_farmlandManager:addStateChangeListener(self)

    return true
end

function YieldMap:update(dt)
    if self:getIsResetButtonActive() then
        local inputHelpMode = g_inputBinding:getInputHelpMode()
        if inputHelpMode ~= self.lastInputHelpMode then
            self.lastInputHelpMode = inputHelpMode
            self:updateResetButton()
        end
    end
end

local function worldCoordsToLocalCoords(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, size, terrainSize)
    return math.floor(size * (startWorldX+terrainSize*0.5) / terrainSize),
           math.floor(size * (startWorldZ+terrainSize*0.5) / terrainSize),
           math.floor(size * (widthWorldX+terrainSize*0.5) / terrainSize),
           math.floor(size * (widthWorldZ+terrainSize*0.5) / terrainSize),
           math.floor(size * (heightWorldX+terrainSize*0.5) / terrainSize),
           math.floor(size * (heightWorldZ+terrainSize*0.5) / terrainSize)
end

function YieldMap:setAreaYield(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, yieldPercentage)
    local modifier = self.densityMapModifiersYield.modifier
    local maskFilter = self.densityMapModifiersYield.maskFilter
    if modifier == nil or maskFilter == nil then
        self.densityMapModifiersYield.modifier = DensityMapModifier:new(self.bitVectorMap, 0, self.numChannels)
        modifier = self.densityMapModifiersYield.modifier

        self.densityMapModifiersYield.maskFilter = DensityMapFilter:new(g_currentMission.terrainDetailId, g_currentMission.terrainDetailTypeFirstChannel, g_currentMission.terrainDetailTypeNumChannels)
        maskFilter = self.densityMapModifiersYield.maskFilter
        maskFilter:setValueCompareParams("greater", 0)
    end

    startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ = worldCoordsToLocalCoords(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, self.sizeX, g_currentMission.terrainSize)
    modifier:setParallelogramDensityMapCoords(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, "ppp")

    local internalYieldValue = self:getNearestInternalYieldValueFromValue(yieldPercentage / 2 * 100)
    modifier:executeSet(internalYieldValue, maskFilter)

    self:setMinimapRequiresUpdate(true)
end

function YieldMap:getNearestInternalYieldValueFromValue(value)
    local minDifference = 10000
    local minValue = 0
    if value > 0 then
        for i=1, #self.yieldValues do
            local yieldValue = self.yieldValues[i].displayValue
            local difference = math.abs(value - yieldValue)
            if difference < minDifference then
                minDifference = difference
                minValue = self.yieldValues[i].value
            end
        end
    end

    return minValue
end

function YieldMap:resetFarmlandYieldArea(farmlandId)
    local modifier = self.densityMapModifiersReset.modifier
    local modifierLock = self.densityMapModifiersReset.modifierLock
    local farmlandMask = self.densityMapModifiersReset.farmlandMask
    local farmlandManager = g_farmlandManager
    if modifier == nil or modifierLock == nil or farmlandMask == nil then
        self.densityMapModifiersReset.modifier = DensityMapModifier:new(self.bitVectorMap, 0, self.numChannels)
        modifier = self.densityMapModifiersReset.modifier

        self.densityMapModifiersReset.modifierLock = DensityMapModifier:new(self.sharedSoilStateMap.bitVectorMap, self.lockChannel, 1)
        modifierLock = self.densityMapModifiersReset.modifierLock

        self.densityMapModifiersReset.farmlandMask = DensityMapFilter:new(farmlandManager.localMap, 0, farmlandManager.numberOfBits)
        farmlandMask = self.densityMapModifiersReset.farmlandMask
    end

    farmlandMask:setValueCompareParams("equals", farmlandId)
    modifier:executeSet(0, farmlandMask)
    modifierLock:executeSet(0, farmlandMask)

    self:setMinimapRequiresUpdate(true)
end

function YieldMap:buildOverlay(overlay, yieldFilter, isColorBlindMode)
    resetDensityMapVisualizationOverlay(overlay)
    setOverlayColor(overlay, 1, 1, 1, 1)

    local yieldMapId = self.bitVectorMap

    for i=1, #self.yieldValues do
        if yieldFilter[i] then
            local yieldValue = self.yieldValues[i]
            local pct = (i-1) / (#self.yieldValues - 1)

            local r, g, b
            if isColorBlindMode then
                if yieldValue.colorBlind ~= nil then
                    r, g, b = yieldValue.colorBlind[1], yieldValue.colorBlind[2], yieldValue.colorBlind[3]
                else
                    r, g, b = pct * 0.9 + 0.1, pct * 0.9 + 0.1, 0.1
                end
            else
                r, g, b = yieldValue.color[1], yieldValue.color[2], yieldValue.color[3]
            end

            setDensityMapVisualizationOverlayStateColor(overlay, yieldMapId, 0, 0, self.numChannels, yieldValue.value, r, g, b)
        end
    end
end

function YieldMap:getMinimapZoomFactor()
    return 0.1
end

function YieldMap:getMinMaxValue()
    if #self.yieldValues > 0 then
        return self.yieldValues[1].displayValue, self.yieldValues[#self.yieldValues].displayValue, #self.yieldValues
    end

    return 0, 1, 0
end

function YieldMap:getDisplayValues()
    if self.valuesToDisplay == nil then
        self.valuesToDisplay = {}

        for i=1, #self.yieldValues do
            local pct = (i-1) / (#self.yieldValues - 1)
            local yieldValue = self.yieldValues[i]

            local yieldValueToDisplay = {}
            yieldValueToDisplay.colors = {}
            yieldValueToDisplay.colors[true] = {yieldValue.colorBlind or {pct * 0.9 + 0.1, pct * 0.9 + 0.1, 0.1}}
            yieldValueToDisplay.colors[false] = {yieldValue.color}
            yieldValueToDisplay.description = string.format("%d%%", yieldValue.displayValue)

            table.insert(self.valuesToDisplay, yieldValueToDisplay)
        end
    end

    return self.valuesToDisplay
end

function YieldMap:getValueFilter()
    if self.valueFilter == nil then
        self.valueFilter = {}

        for i=1, #self.yieldValues do
            table.insert(self.valueFilter, true)
        end
    end

    return self.valueFilter
end

function YieldMap:onValueMapSelectionChanged(valueMap)
    self.yieldMapSelected = valueMap == self

    self:updateResetButton()
end

function YieldMap:onFarmlandSelectionChanged(farmlandId, fieldNumber, fieldArea)
    self.selectedFarmland = farmlandId
    self.selectedField = fieldNumber
    self.selectedFieldArea = fieldArea

    self:updateResetButton()
end

function YieldMap:onFarmlandStateChanged(farmlandId, farmId)
    if farmId == FarmlandManager.NO_OWNER_FARM_ID then
        self:resetFarmlandYieldArea(farmlandId)
    end
end

---
-- @includeCode
function YieldMap:setMapFrame(mapFrame)
    self.mapFrame = mapFrame

    self:updateResetButton()
end

---
-- @includeCode
function YieldMap:getIsResetButtonActive()
    return (self.selectedFarmland ~= nil and self.selectedFieldArea ~= nil and self.selectedFieldArea > 0) and self.yieldMapSelected
end

---
-- @includeCode
function YieldMap:updateResetButton()
    local mapFrame = self.mapFrame
    local isActive = self:getIsResetButtonActive()
    mapFrame.resetYieldButtonBackground:setVisible(isActive)

    if isActive then
        local text
        if self.selectedField ~= nil and self.selectedField ~= 0 then
            text = string.format(g_i18n:getText("ui_precisionFarming_resetYield"), self.selectedField)
        else
            text = g_i18n:getText("ui_precisionFarming_resetYieldAdditionalField")
        end

        IngameMapExtension.updateButtonOnInputHelpChange(mapFrame.resetYieldButton, "ingameMenuPrecisionFarmingResetYieldButtonConsole", "ingameMenuPrecisionFarmingResetYieldButton")
        mapFrame.resetYieldButton:setText(text)

        self.lastInputHelpMode = g_inputBinding:getInputHelpMode()
    end
end

function YieldMap:onClickButtonResetYield()
    if self:getIsResetButtonActive() then
        local farmlandStatistics = g_precisionFarming.farmlandStatistics
        if farmlandStatistics ~= nil then
            if farmlandStatistics.selectedFarmlandId ~= nil then
                self:resetFarmlandYieldArea(farmlandStatistics.selectedFarmlandId)
                g_precisionFarming:updatePrecisionFarmingOverlays()
                return true
            end
        end
    end

    return false
end

function YieldMap:getHelpLinePage()
    return 5
end
