local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath (modname)
-- NOTE: 30 indicates 10 more beyond the visible bar is allowed.

local enable_wonder = minetest.setting_getbool ("enable_wonder")
unified_hunger = {}

-- HUD statbar values
unified_hunger.hunger = {}
unified_hunger.hunger_out = {}

-- HUD item ids
local hunger_hud = {}

if minetest.setting_getbool ("enable_damage") then
    unified_hunger.enable_damage = true
else
    unified_hunger.enable_damage = false
end

HUNGER_HUD_TICK = 0.5 -- 0.1

--Some hunger settings
unified_hunger.exhaustion = {} -- Exhaustion is experimental!

-- time in seconds after that 1 hunger point is taken

local HUNGER_TICK_DEFAULT = 180
if enable_wonder then
      HUNGER_TICK_DEFAULT =  90
end

   HUNGER_HUNGER_TICK = tonumber (minetest.setting_get ("hunger_tick")) or HUNGER_TICK_DEFAULT
if HUNGER_HUNGER_TICK < 10 then HUNGER_HUNGER_TICK = 10 end

HUNGER_EXHAUST_DIG = 1.5  -- exhaustion increased this value after digged node
HUNGER_EXHAUST_PLACE = 1 -- exhaustion increased this value after placed
HUNGER_EXHAUST_MOVE = 0.3 -- exhaustion increased this value if player movement detected
HUNGER_EXHAUST_LVL = 160 -- at what exhaustion player satiation gets lowerd


--[[load custom settings
local set = io.open(minetest.get_modpath("unified_hunger").."/unified_hunger.conf", "r")
if set then
    dofile(minetest.get_modpath("unified_hunger").."/unified_hunger.conf")
    set:close()
end--]]

local function custom_hud(player)
    hb.init_hudbar(player, "satiation", unified_hunger.get_hunger(player))
end

local function pull_hunger(player)
    local inv = player:get_inventory()
    if not inv then return nil end
    local hgp = inv:get_stack("hunger", 1):get_count()
    if hgp == 0 then
        hgp = 21
        inv:set_stack("hunger", 1, ItemStack({name = ":", count = hgp}))
    else
        hgp = hgp
    end
    unified_hunger.hunger[name] = hgp - 1
    return true
end

local function push_hunger(player)
    local inv = player:get_inventory()
    local name = player:get_player_name()
    local value = unified_hunger.hunger[name]
    if not inv or not value then  return nil  end
    inv:set_stack("hunger", 1, ItemStack({name = ":", count = value + 1}))
    return true
end

-- Keep these for backwards compatibility

function unified_hunger.save_hunger(player)
    push_hunger(player)
end

function unified_hunger.load_hunger(player)
    pull_hunger(player)
end

-- Poison player
local function poisenp(tick, time, time_left, player)
    time_left = time_left + tick
    if time_left < time then
        minetest.after(tick, poisenp, tick, time, time_left, player)
    else
        --reset hud image
    end
    if player:get_hp()-1 > 0 then
        player:set_hp(player:get_hp()-1)
    end

end

function unified_hunger.item_eat (hunger_change,
    replace_with_item, poisen, heal, msg)

    return function (itemstack, user, pointed_thing)
        if itemstack:take_item() ~= nil and user ~= nil then
            local name = user:get_player_name()
            local h = tonumber(unified_hunger.hunger[name])
            local hp = user:get_hp()
            minetest.sound_play("unified_hunger_eat_generic", {
                object = user,
                max_hear_distance = 10,
                gain = 1.0
            })

            -- Saturation
            if h < 30 and hunger_change then
                h = h + hunger_change
                if h > 30 then h = 30 end
                unified_hunger.hunger[name] = h
                unified_hunger.set_hunger(user)
            end
            -- Healing
            if hp < 20 and heal then
                hp = hp + heal
                if hp > 20 then hp = 20 end
                user:set_hp(hp)
            end
            -- Poison
            if poisen then
                --set hud-img
                poisenp(1.0, poisen, 0, user)
            end

            if replace_with_item then
                if itemstack:is_empty() then
                    itemstack:add_item(replace_with_item)
                else
                    local inv = user:get_inventory()
                    if inv:room_for_item("main", {name = replace_with_item}) then
                        inv:add_item("main", replace_with_item)
                    else
                        local pos = user:getpos()
                        pos.y = math.floor(pos.y + 0.5)
                        core.add_item(pos, replace_with_item)
                    end
                end
            end

            if msg ~= nil then
                local mt   = type (msg)
                local send = nil

                if     mt == "string" then
                    send = msg
                elseif mt == "table"  then
                    send = msg [math.random (#msg)]
                end

                minetest.chat_send_player (name, send)
            end
        end
        return itemstack
    end
end

unified_hunger.overwrite = function (name, satiate, heal, poison,
    replace, msg)

    if not unified_hunger.enable_damage then
        poison = nil
    end
    name = minetest.registered_aliases [name] or name

    if minetest.registered_items [name] ~= nil then
        minetest.override_item (name, {
            on_use = unified_hunger.item_eat (satiate,
                replace, poison, heal, msg)
        })
    end
end

local overwrite = unified_hunger.overwrite

-- ===================================================================

-- player-action based hunger changes
function unified_hunger.handle_node_actions(pos, oldnode, player, ext)
    if not player or not player:is_player() then
        return
    end
    local name = player:get_player_name()
    local exhaus = unified_hunger.exhaustion[name]
    if exhaus == nil then return end
    local new = HUNGER_EXHAUST_PLACE
    -- placenode event
    if not ext then
        new = HUNGER_EXHAUST_DIG
    end
    -- assume its send by main timer when movement detected
    if not pos and not oldnode then
        new = HUNGER_EXHAUST_MOVE
    end
    exhaus = exhaus + new
    if exhaus > HUNGER_EXHAUST_LVL then
        exhaus = 0
        local h = tonumber(unified_hunger.hunger[name])
        h = h - 1
        if h < 0 then h = 0 end
        unified_hunger.hunger[name] = h
        unified_hunger.set_hunger(player)
    end
    unified_hunger.exhaustion[name] = exhaus
end

--minetest.register_on_placenode(unified_hunger.handle_node_actions)
minetest.register_on_dignode(unified_hunger.handle_node_actions)

-- register satiation hudbar
hb.register_hudbar(
    "satiation", 0xFFFFFF, "Satiation",
    {
        icon = "unified_hunger_icon.png",
        bgicon = "unified_hunger_bgicon.png",
        bar = "unified_hunger_bar.png"
    },
    20, 30, false
)

-- update hud elemtents if value has changed
local function update_hud(player)
    local name = player:get_player_name()
    local h_out = tonumber(unified_hunger.hunger_out[name])
    local h = tonumber(unified_hunger.hunger[name])
    if h_out ~= h then
        unified_hunger.hunger_out[name] = h
        hb.change_hudbar(player, "satiation", h)
    end
end

unified_hunger.get_hunger = function(player)
    pull_hunger(player)
    return unified_hunger.hunger[name]
end

unified_hunger.set_hunger = function(player, value)
    local name = player:get_player_name()
    if value > 30 then value = 30 end
    if value < 0 then value = 0 end
    unified_hunger.hunger[name] = value
    return push_hunger(player)
end



minetest.register_on_joinplayer(function(player)
    local name = player:get_player_name()
    local inv = player:get_inventory()
    inv:set_size("hunger", 1)
    pull_hunger(player)  -- Load hunger[name] (and ensure non-nil).
    unified_hunger.hunger_out[name] = unified_hunger.hunger[name]
    unified_hunger.exhaustion[name] = 0
    custom_hud(player)
    push_hunger(player)  -- Save the now non-nil value.
end)

minetest.register_on_respawnplayer(function(player)
    -- reset hunger (and save)
    local name = player:get_player_name()
    unified_hunger.set_hunger(player, 20)
    unified_hunger.exhaustion[name] = 0
end)

-- ===================================================================

local param_starve = {
    description = "Be hungry" ,
    params      = ""          ,
    privs       = {}          ,

    func = function (plname, params)
        local player = minetest.env:get_player_by_name (plname)
        unified_hunger.hunger [plname] = 5
        unified_hunger.set_hunger (player)
        minetest.chat_send_player (plname, "O.K. you're hungry")

    end
}

minetest.register_chatcommand ("starve", param_starve)

-- ===================================================================

minetest.register_privilege ("satiate", {
    description          = "satiate administration" ,
    give_to_singleplayer = false                    ,
})

local param_satiate = {
    description = "Be satiated"    ,
    params      = ""               ,
    privs       = { satiate=true } ,

    func = function (plname, params)
        local player = minetest.env:get_player_by_name (plname)
        unified_hunger.hunger [plname] = 20
        unified_hunger.set_hunger (player)
        minetest.chat_send_player (plname, "O.K. you're satiated")

    end
}

minetest.register_chatcommand ("satiate", param_satiate)

-- ===================================================================

if unified_hunger.enable_damage then
local main_timer = 0
local timer = 0
local timer2 = 0

minetest.register_globalstep(function(dtime)
    main_timer = main_timer + dtime
    timer = timer + dtime
    timer2 = timer2 + dtime

    if main_timer > HUNGER_HUD_TICK
    or timer > 4
    or timer2 > HUNGER_HUNGER_TICK then

        if  main_timer > HUNGER_HUD_TICK then
            main_timer = 0
        end

        for _,player in pairs(minetest.get_connected_players()) do

            local name = player:get_player_name()
            local h = tonumber(unified_hunger.hunger[name])
            local hp = player:get_hp()

            if timer > 4 then

                -- heal player by 1 hp if not dead and satiation is > 15
                if h > 15
                and hp > 0
                and player:get_breath() > 0 then
                    player:set_hp(hp + 1)
                -- or damage player by 1 hp if satiation is < 2
                elseif h <= 1 then
                    if hp - 1 >= 0 then
                        player:set_hp(hp - 1)
                    end
                end
            end

            -- lower satiation by 1 point after xx seconds
            if timer2 > HUNGER_HUNGER_TICK then
                    if h > 0 then
                    h = h - 1
                    unified_hunger.hunger[name] = h
                    unified_hunger.set_hunger(player)
                end
            end

            -- update hud elements
            update_hud(player)

            -- Determine if player is walking
            local controls = player:get_player_control()
            if controls.up
            or controls.down
            or controls.left
            or controls.right then
                unified_hunger.handle_node_actions(nil, nil, player)
            end

        end
    end

    if timer > 4 then
        timer = 0
    end

    if timer2 > HUNGER_HUNGER_TICK then
        timer2 = 0
    end
end)
end -- end if damage enabled