
local _gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81
local _movement_speed = tonumber(minetest.settings:get("movement_speed_walk")) or 4.0
local _movement_jump = tonumber(minetest.settings:get("movement_speed_jump")) or 6.5

local function friction_fix_check_has_obj(self, player, pi)
    if (pi._ff_obj == nil) or (pi._ff_obj:get_pos() == nil) then
        pi._ff_obj = nil
        return false
    end
    return true
end

aom_vehicleapi.friction_fix = {
    initial_properties = {
        visual = "cube",
        textures = {"blank.png", "blank.png", "blank.png", "blank.png", "blank.png", "blank.png"},
        -- textures = {"white.png", "white.png", "white.png", "white.png", "white.png", "white.png"},
        use_texture_alpha = true,
        stepheight = 0,
        physical = false,
        collide_with_objects = false,
        pointable = false,
        static_save = false,
    },
    _get_real_target_pos = function(self)
        local parent_yaw = aom_vehicleapi.get_actual_yaw(self._parent)
        local parent_pos = self._parent.object:get_pos()
        local target_pos = aom_vehicleapi.rotate_point(self._rpos, parent_yaw) + parent_pos
        return target_pos
    end,
    on_step = function(self, dtime)
        if (not self._parent) or (self._parent.object:get_pos() == nil) then self.object:remove(); return end
        local player = minetest.is_player(self._player) and self._player
        if (not player) or (player:get_pos() == nil) then self.object:remove(); return end
        if player:get_attach() ~= self.object then self.object:remove(); return end

        local min_y = aom_vehicleapi.get_platform_y(self._parent, player, self._parent._aomv_pl[player:get_player_name()])
        local plphys = player:get_physics_override()
        local gravity = _gravity * plphys.gravity
        local speed = _movement_speed * plphys.speed
        local look_yaw = player:get_look_horizontal()
        local parent_yaw = aom_vehicleapi.get_actual_yaw(self._parent)
        local friction = 0.98

        self.object:set_yaw(look_yaw)

        -- movement
        local ctrl = player:get_player_control()
        local movedir = vector.new(0, 0, 0)
        movedir.z = (ctrl.up and 1 or 0) + (ctrl.down and -1 or 0)
        movedir.x = (ctrl.right and 1 or 0) + (ctrl.left and -1 or 0)

        -- make it relative to look direction
        movedir = vector.rotate_around_axis(movedir, vector.new(0,1,0), look_yaw - parent_yaw)
        movedir = movedir * speed

        self._rvel.x = self._rvel.x * (1 - friction)
        self._rvel.z = self._rvel.z * (1 - friction)

        self._rvel.y = self._rvel.y - gravity * dtime * 2
        self._rvel = self._rvel + (movedir * friction)

        if self._jump_cd and self._jump_cd > 0 then
            self._jump_cd = self._jump_cd - dtime
        end
        if self.is_on_floor and ctrl.jump and (self._jump_cd or -1) < 0 then
            self._jump_cd = 0.3
            self._rvel.y = _movement_jump * plphys.jump
            self.is_on_floor = false
        end

        -- handle relative pos and vel
        self._rpos = self._rpos + (self._rvel * dtime)
        self.is_on_floor = (self._rpos.y <= min_y + 0.001)
        self._rpos.y = math.max(min_y, self._rpos.y)

        local parent_pos = self._parent.object:get_pos()
        local target_pos = aom_vehicleapi.rotate_point(self._rpos, parent_yaw) + parent_pos

        if self.is_on_floor and self._rvel.y < 0 then self._rvel.y = 0 end
        -- self._last_vel = aom_vehicleapi.rotate_point(self._rvel, parent_yaw) + self._parent.object:get_velocity()
        self.object:move_to(target_pos)
    end,
    on_activate = function(self, staticdata, dtime_s)
        aom_vehicleapi.on_activate(self, staticdata, dtime_s)
    end,
    on_deactivate = function(self, removal)
    end,
    _aom_statusfx_enable = false,
    _no_player_animation = true,
}

minetest.register_entity("aom_vehicleapi:friction_fix_ENTITY", aom_vehicleapi.friction_fix)


-- add the fake player controller and attach player to it
function aom_vehicleapi.friction_fix_start(self, player, pi)
    if player:get_attach() ~= nil then
        return
    end
    friction_fix_check_has_obj(self, player, pi)
    if pi._ff_obj then return end
    local pos = player:get_pos()
    local parent_pos = self.object:get_pos()
    local vel = player:get_velocity()
    local rpos = pos - parent_pos
    local obj = minetest.add_entity(pos, "aom_vehicleapi:friction_fix_ENTITY")
    local ent = obj and obj:get_luaentity()
    if not ent then return end
    pi._ff_obj = obj
    player:set_attach(obj)

    local parent_yaw = aom_vehicleapi.get_actual_yaw(self)

    obj:set_velocity(vel)
    ent._rvel = aom_vehicleapi.rotate_point(vel, -parent_yaw)
    ent._rpos = aom_vehicleapi.rotate_point(rpos, -parent_yaw)
    ent._player = player
    ent._parent = self
end

function aom_vehicleapi.friction_fix_step(self, player, pi, dtime)
    local has_obj = friction_fix_check_has_obj(self, player, pi)
    local vel = self.object:get_velocity()
    local len = vector.length(vel)
    -- use a larger velocity threshold so it doesn't jitter back and forth
    if len < 0.5 then
        if not self._aomv_platforms then
            aom_vehicleapi.do_player_push_up(self, dtime)
            aom_vehicleapi.platform_under_player(self, player, pi)
        end
        return -- skip using fake player / friction fix unless going fast
    end

    if (pi.time_last_leave ~= nil) and (pi.time_last_leave < 0.5) then
        return
    end
    -- if the ship is very slow, use platforms instead so it's not gross and laggy
    if len < 0.2 and has_obj and (not self._aomv_platforms) then
        aom_vehicleapi.friction_fix_end(self, player, pi)
    end

    if player:get_attach() == nil then
        aom_vehicleapi.friction_fix_start(self, player, pi)
    end
end

-- remove the fake player controller, and clean up
function aom_vehicleapi.friction_fix_end(self, player, pi)
    if not friction_fix_check_has_obj(self, player, pi) then
        return
    end
    local ent = pi._ff_obj:get_luaentity()
    local vel = aom_vehicleapi.rotate_point(ent._rvel, aom_vehicleapi.get_actual_yaw(self)) + self.object:get_velocity()
    player:set_detach()
    vel = vel - player:get_velocity()
    player:add_velocity(vel)
    pi._ff_obj:remove()
    pi._ff_obj = nil
end


-- LISTEN("aomv_on_enter_vehicle_poly", aom_vehicleapi.friction_fix_start)
LISTEN("aomv_on_stand_vehicle_poly", aom_vehicleapi.friction_fix_step)
LISTEN("aomv_on_leave_vehicle_poly", aom_vehicleapi.friction_fix_end)
