use float_ord::FloatOrd;
use crate::error::SelectionError;
use crate::properties::{Properties, Stretch, Style, Weight};
pub fn find_best_match(
candidates: &[Properties],
query: &Properties,
) -> Result<usize, SelectionError> {
let mut matching_set: Vec<usize> = (0..candidates.len()).collect();
if matching_set.is_empty() {
return Err(SelectionError::NotFound);
}
let matching_stretch = if matching_set
.iter()
.any(|&index| candidates[index].stretch == query.stretch)
{
query.stretch
} else if query.stretch <= Stretch::NORMAL {
match matching_set
.iter()
.filter(|&&index| candidates[index].stretch < query.stretch)
.min_by_key(|&&index| FloatOrd(query.stretch.0 - candidates[index].stretch.0))
{
Some(&matching_index) => candidates[matching_index].stretch,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| FloatOrd(candidates[index].stretch.0 - query.stretch.0))
.unwrap();
candidates[matching_index].stretch
}
}
} else {
match matching_set
.iter()
.filter(|&&index| candidates[index].stretch > query.stretch)
.min_by_key(|&&index| FloatOrd(candidates[index].stretch.0 - query.stretch.0))
{
Some(&matching_index) => candidates[matching_index].stretch,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| FloatOrd(query.stretch.0 - candidates[index].stretch.0))
.unwrap();
candidates[matching_index].stretch
}
}
};
matching_set.retain(|&index| candidates[index].stretch == matching_stretch);
let style_preference = match query.style {
Style::Italic => [Style::Italic, Style::Oblique, Style::Normal],
Style::Oblique => [Style::Oblique, Style::Italic, Style::Normal],
Style::Normal => [Style::Normal, Style::Oblique, Style::Italic],
};
let matching_style = *style_preference
.iter()
.find(|&query_style| {
matching_set
.iter()
.any(|&index| candidates[index].style == *query_style)
})
.unwrap();
matching_set.retain(|&index| candidates[index].style == matching_style);
let matching_weight = if matching_set
.iter()
.any(|&index| candidates[index].weight == query.weight)
{
query.weight
} else if query.weight >= Weight(400.0)
&& query.weight < Weight(450.0)
&& matching_set
.iter()
.any(|&index| candidates[index].weight == Weight(500.0))
{
Weight(500.0)
} else if query.weight >= Weight(450.0)
&& query.weight <= Weight(500.0)
&& matching_set
.iter()
.any(|&index| candidates[index].weight == Weight(400.0))
{
Weight(400.0)
} else if query.weight <= Weight(500.0) {
match matching_set
.iter()
.filter(|&&index| candidates[index].weight <= query.weight)
.min_by_key(|&&index| FloatOrd(query.weight.0 - candidates[index].weight.0))
{
Some(&matching_index) => candidates[matching_index].weight,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| FloatOrd(candidates[index].weight.0 - query.weight.0))
.unwrap();
candidates[matching_index].weight
}
}
} else {
match matching_set
.iter()
.filter(|&&index| candidates[index].weight >= query.weight)
.min_by_key(|&&index| FloatOrd(candidates[index].weight.0 - query.weight.0))
{
Some(&matching_index) => candidates[matching_index].weight,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| FloatOrd(query.weight.0 - candidates[index].weight.0))
.unwrap();
candidates[matching_index].weight
}
}
};
matching_set.retain(|&index| candidates[index].weight == matching_weight);
matching_set
.into_iter()
.next()
.ok_or(SelectionError::NotFound)
}