local function get_nearby_player(pos)
    for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 200)) do
        if obj:is_player() then return obj end
    end
end

local function meteorite_explode(pos, type)
    --breaking nodes
    sbz_api.explode(pos, 8, 0.9, false, '.meteorite')
    --placing nodes
    local protected = minetest.is_protected(pos, '.meteorite')
    if not protected and is_air(pos) then
        minetest.set_node(
            pos,
            { name = type == 'antimatter_blob' and 'sbz_meteorites:antineutronium' or 'sbz_meteorites:neutronium' }
        )
    end

    local node_types = {
        matter_blob = { 'sbz_meteorites:meteoric_matter', 'sbz_meteorites:meteoric_metal' },
        emitter = { 'sbz_meteorites:meteoric_emittrium', 'sbz_meteorites:meteoric_metal' },
        antimatter_blob = { 'sbz_meteorites:meteoric_antimatter', 'sbz_meteorites:meteoric_antimatter' },
        strange_blob = { 'sbz_resources:strange_blob', 'sbz_resources:strange_blob' },
    }
    if not protected then
        for _ = 1, 16 do
            local new_pos = pos + vector.new(math.random(-1, 1), math.random(-1, 1), math.random(-1, 1))
            if is_air(new_pos) then
                minetest.set_node(new_pos, {
                    name = math.random() < 0.2 and node_types[type][2] or node_types[type][1],
                })
            end
        end
    end
    --knockback
    for _, obj in ipairs(minetest.get_objects_inside_radius(pos, 16)) do
        if obj:is_player() then
            local dir = obj:get_pos() - pos
            obj:add_velocity((vector.normalize(dir) + vector.new(0, 0.5, 0)) * 0.5 * (16 - vector.length(dir)))
        end
    end
    --particle effects
    minetest.add_particlespawner {
        time = 0.1,
        amount = 2000 / (protected and 5 or 1),
        pos = pos,
        radius = 1,
        drag = 0.2,
        glow = 14,
        exptime = { min = 2, max = 5 },
        size = { min = 3, max = 6 },
        texture = 'meteorite_trail_' .. type .. '.png',
        animation = { type = 'vertical_frames', aspect_width = 4, aspect_height = 4, length = -1 },
        attract = {
            kind = 'point',
            origin = pos,
            strength = { min = -4, max = 0 },
        },
    }
    local forward = vector.new(1, 0, 0)
    local up = vector.new(0, 1, 0)

    for _ = 1, 500 / (protected and 5 or 1) do
        local dir = vector.rotate_around_axis(forward, up, math.random() * 2 * math.pi)
        local expiry = math.random() * 3 + 2
        minetest.add_particle {
            pos = pos + dir,
            velocity = dir * (5 + math.random()),
            drag = vector.new(0.2, 0.2, 0.2),
            glow = 14,
            expirationtime = expiry,
            size = math.random() * 3 + 3,
            texture = 'meteorite_trail_' .. type .. '.png^[colorize:#aaaaaa:alpha',
            animation = { type = 'vertical_frames', aspect_width = 4, aspect_height = 4, length = expiry },
        }
    end
end

sbz_api.meteorite_explode = meteorite_explode

minetest.register_entity('sbz_meteorites:meteorite', {
    initial_properties = {
        visual = 'cube',
        visual_size = { x = 2, y = 2 },
        automatic_rotate = 0.2,
        glow = 14,
        selectionbox = { -1, -1, -1, 1, 1, 1 },
        physical = false, --so they enter unloaded chunks properly
    },
    on_activate = function(self, staticdata, dtime)
        -- the "dtime" variable name is a lie in this case, its meant to be "time that it was unloaded" i believe?
        if dtime and dtime > 600 then
            self.object:remove()
            return
        end

        self.object:set_rotation(vector.new(math.random() * 2, math.random(), math.random() * 2) * math.pi)

        if staticdata and staticdata ~= '' then --not new, just unpack staticdata
            self.type = staticdata
        else --new entity, initialise stuff
            local types = { 'matter_blob', 'emitter', 'antimatter_blob', 'strange_blob' }
            local pos = self.object:get_pos()

            if vector.in_area(pos, vector.new(-100, -100, -100), vector.new(100, 100, 100)) then
                types[4] = nil -- dont want strange meteorites spawning to some noob who wont know what to do
            end

            self.type = types[math.random(#types)]
            local offset = vector.new(math.random(-48, 48), math.random(-48, 48), math.random(-48, 48))
            local pos = self.object:get_pos()
            local target = get_nearby_player(pos)

            if not target then
                self.object:remove()
                return
            end
            self.object:set_velocity(1.5 * vector.normalize(target:get_pos() - pos + offset))
        end

        local texture = self.type .. '.png^meteorite.png'

        self.object:set_properties { textures = { texture, texture, texture, texture, texture, texture } }
        self.object:set_armor_groups { immortal = 1 }
        self.sound = minetest.sound_play(
            { name = 'rocket-loop-99748', gain = 0.15, fade = 0.1 },
            { object = self.object, max_hear_distance = 100, loop = true }
        )
        self.waypoint = nil
        self.time_since = 100
    end,
    on_deactivate = function(self)
        if not self.type then return end
        if self.sound then minetest.sound_fade(self.sound, 0.1, 0) end
        if self.waypoint then sbz_api.remove_waypoint(self.waypoint) end
    end,
    get_staticdata = function(self)
        return self.type
    end,
    on_step = function(self, dtime)
        if not self.type then return end
        local pos = self.object:get_pos()
        local diag = vector.new(1, 1, 1)
        for x = -1, 1 do
            for y = -1, 1 do
                for z = -1, 1 do
                    local node = minetest.get_node(pos + vector.new(x, y, z)).name
                    if node ~= 'ignore' and node ~= 'air' and node ~= 'sbz_power:funny_air' then --colliding with something, should explode
                        meteorite_explode(pos, self.type)
                        minetest.sound_play(
                            { name = 'distant-explosion-47562', gain = 0.4 },
                            { pos = self.object:get_pos(), max_hear_distance = 300 }
                        )
                        self.object:remove()
                        return
                    end
                end
            end
        end

        --the stopping moving bug seems to be it hitting unloaded chunks
        minetest.add_particlespawner {
            time = dtime,
            amount = 1,
            pos = { min = pos - diag, max = pos + diag },
            vel = { min = -0.5 * diag, max = 0.5 * diag },
            drag = 0.2,
            glow = 14,
            exptime = { min = 10, max = 20 },
            size = { min = 2, max = 4 },
            texture = 'meteorite_trail_' .. self.type .. '.png',
            animation = { type = 'vertical_frames', aspect_width = 4, aspect_height = 4, length = -1 },
        }
        self.time_since = (self.time_since or 0) + dtime
        if self.waypoint and self.time_since >= 2 then
            sbz_api.remove_waypoint(self.waypoint)
            self.waypoint = nil
        end
        sbz_api.move_waypoint(self.waypoint, pos)
    end,
    show_waypoint = function(self)
        if not self.waypoint then
            self.waypoint = sbz_api.set_waypoint(self.object:get_pos(), {
                name = '',
                dist = 10,
                image = 'visualiser_trail.png^[verticalframe:3:0',
            })
        end
        self.time_since = 0
    end,
})

--- I think player checking if its colliding would be a lot better than all meteorites checking if a player is colliding
local function meteorite_player_collision(dtime)
    for _, ref in pairs(core.get_connected_players()) do
        for obj in core.objects_inside_radius(ref:get_pos(), 1) do
            local lt = obj:get_luaentity()
            if lt and lt.name == 'sbz_meteorites:meteorite' then
                meteorite_explode(obj:get_pos(), lt.type)
                minetest.sound_play(
                    { name = 'distant-explosion-47562', gain = 0.4 },
                    { pos = obj:get_pos(), max_hear_distance = 300 }
                )
                obj:remove()
            end
        end
    end
end

core.register_globalstep(meteorite_player_collision)
