local mod_name = minetest.get_current_modname()
local mod_path = minetest.get_modpath(mod_name)
local S = minetest.get_translator(mod_name)



local function alert_nearby_deer(self, dist)
    if not dist then dist = 30 end
    local nearby_objects = minetest.get_objects_inside_radius(self.object:get_pos(), dist)
    for _, object in pairs(nearby_objects) do
        local ent = object:get_luaentity()
        if ent and (not ent._pmb_target)
        and ent.name == "pmb_animals:deer" then
            pmb_entity_api.set_state(ent, "flee")
            ent._pmb_target = self._pmb_target
        end
    end
end


local deer = {
    initial_properties = {
        visual = "mesh",
        mesh = "pmb_deer.b3d",
        textures = {"pmb_deer.png"},
        use_texture_alpha = false,
        stepheight = 0.5,
        hp_max = 20,
        physical = true,
        collisionbox = {-0.4, -0.5, -0.4, 1.5, 1, 0.4},
        selectionbox = {-0.4, -0.5, -0.4, 1.5, 1, 0.4},
        pointable = true,
        damage_texture_modifier = "^[colorize:#ff9999:50",
    },
    _name = "pmb_animals:deer",

    on_step = function(self, dtime, moveresult)
        pmb_entity_api.mob_on_step(self, dtime, moveresult)
    end,
    _drop = {
        max_items = 2,
        items = {
            {
                rarity = 1,
                items = {"pmb_animals:animal_meat"},
            },
            {
                rarity = 2,
                items = {"pmb_animals:animal_meat"},
            },
        }
    },
    on_death = function(self, killer)
        -- if killer then
        -- end
    end,

    _get_deer_pack = function(self)
        self._pmb_deer_pack = pmb_entity_api.get_objects_of_type(self.object:get_pos(), 20, {["pmb_animals:deer"]=true})
    end,

    _states = {
        on_step = function(self, dtime, moveresult)
            pmb_entity_api.test_fall_damage(self, dtime, false)
            if not self.object:get_pos() then
                return "die"
            end

            if self._pmb_deer_pack_recalc_timer and self._pmb_deer_pack_recalc_timer > 0 then
                self._pmb_deer_pack_recalc_timer = self._pmb_deer_pack_recalc_timer - dtime
            else
                self._pmb_deer_pack_recalc_timer = math.random() + 1
                self:_get_deer_pack()
            end

            if self._state ~= "death" then
                local in_or_above_water = pmb_entity_api.float_in_liquids(self, dtime * 20, {offset=vector.new(0, 1.2, 0)})
                if not in_or_above_water then
                    pmb_entity_api.apply_gravity(self, dtime)
                end
            end
        end,
        idle = {
            animation = "idle",
            on_state_start = function(self)
                self.object:set_velocity(vector.new(0,0,0))
                self._pmb_speed = 4
            end,
            step = function(self, dtime, moveresult)
                pmb_entity_api.decelerate(self, 0.5)
                pmb_entity_api.get_wander(self, 270)
                pmb_entity_api.get_target(self, nil, {no_to_pos=false})

                local dist = pmb_entity_api.get_target_dist(self)

                if self._pmb_to_pos and self._pmb_state_time > 5 then
                    return "roam"
                elseif self._pmb_to_pos and self._pmb_target and ((math.random() < 0.01)
                or (dist and dist < 10)) then
                    return "flee"
                end
            end,
        },
        roam = {
            -- animation = "walk",
            on_state_start = function(self)
                self._pmb_roam_check_timer = 4
                self._pmb_speed = 4
            end,
            step = function(self, dtime, moveresult)
                self._pmb_roam_check_timer = self._pmb_roam_check_timer - dtime

                pmb_entity_api.get_wander(self, 300)

                local tmp_vel = self.object:get_velocity()
                tmp_vel.y = 0
                if vector.length(tmp_vel) > 0.2 then
                    pmb_entity_api.set_my_animation(self, "walk")
                else
                    pmb_entity_api.set_my_animation(self, "idle")
                end

                pmb_entity_api.get_target(self, nil, {no_to_pos=false})
                pmb_entity_api.get_path(self)
                pmb_entity_api.do_path(self)
                pmb_entity_api.check_jump(self, 4)

                if not pmb_entity_api.path_reachable(self) then
                    self._pmb_to_pos = nil
                end
                -- no target or target pos
                if (not self._pmb_to_pos) and (not self._pmb_target) then
                    return "idle"
                end

                -- found target, so run
                if self._pmb_target then
                    return "flee"
                end

                if self._pmb_roam_check_timer <= 0 and self._pmb_to_pos
                and (self._pmb_deer_pack and #self._pmb_deer_pack > 1) then
                    self._pmb_roam_check_timer = 3
                    local list = self._pmb_deer_pack
                    local avg_pos

                    for i, obj in pairs(list) do
                        if obj then
                            local opos = obj:get_pos()
                            if opos then
                                if avg_pos then
                                    avg_pos = vector.add(avg_pos, opos)
                                else
                                    avg_pos = opos
                                end
                            end
                        end
                    end
                    avg_pos = vector.divide(avg_pos, #list)
                    avg_pos = vector.add(avg_pos, self._pmb_to_pos)
                    avg_pos = vector.divide(avg_pos, 2)

                    self._pmb_to_pos = vector.copy(avg_pos)
                    self._pmb_path = {}
                    self._pmb_path_timer = 30947
                    pmb_entity_api.get_path(self, pmb_entity_api.min_cost)
                    -- minetest.chat_send_all("corrected to pack " .. dump(self._pmb_path ~= nil))
                end

                pmb_entity_api.rotate_to_path(self, dtime*8)
            end,
        },
        flee = {
            animation = nil,
            on_state_start = function(self)
                -- self._pmb_to_pos = nil
                self._pmb_speed = 8
            end,
            step = function(self, dtime, moveresult)

                local tmp_vel = self.object:get_velocity()
                tmp_vel.y = 0
                if vector.length(tmp_vel) > 4 then
                    pmb_entity_api.set_my_animation(self, "run")
                elseif vector.length(tmp_vel) > 0.2 then
                    pmb_entity_api.set_my_animation(self, "walk")
                else
                    pmb_entity_api.set_my_animation(self, "idle")
                end

                local dist = pmb_entity_api.get_target_dist(self)
                if dist and dist > 90 then return "roam" end

                if not self._pmb_target and self._pmb_state_time > 5 then
                    return "roam"
                elseif self._pmb_target then
                    self._pmb_run_from_pos = self._pmb_target:get_pos()
                end

                if self._pmb_run_from_pos then
                    pmb_entity_api.run_from(self, self._pmb_run_from_pos, {})
                end

                if (not self._pmb_to_pos) and not self._pmb_time_before_idle then
                    self._pmb_time_before_idle = self._pmb_state_time
                    if self._pmb_state_time > self._pmb_time_before_idle + 1 then
                        return "idle"
                    end
                end

                pmb_entity_api.check_jump(self, 4)

                pmb_entity_api.rotate_to_path(self, dtime*8)
            end,
        },
        death = {
            animation = "death",
            on_state_start = function(self)
                self._pmb_detectable = false
                self.object:set_properties({
                    pointable = false,
                })
            end,
            step = function(self, dtime, moveresult)
                pmb_entity_api.decelerate(self, 0.2)
                local vel = self.object:get_velocity()
                vel.y = vel.y - self._pmb_gravity * dtime
                self.object:set_velocity(vel)
                if self._pmb_state_time > 2 then
                    pmb_entity_api.do_drops(self)
                    self.object:remove()
                end
            end,
        },
    },
    _default_state = "idle",
    on_activate = function(self, staticdata, dtime_s)
        self.object:set_armor_groups({
            pierce=200,
            slash=100,
            blunt=100,
            magic=100,
            poison=100,
        })
        -- self.object:set_hp(self._pmb_max_health)
        pmb_entity_api.on_activate(self, staticdata, dtime_s)
        self._pmb_deer_pack_recalc_timer = 0

        local pos = self.object:get_pos()
        if self._age ~= nil and (not self._no_despawn) and pos and not pmb_entity_api.has_mobs_in_radius(pos, 40, {self.name}, 0, 5) then
            return self.object:remove()
        end
    end,
    get_staticdata = function(self)
        return pmb_entity_api.get_staticdata(self)
    end,
    on_deactivate = function(self, removal)
        pmb_entity_api.on_deactivate(self, removal)
    end,
    on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
        if damage and damage > 0 then
            pmb_animals.do_sound(self, "hurt_0", 1)
            if puncher and puncher ~= self.object and not self._pmb_target then
                self._pmb_target = puncher
                alert_nearby_deer(self, 80)
                pmb_entity_api.set_state(self, "flee")
            end
            local hp = self.object:get_hp() - damage
            if hp <= 0 then
                pmb_entity_api.set_state(self, "death")
                self.object:set_hp(self._pmb_max_health)
                self.object:set_armor_groups({fleshy=0})
                return true
            end
        end
        return pmb_entity_api.damage.on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
    end,
    _pmb_staticdata_load_list = {
        "_pmb_speed",
        "_pmb_damage_groups",
        "_pmb_hostile",
    },
    _pmb_speed = 2,
    _pmb_acceleration = 20,
    _pmb_gravity = 9,
    _pmb_max_health = 20,
    _pmb_deer_pack = {},
    _pmb_deer_pack_recalc_timer = 0,
    _pmb_hostile = {player=1, ["pmb_animals:wolf"]=1},
    _pmb_scariness = 1,
    _pmb_damage_groups = {
        pierce=1,
        slash=1,
        blunt=2,
    },
    _animations = {
        idle = {frames={x=0, y=174}, blend=0.2, speed=40},
        walk = {frames={x=181, y=219}, blend=0.1, speed=40},
        run = {frames={x=231, y=259}, blend=0.1, speed=60},
        death = {frames={x=270, y=276}, blend=0.2, loop=false, speed=40},
    },
    _pmb_wander = 3,
    _pmb_range = 50,
    _pmb_statusfx_enable = true,
}


minetest.register_entity("pmb_animals:deer", deer)


minetest.register_craftitem("pmb_animals:deer_spawn",
{
    description = "Deer spawn egg",
    inventory_image = "pmb_deer.png",
    on_place = function(itemstack, placer, pointed_thing)
        local ent = minetest.add_entity(vector.offset(minetest.get_pointed_thing_position(pointed_thing), 0, 1, 0), "pmb_animals:deer")
    end,
})




local _biomes = { ["ash_forest_dense"]=0.8, ["ash_forest"]=0.5, ["taiga_tall_dense"]=1, ["ruined_valley"]=0.3, }
local _nodes = { ["pmb_soil:forest_dirt"]=0.5, ["pmb_soil:forest_grass_1"]=0.2, ["pmb_soil:forest_grass_2"]=1, ["pmb_soil:snow"]=1, }

if pmb_mob_spawn then
    pmb_mob_spawn.register_mob({
        name = "pmb_animals:deer",
        groups = {"passive", "nature", "pmb_animals_deer"},
        biomes = {},
        on_spawn = function(self, pos)
            for i = 0, (math.random(1,3)) do
                local spos = vector.offset(
                    pos,
                    math.random()*2-1,
                    0.8,
                    math.random()*2-1
                )
                local obj = minetest.add_entity(spos, "pmb_animals:deer")
                obj:set_yaw(math.random() * math.pi * 2)
            end
        end,
        can_spawn = function(pos, spawn_info)
            -- do cheap tests first
            if not pmb_dimensions.is_in("overworld", pos) then return false end
            if not pmb_mob_spawn.has_head_room(pos, 1) then return false end
            local node_below = minetest.get_node(vector.offset(
                pos, 0, -1, 0
            ))
            -- only spawn under sky
            if not pmb_mob_spawn.has_natural_light(pos, 9, 15) then return false end
            -- don't spawn if artificial lights around
            if pmb_mob_spawn.has_light(pos, 2, 15, 0) then return false end

            local spawnable = false
            if (_biomes[spawn_info.biome_name] ~= nil) and (math.random() < _biomes[spawn_info.biome_name]) then
                spawnable = true
            elseif (_nodes[node_below.name] ~= nil) and (math.random() < _nodes[node_below.name]) then
                spawnable = true
            end

            -- do expensive tests last
            if spawnable then
                if pmb_entity_api.has_mobs_in_radius(pos, 60, {"pmb_animals:deer"}, 0, 1) then
                    return true
                end
            end
            return false
        end,
        can_despawn = function(self)
            local pos = self.object:get_pos()
            if (not pos) or not pmb_entity_api.has_mobs_in_radius(pos, 50, {"pmb_animals:deer"}, 0, 7) then
                return true
            end
            if self._age and self._age < 120 then return false end
            return true
        end,
    })
    pmb_mob_spawn.cap.pmb_animals_deer.max = 12
end


deer = nil
