pmb_elemental.stone = {}
function pmb_elemental.stone.do_sound(self, name, gain)
    minetest.sound_play(("pmb_elementals_"..name), {
        gain = gain or 1,
        pos = self.object:get_pos(),
        object = self.object,
    })
end

minetest.register_entity("pmb_elementals:stone", {
    initial_properties = {
        visual = "mesh",
        mesh = "pmb_elemental.b3d",
        textures = {"pmb_elemental_stone.png"},
        use_texture_alpha = false,
        stepheight = 0.5,
        hp_max = 10,
        physical = true,
        collisionbox = {-0.6, -0.5, -0.6, 0.6, 2.2, 0.6},
        selectionbox = {-0.6, -0.5, -0.6, 0.6, 2.2, 0.6},
        pointable = true,
        damage_texture_modifier = "^[colorize:#ff9999:50",
        -- automatic_face_movement_dir = 90.0,
        -- automatic_face_movement_max_rotation_per_sec = 360,
    },
    _name = "pmb_elementals:stone",


    on_step = pmb_entity_api.do_states(),


    _drop = {
        max_items = 5,
        items = {
            {
                rarity = 3,
                items = {"pmb_items:copper_nugget"},
            },
            {
                rarity = 2,
                items = {"pmb_elementals:elemental_core_stone"},
            },
            {
                rarity = 3,
                items = {"pmb_items:tin_nugget"},
            },
            {
                rarity = 2,
                items = {"pmb_stone:cobble"},
            },
            {
                rarity = 2,
                items = {"pmb_stone:cobble 2"},
            },
        }
    },
    on_death = function(self, killer)
        if killer then
            pmb_entity_api.do_drops(self)
        end
    end,

    _states = {
        on_step = function (self, dtime, moveresult)
            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
                pmb_entity_api.decelerate(self, 0.98)
            end
        end,
        idle = {
            animation = "idle",
            step = function(self, dtime, moveresult)
                if not self._pmb_idle_time then self._pmb_idle_time = 0
                else self._pmb_idle_time = self._pmb_idle_time + dtime end
                pmb_entity_api.get_target(self)
                pmb_entity_api.get_wander(self, 60)
                pmb_entity_api.decelerate(self, 0.98)
                if self._pmb_to_pos and self._pmb_idle_time > 1 then
                    self._pmb_idle_time = nil
                    return "follow"
                end
            end,
        },
        defend = {
            animation = "defend",
            fromstates = {idle=1, follow=1},
            on_state_start = function(self)
                self._pmb_defend_time = 0
            end,
            step = function(self, dtime, moveresult)
                self._pmb_defend_time = self._pmb_defend_time + dtime
                pmb_entity_api.get_target(self)
                pmb_entity_api.decelerate(self, 0.98)
                if self._pmb_defend_time > (1 / pmb_entity_api.get_current_animation_length(self))*19 then
                    self._pmb_defend_time = nil
                    return "follow"
                end
            end,
        },
        shoulder_smash = {
            animation = "shoulder_smash",
            fromstates = {idle=1, follow=1},
            on_state_start = function(self)
                self._pmb_substate = "windup"
                pmb_elemental.stone.do_sound(self, "charge_start")
            end,
            step = function(self, dtime, moveresult)
                pmb_entity_api.get_target(self)
                pmb_entity_api.decelerate(self, 0.98)
                pmb_entity_api.rotate_to_target(self, dtime*12)
                if self._pmb_substate == "windup"
                and self._pmb_state_time > (1 / pmb_entity_api.get_current_animation_length(self))*10 then
                    self._pmb_substate = nil
                    local dist = pmb_entity_api.get_target_dist(self)
                    if dist and dist < 4 then
                        self._pmb_target:punch(self.object, 1.0, {
                            full_punch_interval = 1.0,
                            damage_groups = {
                                blunt=4,
                            }
                        }, nil)
                        self._pmb_since_attack = 0
                        pmb_elemental.stone.do_sound(self, "hurt_1")
                    end
                end
                if self._pmb_state_time > (1 / pmb_entity_api.get_current_animation_length(self))*27 then
                    return "follow"
                end
            end,
        },
        follow = {
            animation = "follow",
            on_state_start = function(self)
                self._walk_frame = nil
            end,
            step = function(self, dtime, moveresult)
                ----------------------------------------------------------
                -- so dumb that we need to implement a timer that already exists in the engine..
                if not self._walk_frame then
                    self._walk_frame = 0
                end

                local animframes = pmb_entity_api.get_current_animation_length(self)
                self._walk_frame = (self._walk_frame + dtime) % ((1 / animframes) * (119 - 60))
                -- mod to the frame count and so the anim duration
                if self._walk_frame >= (7 * (1 / animframes)) and not self._walk_frame_flag then
                    pmb_elemental.stone.do_sound(self, "step", 0.3)
                    self._walk_frame_flag = 1
                elseif self._walk_frame >= (37 * (1 / animframes)) and self._walk_frame_flag == 1 then
                    pmb_elemental.stone.do_sound(self, "step", 0.3)
                    self._walk_frame_flag = 2
                elseif self._walk_frame < (37 * (1 / animframes)) and self._walk_frame_flag == 2 then
                    self._walk_frame_flag = nil
                end
                ----------------------------------------------------------

                if not self._pmb_attack_cooldown then self._pmb_attack_cooldown = 1 end
                if self._pmb_attack_cooldown > 0 then self._pmb_attack_cooldown = self._pmb_attack_cooldown - dtime end

                if self._pmb_target and not self._pmb_to_pos then
                    self._pmb_to_pos = self._pmb_target:get_pos()
                    self._pmb_path = {self._pmb_to_pos}
                end
                if not self._pmb_target and self._pmb_state_time > 4 then
                    pmb_entity_api.get_target(self)
                end

                pmb_entity_api.get_path(self, pmb_entity_api.min_cost)
                pmb_entity_api.do_path(self)
                pmb_entity_api.decelerate(self, 0.96)
                pmb_entity_api.check_jump(self, 4)

                local dist = pmb_entity_api.get_target_dist(self) or 1000

                pmb_entity_api.rotate_to_path(self, dtime*8)

                if (not self._pmb_to_pos) and (not self._pmb_target) then return "idle" end

                if (dist > 50) or self._pmb_state_time > 1 then
                    pmb_entity_api.get_target(self)
                end

                if self._pmb_target and self._pmb_to_pos and self._pmb_attack_cooldown <= 0
                and pmb_entity_api.has_los_to_object(self, self._pmb_target) then
                    if dist < 5 then
                        if math.random() < 0.5 then
                            self._pmb_attack_cooldown = nil
                            self._walk_frame = nil
                            return "smash"
                        else
                            self._pmb_attack_cooldown = nil
                            self._walk_frame = nil
                            return "shoulder_smash"
                        end
                    elseif math.random() < 0.2
                    and dist < 15 then
                        pmb_entity_api.do_path(self, 0)
                        self._pmb_attack_cooldown = nil
                        self._walk_frame = nil
                        return "charge"
                    else
                        self._pmb_attack_cooldown = 1
                    end
                end
            end,
        },
        charge = {
            animation = "charge",
            step = function(self, dtime, moveresult)
                if not self._pmb_charge_time then
                    self._pmb_charge_time = 0
                    pmb_entity_api.get_target(self)
                    pmb_entity_api.rotate_to_target(self, 1)
                    self._pmb_substate = "start"
                    self._pmb_since_attack = 1
                    pmb_elemental.stone.do_sound(self, "charge_start")
                end
                if self._pmb_substate == "start" then
                    pmb_entity_api.decelerate(self, 0.98)
                end

                local animframes = pmb_entity_api.get_current_animation_length(self)

                if self._pmb_substate == "start"
                and self._pmb_charge_time > (1 / animframes) * 13 then
                    self._pmb_substate = "charge"
                    pmb_elemental.stone.do_sound(self, "charge")
                    pmb_elemental.stone.do_sound(self, "steps", 0.2)

                    local tp = (self._pmb_target and self._pmb_target:get_pos())
                    if tp then
                        tp = tp + (self._pmb_target:get_velocity() * 0.5)
                        pmb_entity_api.rotate_to_pos(self, tp, 1, false)
                    else
                        pmb_entity_api.rotate_to_target(self, 1, false)
                    end
                end
                if self._pmb_substate == "charge"
                and (self._pmb_charge_time < (1 / animframes) * (40)) then
                    -- move toward where the player was
                    local dir = self.object:get_yaw()
                    dir = minetest.yaw_to_dir(dir)
                    dir = vector.multiply(dir, (self._pmb_speed or 1) * 1.7)
                    dir.y = self.object:get_velocity().y
                    self.object:set_velocity(dir)
                end

                pmb_entity_api.check_jump(self, 2)

                if self._pmb_since_attack >= 1
                and self._pmb_target and self._pmb_substate == "charge" then
                    local dist = pmb_entity_api.get_target_dist(self)
                    if dist and dist < 2.5 then
                        self._pmb_target:punch(self.object, 1.0, {
                            full_punch_interval = 1.0,
                            damage_groups = {
                                blunt=4,
                            }
                        }, nil)
                        self._pmb_since_attack = 0
                    end
                end

                if self._pmb_substate == "charge"
                and self._pmb_charge_time > (1 / animframes) * 51 then
                    self._pmb_substate = nil
                    -- stop moving
                    local vel = self.object:get_velocity()
                    vel.x = 0
                    vel.z = 0
                    self.object:set_velocity(vel)
                end
                if self._pmb_substate == nil then
                    pmb_entity_api.decelerate(self, 0.98)
                end

                self._pmb_charge_time = self._pmb_charge_time + dtime
                if self._pmb_charge_time >= (1 / animframes) * 80 then
                    self._pmb_charge_time = nil
                    return "follow"
                end
            end,
        },
        smash = {
            animation = "smash",
            step = function(self, dtime, moveresult)
                if not self._pmb_target then
                    pmb_entity_api.get_target(self)
                end

                pmb_entity_api.rotate_to_target(self, dtime*6)

                if not self._pmb_smash_time then
                    pmb_elemental.stone.do_sound(self, "windup_start")
                    self._pmb_substate = "windup"
                    self._pmb_smash_time = 0
                end
                self._pmb_smash_time = self._pmb_smash_time + dtime

                pmb_entity_api.decelerate(self, 0.96)

                if self._pmb_substate == "windup"
                and self._pmb_smash_time > (1 / pmb_entity_api.get_current_animation_length(self)) * 17 then
                    pmb_elemental.stone.do_sound(self, "smash")
                    pmb_elemental.stone.do_sound(self, "windup")
                    local hitpos = vector.add(self.object:get_pos(), vector.multiply(minetest.yaw_to_dir(self.object:get_yaw()), 3))
                    pmb_elemental.smash_smoke_puff(hitpos, 30, 1)
                    local dist = pmb_entity_api.get_target_dist(self)
                    if dist and dist < 5 then
                        self._pmb_target:punch(self.object, 1.0, {
                            full_punch_interval = 1.0,
                            damage_groups = {
                                blunt=2,
                            }
                        }, nil)
                    end
                    self._pmb_substate = nil
                end

                if self._pmb_smash_time >= (1 / pmb_entity_api.get_current_animation_length(self)) * 34 then
                    self._pmb_smash_time = nil
                    return "follow"
                end
            end,
        },
        death = {
            animation = "death",
            on_state_start = function(self)
                self._pmb_detectable = false
                self.object:set_properties({
                    pointable = false,
                })
                pmb_entity_api.rotate_to_target(self, 1)
                self.object:set_hp(20)
                self.object:set_armor_groups({fleshy=0})
            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,
        },
    },
    on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
        if damage and damage > 0 then
            if math.random() < 0.5
            and self._states.defend.fromstates[self._state] == 1 then
                pmb_entity_api.set_state(self, "defend")
                return true
            elseif puncher and (puncher ~= self.object) and (not self._pmb_target) then
                self._pmb_target = puncher
                pmb_entity_api.set_state(self, "follow")
            end
            pmb_elemental.stone.do_sound(self, "hurt_"..(math.random(0,2)))

            local hp = self.object:get_hp() - damage
            if hp <= 0 then
                pmb_entity_api.set_state(self, "death")
                return true
            end
        end
        return pmb_entity_api.damage.on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
    end,

    _is_blocking = function(self, atk)
        if math.random() < 0.8
        and self._states.defend.fromstates[self._state] == 1 then
            pmb_entity_api.set_state(self, "defend")
            return true
        end
    end,

    _on_attack_received = function(itemstack, self, atk)
        atk.knockback_mult = (atk.knockback_mult or 1) * 0.1
        atk.multiplier.cut = (atk.multiplier.cut or 1) * 0.1
        minetest.log(dump(atk))
    end,

    _pmb_speed = 4,
    _pmb_acceleration = 10,
    _pmb_max_health = 30,
    _pmb_gravity = 6,
    _pmb_hostile = {player=1},
    _animations = {
        idle = {frames={x=0, y=49}, blend=0.2},
        follow = {frames={x=50, y=109}, blend=0.2},
        smash = {frames={x=110, y=144}, blend=0.2, loop = false},
        charge = {frames={x=150, y=223}, blend=0.2, loop = false},
        defend = {frames={x=230, y=249}, blend=0.2, loop = false},
        shoulder_smash = {frames={x=250, y=279}, blend=0.2, loop = false},
        death = {frames={x=280, y=299}, blend=0.2, loop = false},
    },
    _default_state = "idle",
    _pmb_wander = 3,
    _pmb_range = 20,
    _pmb_is_mob = true,
    _age = 0,
    _pmb_since_attack = 0,
    _knockback = 0.1,
    on_activate = function(self, staticdata, dtime_s)
        self.object:set_armor_groups({
            pierce=70,
            slash=0,
            blunt=150,
            magic=200,
            poison=0,
        })
        pmb_entity_api.on_activate(self, staticdata, dtime_s)
    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,
    _pmb_statusfx_enable = true,
})

minetest.register_craftitem("pmb_elementals:stone_spawn",
{
    description = "Stone Elemental spawn egg",
    inventory_image = "pmb_elemental_stone.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_elementals:stone")
    end,
})
