HDProductionMenu = {}
HDProductionMenu.requestedLevels = setmetatable({}, {__mode = "k"})
HDProductionMenu.requestedProductivities = setmetatable({}, {__mode = "k"})

function HDProductionMenu.requestLevelWhenMenuIsOpen(placeable)
    if placeable == nil
        or g_gui == nil
        or g_gui.currentGuiName ~= "InGameMenu"
        or HDProductionMenu.requestedLevels[placeable] then
        return
    end
    HDProductionMenu.requestedLevels[placeable] = true
    HDLevelSyncEvent.sendRequest(placeable, true)
end

function HDProductionMenu.getProductionName(productionPoint, superFunc)
    local name = superFunc(productionPoint)
    local placeable = productionPoint.owningPlaceable
    if placeable == nil then
        return name
    end

    if HDPlaceableUpgrade.isRelayProductionPoint(placeable, productionPoint) then
        return name
    end

    local spec = HDPlaceableUpgrade.ensureRuntimeSpec(placeable, productionPoint)
    if spec == nil then
        return name
    end
    HDProductionMenu.requestLevelWhenMenuIsOpen(placeable)

    if spec.level >= HDPlaceableUpgrade.getMaxLevel(placeable) then
        return string.format(g_i18n:getText("hd_upgrade_name_max"), name)
    end
    return string.format(g_i18n:getText("hd_upgrade_name_level"), name, spec.level)
end

ProductionPoint.getName = Utils.overwrittenFunction(
    ProductionPoint.getName,
    HDProductionMenu.getProductionName
)

if Gui ~= nil and Gui.NAV_ACTIONS ~= nil then
    table.insert(Gui.NAV_ACTIONS, InputAction.HD_UPGRADE_BUILDING)
end

function HDProductionMenu.getSelectedPlaceable(frame)
    if frame == nil or frame.getSelectedProduction == nil then
        return nil
    end

    local production, productionPoint = frame:getSelectedProduction()
    if productionPoint == nil then
        return nil
    end

    return productionPoint.owningPlaceable, productionPoint, production
end

function HDProductionMenu.requestProductivity(placeable, production)
    if placeable == nil or production == nil or production.id == nil or g_server ~= nil then
        return
    end
    local requested = HDProductionMenu.requestedProductivities[placeable]
    if requested == nil then
        requested = {}
        HDProductionMenu.requestedProductivities[placeable] = requested
    end
    if not requested[production.id] then
        requested[production.id] = true
        HDProductivityEvent.sendSyncRequest(placeable, production.id)
    end
end

function HDProductionMenu.showProductivityDialog(frame, placeable, production)
    local options = {}
    local selectedOption = 1
    local current = HDPlaceableUpgrade.getProductivity(placeable, production.id)
    for index, productivity in ipairs(HDUpgrade.PRODUCTIVITY_OPTIONS) do
        options[index] = string.format("%d%%", productivity)
        if productivity == current then
            selectedOption = index
        end
    end

    local callback = function(target, option, args)
        if type(option) ~= "number" or option < 1 then
            return
        end
        local productivity = HDUpgrade.PRODUCTIVITY_OPTIONS[option]
        if productivity ~= nil then
            HDProductivityEvent.sendRequest(args.placeable, args.production.id, productivity)
            target:updateMenuButtons()
        end
    end

    OptionDialog.createFromExistingGui({
        optionTitle = production.name,
        optionText = g_i18n:getText("hd_productivity_warning"),
        options = options,
        callbackFunc = callback
    }, HDUpgrade.MOD_NAME .. "ProductivityDialog")

    local dialog = OptionDialog.INSTANCE
    dialog.optionElement:setState(selectedOption)
    dialog:setCallback(callback, frame, {
        placeable = placeable,
        production = production
    })
end

function HDProductionMenu.openSelectedProductivity(frame)
    local placeable, productionPoint, production = HDProductionMenu.getSelectedPlaceable(frame)
    if placeable == nil
        or production == nil
        or production.id == nil
        or HDPlaceableUpgrade.isRelayProductionPoint(placeable, productionPoint)
        or placeable:getOwnerFarmId() ~= g_currentMission:getFarmId() then
        return
    end
    HDProductionMenu.showProductivityDialog(frame, placeable, production)
end

function HDProductionMenu.getVisitButtonInfo(frame)
    local visitTexts = {
        g_i18n:getText("button_visit"),
        g_i18n:getText("ui_visit"),
        "Besuchen",
        "Visit"
    }
    for index = #frame.menuButtonInfo, 1, -1 do
        local buttonInfo = frame.menuButtonInfo[index]
        for _, visitText in ipairs(visitTexts) do
            if buttonInfo.text == visitText then
                table.remove(frame.menuButtonInfo, index)
                return buttonInfo
            end
        end
    end
    return nil
end

function HDProductionMenu.showProductionManagementDialog(frame, placeable, production, farmId, visitButtonInfo)
    HDManagementDialog.show(frame, placeable, production, farmId, visitButtonInfo)
end

function HDProductionMenu.updateMenuButtons(frame)
    if frame.pointsSelector == nil
        or frame.pointsSelector:getState() ~= InGameMenuProductionFrame.POINTS_OWNED
        or not g_currentMission:getHasPlayerPermission("manageProductions") then
        return
    end

    local placeable, productionPoint, production = HDProductionMenu.getSelectedPlaceable(frame)
    local farmId = g_currentMission:getFarmId()
    if placeable ~= nil then
        if HDPlaceableUpgrade.isRelayProductionPoint(placeable, productionPoint) then
            return
        end
        HDPlaceableUpgrade.ensureRuntimeSpec(placeable, productionPoint)
        HDUpgradeSavegame.applyPendingLevel(placeable)
        HDProductionMenu.requestProductivity(placeable, production)
    end
    if placeable == nil then
        return
    end

    if production ~= nil and production.id ~= nil and placeable:getOwnerFarmId() == farmId then
        local visitButtonInfo = HDProductionMenu.getVisitButtonInfo(frame)
        table.insert(frame.menuButtonInfo, {
            profile = "buttonOk",
            inputAction = InputAction.HD_UPGRADE_BUILDING,
            text = g_i18n:getText("hd_management_button"),
            callback = function()
                HDProductionMenu.showProductionManagementDialog(
                    frame,
                    placeable,
                    production,
                    farmId,
                    visitButtonInfo
                )
            end
        })
    end

end

InGameMenuProductionFrame.updateMenuButtons = Utils.appendedFunction(
    InGameMenuProductionFrame.updateMenuButtons,
    HDProductionMenu.updateMenuButtons
)
