use axum::Json;
use axum::extract::{Multipart, State};
use serde::Serialize;
use sl_map_apis::region::usb_notecard_to_grid_rectangle;
use sl_types::map::{GridRectangleLike as _, USBNotecard};
use crate::auth::CurrentUser;
use crate::error::Error;
use crate::library;
use crate::state::AppState;
use crate::usb_notecard::parse_notecard_form;
#[derive(Debug, Clone, Serialize)]
pub struct ResolvedWaypoint {
pub region_name: String,
pub region_x: u16,
pub region_y: u16,
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Debug, Clone, Serialize)]
pub struct DeriveResponse {
pub lower_left_x: u16,
pub lower_left_y: u16,
pub upper_right_x: u16,
pub upper_right_y: u16,
pub waypoints: Vec<ResolvedWaypoint>,
}
pub async fn derive_rectangle(
user: CurrentUser,
State(state): State<AppState>,
multipart: Multipart,
) -> Result<Json<DeriveResponse>, Error> {
let form = parse_notecard_form(multipart).await?;
let (border_north, border_south, border_east, border_west) = form.borders();
let notecard = match (form.notecard, form.notecard_id) {
(Some(nc), _) => nc,
(None, Some(id)) => {
let row = library::assert_can_read_notecard(&state.db, user.user_id, id).await?;
row.body.parse()?
}
(None, None) => return Err(Error::BadRequest("missing notecard".to_owned())),
};
let mut region_cache = state.region_cache.lock().await;
let rect = usb_notecard_to_grid_rectangle(&mut region_cache, ¬ecard)
.await?
.expanded_west(border_west)
.expanded_east(border_east)
.expanded_south(border_south)
.expanded_north(border_north);
let waypoints = resolve_waypoints(&mut region_cache, ¬ecard).await?;
drop(region_cache);
let lower_left = rect.lower_left_corner();
let upper_right = rect.upper_right_corner();
Ok(Json(DeriveResponse {
lower_left_x: lower_left.x(),
lower_left_y: lower_left.y(),
upper_right_x: upper_right.x(),
upper_right_y: upper_right.y(),
waypoints,
}))
}
async fn resolve_waypoints(
region_cache: &mut sl_map_apis::region::RegionNameToGridCoordinatesCache,
notecard: &USBNotecard,
) -> Result<Vec<ResolvedWaypoint>, Error> {
let mut out = Vec::with_capacity(notecard.waypoints().len());
for waypoint in notecard.waypoints() {
let region = waypoint.location().region_name();
let grid = region_cache
.get_grid_coordinates(region)
.await?
.ok_or_else(|| {
Error::BadRequest(format!(
"could not resolve region `{}` to grid coordinates",
region.to_owned().into_inner()
))
})?;
let rc = waypoint.region_coordinates();
out.push(ResolvedWaypoint {
region_name: region.to_owned().into_inner(),
region_x: grid.x(),
region_y: grid.y(),
x: rc.x(),
y: rc.y(),
z: rc.z(),
});
}
Ok(out)
}