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_wolves(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:wolf" then
            pmb_entity_api.set_state(ent, "charge")
            ent._pmb_target = self._pmb_target
        end
    end
end


local function is_scared_of_target(self)
    if (not self._pmb_target) or (not self._pmb_target:get_pos()) then return end

    local power_factor = 1
    if self._pmb_target:is_player() then
        power_factor = 4
    else
        local ent = self._pmb_target:get_luaentity()
        if not ent then return end
        power_factor = (ent._pmb_scariness or 1) / self._pmb_scariness
    end

    if (not pmb_entity_api.has_mobs_in_radius(self.object:get_pos(), 50, {"pmb_animals:wolf"}, 0, power_factor-1)) then
        return false
    else
        return true
    end
end


local function should_attack_target(self, obj)
    if true then return true end
    -- DISABLED FOR NOW because it's broken
    local dist = vector.distance(self.object:get_pos(), obj:get_pos())
    -- in attack range
    if dist < 20 then
        return true
    elseif self._pmb_wolf_pack and #self._pmb_wolf_pack >= 5 then
        return true
    end
    return false
end

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

    on_step = pmb_entity_api.do_states(),
    _drop = {
        max_items = 2,
        items = {
            {
                rarity = 8,
                items = {"pmb_animals:wolf_fang"},
            },
            {
                rarity = 1,
                items = {"pmb_animals:animal_meat"},
            },
            {
                rarity = 2,
                items = {"pmb_animals:wolf_pelt"},
            },
        }
    },
    on_death = function(self, killer)
        -- if killer then
        -- end
    end,

    _get_wolf_pack = function(self)
        self._pmb_wolf_pack = pmb_entity_api.get_objects_of_type(self.object:get_pos(), 20, {["pmb_animals:wolf"]=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 "death"
            end
            if self._pmb_target then
                self._pmb_speed = 18
            else
                self._pmb_speed = 4
            end

            pmb_entity_api.decelerate(self, 0.97)

            self._pmb_time_since_target = (self._pmb_time_since_target or 0) + dtime

            if self._pmb_wolf_pack_recalc_timer and self._pmb_wolf_pack_recalc_timer > 0 then
                self._pmb_wolf_pack_recalc_timer = self._pmb_wolf_pack_recalc_timer - dtime
            else
                self._pmb_wolf_pack_recalc_timer = math.random() + 1
                self:_get_wolf_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))
                if math.random() < 0.25 then
                    self._pmb_can_sit = true
                end
            end,
            step = function(self, dtime, moveresult)
                pmb_entity_api.decelerate(self, 0.5)
                pmb_entity_api.get_target(self, nil, {no_to_pos=false})
                pmb_entity_api.get_wander(self, 170)
                if self._pmb_state_time > 5 and self._pmb_can_sit then
                    self._pmb_can_sit = false
                    return "sit"
                end
                if self._pmb_state_time > 1 then
                    if self._pmb_target then
                        if (not is_scared_of_target(self)) then
                            return "charge"
                        else
                            return "flee"
                        end
                    end
                    return "roam"
                end
            end,
        },
        sit = {
            animation = "sit",
            on_state_start = function(self)
                self.object:set_velocity(vector.new(0,0,0))
            end,
            step = function(self, dtime, moveresult)
                pmb_entity_api.decelerate(self, 0.2)
                pmb_entity_api.get_target(self, nil, {no_to_pos=true})
                if self._pmb_target then
                    if (not is_scared_of_target(self)) then
                        return "charge"
                    else
                        return "flee"
                    end
                end
            end,
        },
        roam = {
            animation = "walk",
            on_state_start = function(self)
                self._pmb_roam_check_timer = 4
            end,
            step = function(self, dtime, moveresult)
                if not self._pmb_attack_cooldown then self._pmb_attack_cooldown = 2 end
                if self._pmb_attack_cooldown > 0 then self._pmb_attack_cooldown = self._pmb_attack_cooldown - dtime end
                self._pmb_roam_check_timer = self._pmb_roam_check_timer - dtime

                pmb_entity_api.get_wander(self, 700)

                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, nil, 0.1)
                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 chase them
                if self._pmb_target then
                    if is_scared_of_target(self) then
                        return "flee"
                    else
                        return "charge"
                    end
                end

                if self._pmb_roam_check_timer <= 0 and self._pmb_to_pos
                and (self._pmb_wolf_pack and #self._pmb_wolf_pack > 1) then
                    self._pmb_roam_check_timer = 3
                    local list = self._pmb_wolf_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_flee_check_timer = 0
            end,
            step = function(self, dtime, moveresult)
                self._pmb_flee_check_timer = self._pmb_flee_check_timer - dtime

                if self._pmb_flee_check_timer <= 0 then
                    self._pmb_flee_check_timer = 0.5 + math.random()
                    if self._pmb_target and not is_scared_of_target(self) then
                        return "charge"
                    end
                end

                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) > 1 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 > 60 then return "roam" end

                -- pmb_entity_api.get_target(self, nil, {no_to_pos=true})
                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,
        },
        charge = {
            animation = nil,
            on_state_start = function(self)
                if self._pmb_target then
                    self._pmb_time_since_target = 0
                end
                self._pmb_flee_check_timer = 0
            end,
            step = function(self, dtime, moveresult)
                if not self then return end
                if not self._pmb_attack_cooldown then self._pmb_attack_cooldown = 5 end
                if self._pmb_attack_cooldown > 0 then self._pmb_attack_cooldown = self._pmb_attack_cooldown - dtime end
                self._pmb_flee_check_timer = self._pmb_flee_check_timer - dtime

                if self._pmb_flee_check_timer <= 0 then
                    self._pmb_flee_check_timer = 0.5 + math.random()
                    if self._pmb_target then
                        if (is_scared_of_target(self)) then
                            return "flee"
                        end
                    end
                end
                local dist = pmb_entity_api.get_target_dist(self)

                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) > 1 then
                    pmb_entity_api.set_my_animation(self, "walk")
                else
                    pmb_entity_api.set_my_animation(self, "idle")
                end

                if dist and dist > 5 then
                    pmb_entity_api.get_and_follow_target(self, 2.5)
                else
                    pmb_entity_api.get_and_follow_target(self, 1)
                end
                pmb_entity_api.check_jump(self, 4)

                -- no target or target pos
                if (not self._pmb_target) then
                    return "roam"
                end

                if false and dist and dist < 5 then
                    pmb_entity_api.rotate_to_target(self, dtime*2)
                end
                pmb_entity_api.rotate_to_path(self, dtime*8)

                if pmb_entity_api.has_los_to_object(self, self._pmb_target) then
                    pmb_entity_api.punch_in_radius(self, 1, 3, {
                        damage_groups = {
                            pierce=2,
                            slash=1,
                        }
                    })
                end

                local has_los
                if self._pmb_target ~= nil then
                    has_los = pmb_entity_api.has_los_to_object(self, self._pmb_target)
                end

                if (dist and dist > 80 or not has_los) and self._pmb_state_time > 9 then
                    self._pmb_target = nil
                    pmb_entity_api.get_target(self)
                end

                if self._pmb_to_pos and self._pmb_attack_cooldown <= 0 and dist and dist < 6 then
                    -- pmb_entity_api.do_path(self, 0)
                    self._pmb_attack_cooldown = nil
                    local vel = self.object:get_velocity()
                    vel.x = 0
                    vel.z = 0
                    self.object:set_velocity(vel)
                    return "pre_lunge"
                end
            end,
        },
        pre_lunge = {
            animation = "idle",
            on_state_start = function(self)
                self._pmb_lunge_time = 0.01
            end,
            step = function(self, dtime, moveresult)
                if not self._pmb_target then
                    pmb_entity_api.get_target(self)
                end
                if not self._pmb_path then
                    pmb_entity_api.get_path(self, pmb_entity_api.min_cost)
                end
                pmb_entity_api.rotate_to_target(self, dtime*3)

                if not self._pmb_lunge_time then self._pmb_lunge_time = 0.01 end
                if self._pmb_lunge_time > 0 then self._pmb_lunge_time = self._pmb_lunge_time - dtime end

                if self._pmb_lunge_time <= 0 then
                    self._pmb_lunge_time = nil
                    return "lunge"
                end
            end,
        },
        lunge = {
            animation = "lunge",
            on_state_start = function(self)
                pmb_animals.do_sound(self, "lunge_"..(math.random(0,3)), 1)
                self._pmb_path = nil
                self._pmb_to_pos = nil
            end,
            step = function(self, dtime, moveresult)
                if not self._pmb_attack_time then self._pmb_attack_time = 1
                else self._pmb_attack_time = self._pmb_attack_time - dtime end

                local target_pos = self._pmb_target and self._pmb_target:get_pos()
                if not target_pos then self._pmb_target = nil end

                if self._pmb_attack_time == 1 then
                    if not self._pmb_target then
                        return "idle"
                    end
                    local pos = self.object:get_pos()
                    local dir = vector.direction(pos, target_pos)
                    local yaw = minetest.dir_to_yaw(dir)
                    self.object:set_yaw(yaw)
                    dir = vector.multiply(dir, self._pmb_speed * 0.8)
                    dir.y = 3
                    self.object:set_velocity(dir)
                end

                if self._pmb_attack_time <= 0 then
                    self._pmb_attack_time = nil
                    return "charge"
                end

                local dist = pmb_entity_api.get_target_dist(self)
                if self._pmb_since_attack >= 1 and dist and dist < 2
                and pmb_entity_api.has_los_to_object(self, self._pmb_target) then
                    self._pmb_target:punch(self.object, 1.0, {
                        full_punch_interval = 1.0,
                        damage_groups = {
                            pierce=2,
                            slash=1,
                        }
                    }, nil)
                    self._pmb_since_attack = 0
                end
            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 > 3 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=200,
            magic=100,
            poison=100,
        })
        -- self.object:set_hp(self._pmb_max_health)
        pmb_entity_api.on_activate(self, staticdata, dtime_s)
        self._pmb_wolf_pack_recalc_timer = 0
    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_wolves(self, 60)
                pmb_entity_api.set_state(self, "charge")
            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 = 5,
    _pmb_acceleration = 20,
    _pmb_gravity = 9,
    _pmb_max_health = 10,
    _pmb_wolf_pack = {},
    _pmb_wolf_pack_recalc_timer = 0,
    _pmb_hostile = {player=1, ["pmb_animals:deer"]=1},
    _pmb_scariness = 3,
    _pmb_damage_groups = {
        pierce=2,
        slash=1,
    },
    _animations = {
        idle = {frames={x=0, y=9}, blend=0.1},
        walk = {frames={x=10, y=29}, blend=0.1},
        run = {frames={x=40, y=54}, blend=0.1},
        lunge = {frames={x=40, y=54}, blend=0.1},
        pre_lunge = {frames={x=60, y=70}, blend=0.1},
        sit = {frames={x=80, y=90}, blend=0.4},
        death = {frames={x=100, y=110}, blend=0.2, loop=false},
    },
    _pmb_wander = 3,
    _pmb_range = 20,
    _pmb_statusfx_enable = true,
}


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


minetest.register_craftitem("pmb_animals:wolf_spawn",
{
    description = "Wolf spawn egg",
    inventory_image = "pmb_wolf.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:wolf")
    end,
})




local _biomes = { ["taiga_tall"]=0.7, ["ruined_valley"]=0.3, ["tundra"]=0.8, }
local _nodes = { ["pmb_soil:forest_grass"]=0.7, ["pmb_soil:forest_grass_1"]=1, ["pmb_soil:forest_grass_2"]=1, ["pmb_soil:snow"]=0.7, }

if pmb_mob_spawn then
    pmb_mob_spawn.register_mob({
        name = "pmb_animals:wolf",
        groups = {"hostile", "nature", "pmb_animals_wolf"},
        biomes = {},
        on_spawn = function(self, pos)
            self._pmb_scariness = math.random(1,4) / 2
            for i = 0, (math.random(1,8)) do
                local spos = vector.offset(
                    pos,
                    math.random()*2-1,
                    0.8,
                    math.random()*2-1
                )
                local obj = minetest.add_entity(spos, "pmb_animals:wolf")
                obj:set_yaw(math.random() * math.pi * 2)
                local ent = obj:get_luaentity()
                if ent then
                    ent._pmb_scariness = self._pmb_scariness
                end
            end
        end,
        can_spawn = function(pos, spawn_info)
            if math.random() > 0.1 then return false end
            -- 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, 2) then
                -- minetest.log(minetest.colorize("#f00", "no head room"))
                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
                -- minetest.log(minetest.colorize("#f00", "no nat light"))
                return false end
            -- don't spawn if lights around
            if pmb_mob_spawn.has_light(pos, 5, 15, 0) then
                -- minetest.log(minetest.colorize("#f00", "too close to torches"))
                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() < 0.3) then
                spawnable = true
            else
                -- minetest.log(minetest.colorize("#f00", "chance failed"))
            end

            -- do expensive tests last
            if spawnable then
                if pmb_entity_api.has_mobs_in_radius(pos, 80, {"pmb_animals:wolf"}, 0, 2) 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, 40, {"pmb_animals:wolf"}, 0, 12) then
                return true
            end
            if self._age and self._age < 120 then return false end
            return true
        end,
    })
    pmb_mob_spawn.cap.pmb_animals_wolf.max = 20
end

wolf = nil
