local mod_name = minetest.get_current_modname()
local mod_path = minetest.get_modpath(mod_name)
local S = minetest.get_translator(mod_name)

function aom_vehicleapi.is_in_walkable_polygon(self, ppos)
    local rpos = self.object:get_pos()
    rpos = ppos - rpos
    rpos = aom_vehicleapi.rotate_point(rpos, -aom_vehicleapi.get_actual_yaw(self))
    for i, polydef in pairs(self._aomv_polygon) do
        if polydef.walkable
        and aom_vehicleapi.is_point_inside_polygon(polydef, rpos) then
            return true
        end
    end
    return false
end

function aom_vehicleapi.is_player_on_walkable_polygon(self, player)
    if not minetest.is_player(player) then return false end
    local rpos = self.object:get_pos()
    local ppos = player:get_pos()
    rpos = ppos - rpos
    rpos = aom_vehicleapi.rotate_point(rpos, -aom_vehicleapi.get_actual_yaw(self))
    local pi = self._aomv_pl[player:get_player_name()]
    local in_poly = false
    if pi.last_polydef ~= nil then
        in_poly = aom_vehicleapi.is_point_inside_polygon(pi.last_polydef, rpos)
        if aom_vehicleapi.enable_debug then -- DEBUG display of platforms
            for i, po in pairs(pi.last_polydef.points) do
                local p = vector.new(po[1], 0, po[2])
                p.y = pi.last_polydef.get_platform_y(pi.last_polydef, p)
                p = aom_vehicleapi.rotate_point(p, aom_vehicleapi.get_actual_yaw(self)) + self.object:get_pos()
                aom_vehicleapi.debug_particle(p, "#f00", 0.2, nil)
            end
        end
    end
    if not in_poly then
        pi.last_polydef = nil
        pi.last_polyindex = nil
        for i, polydef in pairs(self._aomv_polygon) do
            if polydef.walkable
            and aom_vehicleapi.is_point_inside_polygon(polydef, rpos) then
                pi.last_polydef = polydef
                pi.last_polyindex = i
                return true
            end
        end
    end
    return in_poly
end

function aom_vehicleapi.get_platform_y(self, player, pi)
    if pi.last_polydef ~= nil then
        local rpos = self.object:get_pos()
        local ppos = player:get_pos()
        rpos = ppos - rpos
        rpos = aom_vehicleapi.rotate_point(rpos, -aom_vehicleapi.get_actual_yaw(self))
        return pi.last_polydef.get_platform_y(pi.last_polydef, rpos)
    end
    return self._aomv_platform_y or 0
end

function aom_vehicleapi.platform_under_player(self, player, pi)
    -- fix for losing platform
    if pi.obj and (not pi.obj:get_luaentity()) then
        pi.obj = nil
    end
    if pi.is_attach then
        if pi.obj ~= nil then pi.obj:remove(); pi.obj = nil end
        return
    end
    if pi.obj == nil then
        local obj, ent = aom_vehicleapi.add_platform_under_player(self, player)
        if obj then pi.obj = obj end
    end
    if not pi.obj then return end
    local pos = player:get_pos()
    local veh_pos = self.object:get_pos()
    pos.y = veh_pos.y + aom_vehicleapi.get_platform_y(self, player, pi)
    pi.obj:move_to(pos, true)
    return pi.obj
end

function aom_vehicleapi.do_player_push_up(self, dtime)
    if self._aomv_no_player_push_up then return end
    for playername, def in pairs(self._aomv_pl) do if def.is_in_poly then repeat
        local player = minetest.get_player_by_name(playername)
        local p = player and player:get_pos()
        if not p then break end
        local pi = self._aomv_pl[player:get_player_name()]
        local veh_pos = self.object:get_pos()
        veh_pos.y = veh_pos.y + aom_vehicleapi.get_platform_y(self, player, pi)
        local is_under = (p.y < veh_pos.y-0.1) and (p.y > veh_pos.y - 2.5)
        if def.is_in_poly and is_under then
            p.y = veh_pos.y+0.1
            player:set_pos(p)
        end
    until true end end
end

function aom_vehicleapi.do_standing_on_vehicles(self, dtime)
    -- local cl = os.clock()
    local pos = self.object:get_pos()

    for playername, pi in pairs(self._aomv_pl) do repeat
        local player = minetest.get_player_by_name(playername)
        local p = player and player:get_pos()
        if not p then
            self._aomv_pl[playername] = nil
            break
        end
        local sdist = aom_vehicleapi.squaredist(p, pos)
        local out_of_range = sdist > 110 ^ 2
        local close_enough_to_stand = sdist < (self._aomv_platform_maxdist or 40) ^ 2
        close_enough_to_stand = close_enough_to_stand
        pi.is_attach = player:get_attach() == self.object

        if out_of_range then
            self._aomv_pl[playername] = nil
            if pi.is_in_poly then
                pi.is_in_poly = false
                SIGNAL("aomv_on_leave_vehicle_poly", self, player, pi)
            end
        elseif close_enough_to_stand and aom_vehicleapi.is_player_on_walkable_polygon(self, player) then
            if not pi.is_in_poly then
                pi.is_in_poly = true
                SIGNAL("aomv_on_enter_vehicle_poly", self, player, pi)
            end
            SIGNAL("aomv_on_stand_vehicle_poly", self, player, pi, dtime)
        else
            if pi.is_in_poly then
                pi.time_last_leave = 0
                pi.is_in_poly = false
                SIGNAL("aomv_on_leave_vehicle_poly", self, player, pi)
            end
        end

        pi.time_last_leave = (pi.time_last_leave or 0) + dtime
        -- table.insert(self._aomv_player_list, player:get_player_name())
    until true end
    -- minetest.log(os.clock() - cl)
end

LISTEN("aomv_on_stand_vehicle_poly", function(self, player, pi, dtime)
    if (not self._aomv_platforms) then return end
    aom_vehicleapi.do_player_push_up(self, dtime)
    aom_vehicleapi.platform_under_player(self, player, pi)
end)

LISTEN("aomv_on_enter_vehicle_poly", function(self, player, pi)
    if (not self._aomv_platforms) then return end
    aom_vehicleapi.platform_under_player(self, player, pi)
end)

LISTEN("aomv_on_leave_vehicle_poly", function(self, player, pi)
    if pi.obj then
        pi.obj:remove()
        pi.obj = nil
    end
end)

function aom_vehicleapi.add_platform_under_player(self, player)
    local pos = player:get_pos()
    local veh_pos = self.object:get_pos()
    local s = vector.new(1, 0.2, 1)
    pos.y = veh_pos.y + aom_vehicleapi.get_platform_y(self, player, self._aomv_pl[player:get_player_name()])

    local obj = minetest.add_entity(pos, "aom_vehicleapi:platform_ENTITY")
    local ent = obj:get_luaentity()
    if obj and ent then
        ent._aom_collisionbox = {
            -s.x, -s.y*2, -s.z, s.x, 0, s.z
        }
        ent._size = s
        ent._parent = self
        ent._player = player
        obj:set_properties({
            visual_size = s * 2,
            collisionbox = ent._aom_collisionbox,
            visual = "none",
        })
    end

    return obj, ent
end

local cbox = {-2, 0, -2, 2, 6/16, 2}
aom_vehicleapi.platform = {
    initial_properties = {
        visual = "cube",
        textures = {"white.png", "white.png", "white.png", "white.png", "white.png", "white.png"},
        use_texture_alpha = false,
        stepheight = 0,
        hp_max = 20,
        physical = true,
        collisionbox = cbox,
        selectionbox = cbox,
        pointable = false,
        damage_texture_modifier = "^[colorize:#ff9999:50",
        static_save = false,
    },
    on_step = function(self, dtime)
        if (not self._parent) or (self._parent.object:get_pos() == nil) then self.object:remove(); return end
        if (not self._player) or (self._player:get_pos() == nil) then self.object:remove(); return end
    end,
    on_death = function(self, killer)
    end,
    on_activate = function(self, staticdata, dtime_s)
        aom_vehicleapi.on_activate(self, staticdata, dtime_s)
    end,
    get_staticdata = function(self)
        return aom_vehicleapi.get_staticdata(self)
    end,
    on_deactivate = function(self, removal)
    end,
    on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
    end,
    _aom_vehicle = nil,
    _aom_statusfx_enable = false,
}

minetest.register_entity("aom_vehicleapi:platform_ENTITY", aom_vehicleapi.platform)
