Icons = {}
Icons[PlayerStates.Poison] = {
    tooltip = tr('You are poisoned'),
    path = '/images/game/states/poisoned',
    id = 'condition_poisoned'
}
Icons[PlayerStates.Burn] = {
    tooltip = tr('You are burning'),
    path = '/images/game/states/burning',
    id = 'condition_burning'
}
Icons[PlayerStates.Energy] = {
    tooltip = tr('You are electrified'),
    path = '/images/game/states/electrified',
    id = 'condition_electrified'
}
Icons[PlayerStates.Drunk] = {
    tooltip = tr('You are drunk'),
    path = '/images/game/states/drunk',
    id = 'condition_drunk'
}
Icons[PlayerStates.ManaShield] = {
    tooltip = tr('You are protected by a magic shield'),
    path = '/images/game/states/magic_shield',
    id = 'condition_magic_shield'
}
Icons[PlayerStates.Paralyze] = {
    tooltip = tr('You are paralysed'),
    path = '/images/game/states/slowed',
    id = 'condition_slowed'
}
Icons[PlayerStates.Haste] = {
    tooltip = tr('You are hasted'),
    path = '/images/game/states/haste',
    id = 'condition_haste'
}
Icons[PlayerStates.Swords] = {
    tooltip = tr('You may not logout during a fight'),
    path = '/images/game/states/logout_block',
    id = 'condition_logout_block'
}
Icons[PlayerStates.Drowning] = {
    tooltip = tr('You are drowning'),
    path = '/images/game/states/drowning',
    id = 'condition_drowning'
}
Icons[PlayerStates.Freezing] = {
    tooltip = tr('You are freezing'),
    path = '/images/game/states/freezing',
    id = 'condition_freezing'
}
Icons[PlayerStates.Dazzled] = {
    tooltip = tr('You are dazzled'),
    path = '/images/game/states/dazzled',
    id = 'condition_dazzled'
}
Icons[PlayerStates.Cursed] = {
    tooltip = tr('You are cursed'),
    path = '/images/game/states/cursed',
    id = 'condition_cursed'
}
Icons[PlayerStates.PartyBuff] = {
    tooltip = tr('You are strengthened'),
    path = '/images/game/states/strengthened',
    id = 'condition_strengthened'
}
Icons[PlayerStates.PzBlock] = {
    tooltip = tr('You may not logout or enter a protection zone'),
    path = '/images/game/states/protection_zone_block',
    id = 'condition_protection_zone_block'
}
Icons[PlayerStates.Pz] = {
    tooltip = tr('You are within a protection zone'),
    path = '/images/game/states/protection_zone',
    id = 'condition_protection_zone'
}
Icons[PlayerStates.Bleeding] = {
    tooltip = tr('You are bleeding'),
    path = '/images/game/states/bleeding',
    id = 'condition_bleeding'
}
Icons[PlayerStates.Hungry] = {
    tooltip = tr('You are hungry'),
    path = '/images/game/states/hungry',
    id = 'condition_hungry'
}

local iconsTable = {
    ["Experience"] = 8,
    ["Magic"] = 0,
    ["Axe"] = 2,
    ["Club"] = 1,
    ["Distance"] = 3,
    ["Fist"] = 4,
    ["Shielding"] = 5,
    ["Sword"] = 6,
    ["Fishing"] = 7
}

local healthBar = nil
local manaBar = nil
local topBar = nil
local states = nil
local experienceTooltip = 'You have %d%% to advance to level %d.'
local settings = {}

function init()
    
    connect(LocalPlayer, {
        onHealthChange = onHealthChange,
        onManaChange = onManaChange,
        onLevelChange = onLevelChange,
        onStatesChange = onStatesChange,
        onMagicLevelChange = onMagicLevelChange,
        onBaseMagicLevelChange = onBaseMagicLevelChange,
        onSkillChange = onSkillChange,
        onBaseSkillChange = onBaseSkillChange
    })
    connect(g_game, {onGameStart = refresh, onGameEnd = offline})

    -- load condition icons
    for k, v in pairs(Icons) do g_textures.preload(v.path) end

    if g_game.isOnline() then refresh() end
end

function terminate()

    disconnect(LocalPlayer, {
        onHealthChange = onHealthChange,
        onManaChange = onManaChange,
        onLevelChange = onLevelChange,
        onStatesChange = onStatesChange,
        onMagicLevelChange = onMagicLevelChange,
        onBaseMagicLevelChange = onBaseMagicLevelChange,
        onSkillChange = onSkillChange,
        onBaseSkillChange = onBaseSkillChange
    })
    disconnect(g_game, {onGameStart = refresh, onGameEnd = offline})
end

function setupTopBar()
    local topPanel = modules.game_interface.getTopBar()
    topBar = topBar or g_ui.loadUI('TopBar', topPanel)
    topBar = topBar or g_ui.createWidget('TopBar', topPanel)

    manaBar = topBar.stats.mana
    healthBar = topBar.stats.health
    states = topBar.stats.states.box

    topBar.onMouseRelease = function(widget, mousePos, mouseButton)
        menu(mouseButton)
    end
end

function refresh(profileChange)
    local player = g_game.getLocalPlayer()
    if not player then return end

    setupTopBar()
    load()
    setupSkills()
    show()
    refreshVisibleBars()

    onLevelChange(player, player:getLevel(), player:getLevelPercent())
    onHealthChange(player, player:getHealth(), player:getMaxHealth())
    onManaChange(player, player:getMana(), player:getMaxMana())
    onMagicLevelChange(player, player:getMagicLevel(), player:getMagicLevelPercent())
    if not profileChange then
        onStatesChange(player, player:getStates(), 0)
    end
    onHealthChange(player, player:getHealth(), player:getMaxHealth())
    onManaChange(player, player:getMana(), player:getMaxMana())
    onLevelChange(player, player:getLevel(), player:getLevelPercent())

    for i = Skill.Fist, Skill.ManaLeechAmount do
        onSkillChange(player, i, player:getSkillLevel(i), player:getSkillLevelPercent(i))
        onBaseSkillChange(player, i, player:getSkillBaseLevel(i))
    end

    topBar.skills.onGeometryChange = setSkillsLayout
end

function refreshVisibleBars()
    local ids = {"Experience", "Magic", "Axe", "Club", "Distance", "Fist", "Shielding",
    "Sword", "Fishing"}

    for i, id in ipairs(ids) do
        local panel = topBar[id] or topBar.skills[id]

        if panel then
            -- experience is exeption
            if id == "Experience" then
                if not settings[id] then
                    panel:setVisible(true)
                end
            else
                panel:setVisible(settings[id] or false)
            end
        end
    end
end

function setSkillsLayout()
    local visible = 0
    local skills = topBar.skills
    local width = skills:getWidth()

    for i, child in ipairs(skills:getChildren()) do
        visible = child:isVisible() and visible + 1 or visible
    end

    local many = visible > 1
    width = many and (width / 2) or width

    skills:getLayout():setCellSize({width = width, height = 19})
end

function offline()
    local player = g_game.getLocalPlayer()

    if player then onStatesChange(player, 0, player:getStates()) end
    save()
end

function toggleIcon(bitChanged)
    local content = states
    if not content then return end

    local icon = content:getChildById(Icons[bitChanged].id)
    if icon then
        icon:destroy()
    else
        icon = loadIcon(bitChanged)
        icon:setParent(content)
    end
end

function loadIcon(bitChanged)
    local icon = g_ui.createWidget('ConditionWidget', content)
    icon:setId(Icons[bitChanged].id)
    icon:setImageSource(Icons[bitChanged].path)
    icon:setTooltip(Icons[bitChanged].tooltip)
    return icon
end

function onHealthChange(localPlayer, health, maxHealth)
    if not healthBar then return end
    if health > maxHealth then maxHealth = health end

    local healthPercent = (health / maxHealth) * 100
    healthBar:setText(comma_value(health) .. ' / ' .. comma_value(maxHealth))
    healthBar:setValue(health, 0, maxHealth)
    healthBar:setPercent(healthPercent)

    if healthPercent > 92 then
        healthBar:setBackgroundColor("#00BC00FF")
    elseif healthPercent > 60 then
        healthBar:setBackgroundColor("#50A150FF")
    elseif healthPercent > 30 then
        healthBar:setBackgroundColor("#A1A100FF")
    elseif healthPercent > 8 then
        healthBar:setBackgroundColor("#BF0A0AFF")
    elseif healthPercent > 3 then
        healthBar:setBackgroundColor("#910F0FFF")
    else
        healthBar:setBackgroundColor("#850C0CFF")
    end
end

function onManaChange(localPlayer, mana, maxMana)
    if not manaBar then return end
    if mana > maxMana then maxMana = mana end

    local manaPercent = (mana / maxMana) * 100
    if manaPercent < 0 then return end
    manaBar:setText(comma_value(mana) .. ' / ' .. comma_value(maxMana))
    manaBar:setValue(mana, 0, maxMana)
    manaBar:setPercent(manaPercent)
end

function onLevelChange(localPlayer, value, percent)
    if not topBar then return end
    local experienceBar = topBar.Experience.progress
    local levelLabel = topBar.Experience.level
    experienceBar:setTooltip(tr(experienceTooltip, 100-percent, value + 1))
    experienceBar:setPercent(percent)
    levelLabel:setText(value)
end

function onStatesChange(localPlayer, now, old)
    if now == old then return end

    local bitsChanged = bit32.bxor(now, old)
    for i = 1, 32 do
        local pow = math.pow(2, i - 1)
        if pow > bitsChanged then break end
        local bitChanged = bit32.band(bitsChanged, pow)
        if bitChanged ~= 0 then toggleIcon(bitChanged) end
    end
end

function show()
    if not g_game.isOnline() then return end
    topBar:setVisible(g_settings.getBoolean("topBar", false))
end

function setupSkillPanel(id, parent, experience, defaultOff)
    local widget = g_ui.createWidget('SkillPanel', parent)
    widget:setId(id)
    widget.level:setTooltip(id)
    widget.icon:setTooltip(id)
    widget.icon:setImageClip({x = iconsTable[id]*9, y = 0, width = 9,height = 9})

    if not experience then 
        widget.progress:setBackgroundColor('#00c000') 
        widget.shop:setVisible(false)
        widget.shop:disable()
        widget.shop:setWidth(0)
        widget.progress:setMarginRight(1)
    end

    settings[id] = settings[id] ~= nil and settings[id] or defaultOff
    if settings[id] == false then widget:setVisible(false) end

    -- breakers
    widget.onGeometryChange = function()
        local margin = widget.progress:getWidth() / 4
        local left = widget.left
        local right = widget.right

        left:setMarginRight(margin)
        right:setMarginRight(margin)
    end

end

function menu(mouseButton)
    if mouseButton ~= 2 then return end

    local menu = g_ui.createWidget('PopupMenu')
    menu:setId("topBarMenu")
    menu:setGameMenu(true)

    local expPanel = topBar.Experience
    local start = expPanel:isVisible() and "Hide" or "Show"
    menu:addOption(start .. " Experience Level",
                   function() toggleSkillPanel(id) end)
    for i, child in ipairs(topBar.skills:getChildren()) do
        local id = child:getId()
        if id ~= "stats" then
            local start = child:isVisible() and "Hide" or "Show"
            menu:addOption(start .. " " .. id .. " Level",
                           function() toggleSkillPanel(id) end)
        end
    end

    menu:display(mousePos)
    return true
end

function setupSkills()
    local t = {
        "Experience", "Magic", "Axe", "Club", "Distance", "Fist", "Shielding",
        "Sword", "Fishing"
    }

    for i, id in ipairs(t) do
        if not topBar[id] and not topBar.skills[id] then
            setupSkillPanel(id, i == 1 and topBar or topBar.skills, i == 1,
                            i == 1)
        end
    end

    local child = topBar.Experience
    topBar:moveChildToIndex(child, 2)
end

function toggleSkillPanel(id)
    if not topBar then return end
    local panel = topBar.skills[id]
    panel = panel or topBar.Experience
    if not panel then return end

    panel:setVisible(not panel:isVisible())
    settings[id] = panel:isVisible()
    setSkillsLayout()
end

function setSkillValue(id, value)
    if not topBar then return end
    local panel = topBar.skills[id]
    if not panel then return end

    panel.level:setText(value)
end

function setSkillPercent(id, percent, tooltip)
    if not topBar then return end
    local panel = topBar.skills[id]
    if not panel then return end

    panel.progress:setPercent(math.floor(percent))
end

function setSkillBase(id, value, baseValue)
    if not topBar then return end
    local panel = topBar.skills[id]
    if not panel then return end

    local progress = topBar.skills[id].progress
    local progressDesc = "You have " .. 100 - math.floor(progress:getPercent()) .. " percent to go"
    local level = topBar.skills[id].level

    if baseValue <= 0 or value < 0 then return end

    if value > baseValue then
        level:setColor('#008b00') -- green
        progress:setTooltip(value .. " = " .. baseValue .. ' + ' ..
                                (value - baseValue) .. "\n" .. progressDesc)
    elseif value < baseValue then
        level:setColor('#b22222') -- red
        progress:setTooltip(baseValue .. ' ' .. (value - baseValue))
    else
        level:setColor('#bbbbbb') -- default
        progress:removeTooltip()
    end

end

function onMagicLevelChange(localPlayer, magiclevel, percent)
    setSkillValue('Magic', magiclevel)
    setSkillPercent('Magic', percent)

    onBaseMagicLevelChange(localPlayer, localPlayer:getBaseMagicLevel())
end

function onBaseMagicLevelChange(localPlayer, baseMagicLevel)
    setSkillBase('Magic', localPlayer:getMagicLevel(), baseMagicLevel)
end

function onSkillChange(localPlayer, id, level, percent)
    id = id + 1
    local t = {
        "Fist", "Club", "Sword", "Axe", "Distance", "Shielding", "Fishing"
    }

    -- imbues, ignore
    if id > #t then return end

    setSkillValue(t[id], level)
    setSkillPercent(t[id], percent)

    setSkillBase(t[id], level, localPlayer:getSkillBaseLevel(id - 1))
end

function onBaseSkillChange(localPlayer, id, baseLevel)
    id = id + 1
    local t = {
        "Fist", "Club", "Sword", "Axe", "Distance", "Shielding", "Fishing"
    }

    -- imbues, ignore
    if id > #t then return end

    setSkillBase(id, localPlayer:getSkillLevel(id), baseLevel)
end

function save()
    local settingsFile = modules.client_profiles.getSettingsFilePath("topbar.json")

    local status, result = pcall(function() return json.encode(settings, 2) end)
    if not status then
        return onError(
                   "Error while saving top bar settings. Data won't be saved. Details: " ..
                       result)
    end

    if result:len() > 100 * 1024 * 1024 then
        return onError(
                   "Something went wrong, file is above 100MB, won't be saved")
    end

    g_resources.writeFileContents(settingsFile, result)
end

function load()
    local settingsFile = modules.client_profiles.getSettingsFilePath("topbar.json")

    if g_resources.fileExists(settingsFile) then
        local status, result = pcall(function()
            return json.decode(g_resources.readFileContents(settingsFile))
        end)
        if not status then
            return onError(
                       "Error while reading top bar settings file. To fix this problem you can delete storage.json. Details: " ..
                           result)
        end
        settings = result
    else
        settings = {}
    end
end