local textures_b = {
    "amogus_entity.png",
    "amogus_entity_b.png",
    "amogus_entity_g.png",
    "amogus_entity_br.png"
}

local entity = {
    physical = true,
    collisionbox = {-0.25, 0, -0.25, 0.25, 0.5, 0.25},

    visual = "mesh",
    visual_size = {x=5, y=5, z=5},

    mesh = "amogus_entity.obj",

    on_rightclick = function(self, clicker)
        minetest.chat_send_player(clicker:get_player_name(), "SO SUSSY!")

        --minetest.sound_play("amogus_sound", {
        --    pos = self.object:get_pos(),
        --    gain = 1.0,
        --    max_hear_distance = 5,
        --    pitch = 2
        --})
        amogus_general.play_random_sound(
            "amogus_sound",
            self.object:get_pos(),
            1.0, -- gain
            40, -- max_hear_distance
            2 -- pitch
        )
    end,

    -- mini crewmate config --
    walk_acceleration_speed = 0.05, -- acceleration speed when no panic, no stress
    run_acceleration_speed = 0.2, -- acceleration speed when chasing player
    run_away_acceleration_speed = 0.4, -- acceleration speed when running away from player

    rotation_acceleration_speed = 0.5, -- self explainatory

    gravity = 9.81, -- m/s^2

    jump_force = 10, -- self explainatory

    friction = 0.8, -- friction (0.0 - no friction like perfectly smooth ice | 1.0 - full friction and can't even move)
    rotation_friction = 0.9, -- the same but for rotation


    largest_distance_to_find_player = 8, -- largest distance to find player
    smallest_distance_to_lost_player = 12, -- smallest distance to lost player

    largest_distance_to_stand_still = 3, -- largest distance to stand still next to the player

    smallest_distance_to_stop_run_away = 30, -- smallest distance to stop run away from player
    --------------------------

    block_lastly_in_front = false,
    rotation_velocity = 0,
    rotation_direction = nil,

    sound_pitch = nil,
    sound_propability = nil,

    -- player detection variables

    player = nil,
    player_pos = nil,
    mini_crewmate_pos = nil,
    crewmate_player_vector = nil,
    distance = nil,
    current_distance = nil,
    current_crewmate_player_vector = nil,
    
    


    mode = 0, -- | 0 - stand still | 1 - walk | 2 - chase | 3 - run away | 4 - rest mode |

    on_activate = function(self, staticdata)
        local texture = textures_b[math.random(#textures_b)]
		self.object:set_properties({textures = {texture}})

        self.object:set_yaw(math.random() * 2 * math.pi)
        self.rotation_direction = math.random(-1, 1)
    end,

    on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
        self.mode = 3
    end,

    on_step = function(self, dtime)

        -- play amogus sound randomly with different pitch depending on situation

        if self.mode == 0 or self.mode == 1 then -- stand still or walk
            sound_pitch = 1.8
            sound_propability = 100

        elseif self.mode == 2 then -- chase
            sound_pitch = 2.5
            sound_propability = 50

        elseif self.mode == 3 then -- run away
            sound_pitch = 3
            sound_propability = 10
        
        else -- rest mode (4)
            sound_pitch = 2
            sound_propability = 300
        end
        

        if math.random(sound_propability) == 1 then
            --minetest.sound_play("amogus_sound", {
            --    pos = self.object:get_pos(),
            --    gain = 1.0,
            --    max_hear_distance = 50,
            --    pitch = sound_pitch
            --})

            amogus_general.play_random_sound(
                "amogus_sound",
                self.object:get_pos(),
                1.0, -- gain
                40, -- max_hear_distance
                sound_pitch -- pitch
            )
        end

        

        -- if on singpleplayer mode or only one player is connected, calculate distance between player and mini crewmate
        if minetest.is_singleplayer() or #minetest.get_connected_players() == 1 then
            -- get first player
            self.player = minetest.get_connected_players()[1]
            
            self.player_pos = self.player:get_pos()
            self.mini_crewmate_pos = self.object:get_pos()
            self.crewmate_player_vector = vector.subtract(self.player_pos, self.mini_crewmate_pos)
            self.distance = vector.length(self.crewmate_player_vector)
        -- if on multiplayer mode, calculate distance between nearest player and mini crewmate
        else
            self.player = minetest.get_connected_players()

            self.distance = nil
            self.crewmate_player_vector = nil

            self.mini_crewmate_pos = self.object:get_pos()
            
            -- iterate over all players
            for i = 1, #self.player do
                minetest.chat_send_all("player " .. i)
                self.player_pos = self.player[i]:get_pos()
                --self.crewmate_player_vector = vector.subtract(self.player_pos, self.mini_crewmate_pos)
                self.current_crewmate_player_vector = vector.subtract(self.player_pos, self.mini_crewmate_pos)
                self.current_distance = vector.length(self.current_crewmate_player_vector)

                -- if distance is nil or current_distance is smaller than distance, set distance to current_distance
                if self.distance == nil or self.current_distance < self.distance then
                    self.distance = self.current_distance
                    self.crewmate_player_vector = self.current_crewmate_player_vector
                end
            end
        end

        
        -- if mode is 0 or 1 (stand still or walk) and distance is smaller or equal to largest_distance_to_find_player, set mode to 2 (chase)
        if (self.mode == 0 or self.mode == 1) and self.distance <= self.largest_distance_to_find_player then
            self.mode = 2
        -- elseif mode is 2 (chase)
        elseif self.mode == 2 then
            -- if distance is greater or equal to smallest_distance_to_lost_player, set mode to 0 (stand still)
            if self.distance >= self.smallest_distance_to_lost_player then
                self.mode = 0
            -- elif distance is smaller or equal to largest_distance_to_stand_still, set mode to 4 (rest mode)
            elseif self.distance <= self.largest_distance_to_stand_still then
                self.mode = 4
            end
        -- if mode is 4 (rest mode) and distance is greater than largest_distance_to_stand_still, set mode to 2 (chase)
        elseif self.mode == 4 and self.distance > self.largest_distance_to_stand_still then
            self.mode = 2
        -- running away mode
        elseif self.mode == 3 then
            -- if distance is greater than smallest_distance_to_lost_player, set mode to 0 (stand still)
            if self.distance > self.smallest_distance_to_stop_run_away then
                self.mode = 1
            else
                -- jump if on ground
                local pos = self.object:get_pos()
                pos.y = pos.y - 0.2
                local bnode = minetest.get_node(pos)
                if bnode.name ~= "air" then
                    local vel = self.object:get_velocity()
                    vel.y = vel.y + self.jump_force
                    self.object:set_velocity(vel)
                end
            end
        end


        -- if if not performing an action related to the player
        if (self.mode == 0 or self.mode == 1) then

            -- make mini crewmate sometimes stand still and sometimes walk
            if math.random(100) == 1 then
                if self.mode == 0 then
                    self.mode = 1
                else
                    self.mode = 0
                end
            end

            -- some chance of chaning rotation_direction to random value
            if math.random(10) == 1 then
                --self.rotation_direction = math.random(-1, 1)
                local rand = math.random()
                
                if rand < 0.2 then
                    self.rotation_direction = 1
                elseif rand < 0.4 then
                    self.rotation_direction = -1
                else
                    self.rotation_direction = 0
                end
            end
            
            -- update rotation_velocity
            self.rotation_velocity = self.rotation_velocity + self.rotation_direction * self.rotation_acceleration_speed * dtime
            
            -- update rotation
            self.object:set_yaw(self.object:get_yaw() + self.rotation_velocity * dtime)
            
            -- apply rotation_friction
            self.rotation_velocity = self.rotation_velocity * (1 - self.rotation_friction * dtime)

        else -- else performing an action related to the player
            self.rotation_velocity = 0;
            
            -- if mode is 3 (run away) set rotation so mini crewmate is facing either at the player or away from the player depending on situation
            if self.mode == 3 then
                -- look away from the player
                self.object:set_yaw(math.atan2(-self.crewmate_player_vector.z, -self.crewmate_player_vector.x))
            else
                -- look at the player
                self.object:set_yaw(math.atan2(self.crewmate_player_vector.z, self.crewmate_player_vector.x))
            end
        end

        -- perform calculations on direction
        local dir = self.object:get_yaw()
        local dir_cos = math.cos(dir)
        local dir_sin = math.sin(dir)

        -- if not standing still and not in rest mode
        if self.mode ~= 0 and self.mode ~= 4 then
            -- make mini crewmate walk or run
            local vel = self.object:get_velocity()
            -- if walking
            if self.mode == 1 then
                vel.x = vel.x + dir_cos * self.walk_acceleration_speed
                vel.z = vel.z + dir_sin * self.walk_acceleration_speed
            -- elif chasing
            elseif self.mode == 2 then
                vel.x = vel.x + dir_cos * self.run_acceleration_speed
                vel.z = vel.z + dir_sin * self.run_acceleration_speed
            else -- else running away (3)
                vel.x = vel.x + dir_cos * self.run_away_acceleration_speed
                vel.z = vel.z + dir_sin * self.run_away_acceleration_speed
            end

            self.object:set_velocity(vel)
        end



        -- Make it also jump when some block is in front of it
        local pos = self.object:get_pos()

        pos.x = pos.x + dir_cos
        pos.z = pos.z + dir_sin
        
        local bnode = minetest.get_node(pos)
        if bnode.name == "air" or bnode.name == "amogus_blocks:water_source" or bnode.name == "amogus_blocks:water_flowing" then
            self.block_lastly_in_front = false
        else
            if self.block_lastly_in_front == false then
                self.block_lastly_in_front = true
                local vel = self.object:get_velocity()
                vel.y = vel.y + self.jump_force
                self.object:set_velocity(vel)
            end
        end



        local vel = self.object:get_velocity()

        -- change velocity by gravity
        vel.y = vel.y - self.gravity * dtime * 2

        -- change velocity by friction
        vel.x = vel.x * (1 - self.friction * dtime)
        vel.z = vel.z * (1 - self.friction * dtime)

        self.object:set_velocity(vel)
    end

}

minetest.register_entity("amogus_entities:mini_crewmate", entity)