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

function aom_elemental.send_attack(self, target, base_damage)
    if minetest.get_modpath("aom_combat") then
        aom_combat.attack.send_attack(nil, self.object, target, {attack={
            base_damage = base_damage
        }})
    else
        target:punch(self.object, 1.0, {
            full_punch_interval = 1.0,
            damage_groups = base_damage
        }, nil)
    end
end

minetest.register_entity("aom_elementals:stone", {
    initial_properties = {
        visual = "mesh",
        mesh = "aom_elemental.b3d",
        textures = {"aom_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,
        static_save = false,
    },
    _name = "aom_elementals:stone",


    on_step = aom_entity_api.do_states(),


    _drop = {
        max_items = 5,
        items = {
            {
                rarity = 3,
                items = {"aom_items:copper_nugget"},
            },
            {
                rarity = 2,
                items = {"aom_elementals:elemental_core_stone"},
            },
            {
                rarity = 3,
                items = {"aom_items:tin_nugget"},
            },
            {
                rarity = 2,
                items = {"aom_stone:cobble"},
            },
            {
                rarity = 2,
                items = {"aom_stone:cobble 2"},
            },
        }
    },
    on_death = function(self, killer)
        if killer then
            aom_entity_api.do_drops(self)
        end
    end,

    _states = {
        on_step = function (self, dtime, moveresult)
            if self._state ~= "death" then
                local in_or_above_water = aom_entity_api.float_in_liquids(self, dtime * 20, {offset=vector.new(0, 1.2, 0)})
                if not in_or_above_water then
                    aom_entity_api.apply_gravity(self, dtime)
                end
                aom_entity_api.decelerate(self, 0.98)
            end
        end,
        idle = {
            animation = "idle",
            step = function(self, dtime, moveresult)
                if not self._aom_idle_time then self._aom_idle_time = 0
                else self._aom_idle_time = self._aom_idle_time + dtime end
                aom_entity_api.get_target(self)
                aom_entity_api.get_wander(self, 60)
                aom_entity_api.decelerate(self, 0.98)
                if self._aom_to_pos and self._aom_idle_time > 1 then
                    self._aom_idle_time = nil
                    return "follow"
                end
            end,
        },
        defend = {
            animation = "defend",
            fromstates = {idle=1, follow=1},
            on_state_start = function(self)
                self._aom_defend_time = 0
            end,
            step = function(self, dtime, moveresult)
                self._aom_defend_time = self._aom_defend_time + dtime
                aom_entity_api.get_target(self)
                aom_entity_api.decelerate(self, 0.98)
                if self._aom_defend_time > (1 / aom_entity_api.get_current_animation_length(self))*19 then
                    self._aom_defend_time = nil
                    return "follow"
                end
            end,
        },
        shoulder_smash = {
            animation = "shoulder_smash",
            fromstates = {idle=1, follow=1},
            on_state_start = function(self)
                self._aom_substate = "windup"
                aom_elemental.stone.do_sound(self, "charge_start")
            end,
            step = function(self, dtime, moveresult)
                aom_entity_api.get_target(self)
                aom_entity_api.decelerate(self, 0.98)
                aom_entity_api.rotate_to_target(self, dtime*12)
                if self._aom_substate == "windup"
                and self._aom_state_time > (1 / aom_entity_api.get_current_animation_length(self))*10 then
                    self._aom_substate = nil
                    local dist = aom_entity_api.get_target_dist(self)
                    if dist and dist < 4 then
                        aom_elemental.send_attack(self, self._aom_target, {
                            blunt=4,
                        })
                        self._aom_since_attack = 0
                        aom_elemental.stone.do_sound(self, "hurt_1")
                    end
                end
                if self._aom_state_time > (1 / aom_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 = aom_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
                    aom_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
                    aom_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._aom_attack_cooldown then self._aom_attack_cooldown = 1 end
                if self._aom_attack_cooldown > 0 then self._aom_attack_cooldown = self._aom_attack_cooldown - dtime end

                if self._aom_target and not self._aom_to_pos then
                    self._aom_to_pos = self._aom_target:get_pos()
                    self._aom_path = {self._aom_to_pos}
                end
                if not self._aom_target and self._aom_state_time > 4 then
                    aom_entity_api.get_target(self)
                end

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

                local dist = aom_entity_api.get_target_dist(self) or 1000

                aom_entity_api.rotate_to_path(self, dtime*8)

                if (not self._aom_to_pos) and (not self._aom_target) then return "idle" end

                if (dist > 50) or self._aom_state_time > 1 then
                    aom_entity_api.get_target(self)
                end

                if self._aom_target and self._aom_to_pos and self._aom_attack_cooldown <= 0
                and aom_entity_api.has_los_to_object(self, self._aom_target) then
                    if dist < 5 then
                        if math.random() < 0.5 then
                            self._aom_attack_cooldown = nil
                            self._walk_frame = nil
                            return "smash"
                        else
                            self._aom_attack_cooldown = nil
                            self._walk_frame = nil
                            return "shoulder_smash"
                        end
                    elseif math.random() < 0.2
                    and dist < 15 then
                        aom_entity_api.do_path(self, 0)
                        self._aom_attack_cooldown = nil
                        self._walk_frame = nil
                        return "charge"
                    else
                        self._aom_attack_cooldown = 1
                    end
                end
            end,
        },
        charge = {
            animation = "charge",
            step = function(self, dtime, moveresult)
                if not self._aom_charge_time then
                    self._aom_charge_time = 0
                    aom_entity_api.get_target(self)
                    aom_entity_api.rotate_to_target(self, 1)
                    self._aom_substate = "start"
                    self._aom_since_attack = 1
                    aom_elemental.stone.do_sound(self, "charge_start")
                end
                if self._aom_substate == "start" then
                    aom_entity_api.decelerate(self, 0.98)
                end

                local animframes = aom_entity_api.get_current_animation_length(self)

                if self._aom_substate == "start"
                and self._aom_charge_time > (1 / animframes) * 13 then
                    self._aom_substate = "charge"
                    aom_elemental.stone.do_sound(self, "charge")
                    aom_elemental.stone.do_sound(self, "steps", 0.2)

                    local tp = (self._aom_target and self._aom_target:get_pos())
                    if tp then
                        tp = tp + (self._aom_target:get_velocity() * 0.5)
                        aom_entity_api.rotate_to_pos(self, tp, 1, false)
                    else
                        aom_entity_api.rotate_to_target(self, 1, false)
                    end
                end
                if self._aom_substate == "charge"
                and (self._aom_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._aom_speed or 1) * 1.7)
                    dir.y = self.object:get_velocity().y
                    self.object:set_velocity(dir)
                end

                aom_entity_api.check_jump(self, 2)

                if self._aom_since_attack >= 1
                and self._aom_target and self._aom_substate == "charge" then
                    local dist = aom_entity_api.get_target_dist(self)
                    if dist and dist < 2.5 then
                        aom_elemental.send_attack(self, self._aom_target, {
                            blunt=4,
                        })
                        self._aom_since_attack = 0
                    end
                end

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

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

                aom_entity_api.rotate_to_target(self, dtime*6)

                if not self._aom_smash_time then
                    aom_elemental.stone.do_sound(self, "windup_start")
                    self._aom_substate = "windup"
                    self._aom_smash_time = 0
                end
                self._aom_smash_time = self._aom_smash_time + dtime

                aom_entity_api.decelerate(self, 0.96)

                if self._aom_substate == "windup"
                and self._aom_smash_time > 18 * (1 / aom_entity_api.get_current_animation_length(self)) then
                    aom_elemental.stone.do_sound(self, "smash")
                    aom_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))
                    aom_elemental.smash_smoke_puff(hitpos, 30, 1)
                    local dist = aom_entity_api.get_target_dist(self)
                    if dist and dist < 5 then
                        aom_elemental.send_attack(self, self._aom_target, {
                            blunt=3,
                        })
                    end
                    self._aom_substate = nil
                end

                if self._aom_smash_time >= (1 / aom_entity_api.get_current_animation_length(self)) * 34 then
                    self._aom_smash_time = nil
                    return "follow"
                end
            end,
        },
        death = {
            animation = "death",
            on_state_start = function(self)
                self._aom_detectable = false
                self.object:set_properties({
                    pointable = false,
                })
                aom_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)
                aom_entity_api.decelerate(self, 0.2)
                local vel = self.object:get_velocity()
                vel.y = vel.y - self._aom_gravity * dtime
                self.object:set_velocity(vel)
                if self._aom_state_time > 2 then
                    aom_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 puncher and (puncher ~= self.object) and (not self._aom_target) then
                self._aom_target = puncher
                aom_entity_api.set_state(self, "follow")
            end
            aom_elemental.stone.do_sound(self, "hurt_"..(math.random(0,2)))

            local hp = self.object:get_hp() - damage
            if hp <= 0 then
                aom_entity_api.set_state(self, "death")
                return true
            end
        end
        return aom_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.6
        and self._states.defend.fromstates[self._state] == 1 then
            aom_entity_api.set_state(self, "defend")
            return true
        end
    end,

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

    _aom_speed = 4,
    _aom_acceleration = 10,
    _aom_max_health = 30,
    _aom_gravity = 6,
    _aom_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",
    _aom_wander = 3,
    _aom_range = 20,
    _aom_is_mob = true,
    _age = 0,
    _aom_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,
        })
        aom_entity_api.on_activate(self, staticdata, dtime_s)
    end,
    get_staticdata = function(self)
        return aom_entity_api.get_staticdata(self)
    end,
    on_deactivate = function(self, removal)
        aom_entity_api.on_deactivate(self, removal)
    end,
    _aom_statusfx_enable = true,
})

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