use alloc::vec::Vec;
use crate::currency::{
E_DECIMAL, E_GROUP, E_PATTERN, F_NAMES, F_NARROW, H_NAME, H_NARROW, H_ROUNDING, H_SYMBOL,
};
use crate::error::WriteError;
use crate::format::locale_key;
use crate::number::{OTHER, TemplatePart};
use crate::writer::encode::varint;
use crate::writer::number::{PatternSpec, body, forms, str8, template};
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct CurrencySpec<'s> {
pub code: &'s str,
pub digits: u8,
pub rounding: u32,
pub symbol: &'s str,
pub narrow: &'s str,
pub name: &'s str,
pub names: Vec<(u8, &'s str)>,
pub edges: u8,
pub pattern: Option<PatternSpec<'s>>,
pub decimal: Option<&'s str>,
pub group: Option<&'s str>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct CurrenciesSpec<'s> {
pub default_digits: u8,
pub narrow: bool,
pub names: bool,
pub name_patterns: Vec<(u8, Vec<TemplatePart<'s>>)>,
pub currencies: Vec<CurrencySpec<'s>>,
}
const ERR: WriteError = WriteError::LocaleEntry(locale_key::CURRENCY_DATA);
fn name_forms<'s>(name: &'s str, list: &[(u8, &'s str)]) -> Vec<(u8, &'s str)> {
let other = list.iter().find(|(c, _)| *c == OTHER).map(|(_, s)| *s);
let other = other.filter(|o| *o != name);
let fallback = other.unwrap_or(name);
let mut out: Vec<(u8, &str)> = list
.iter()
.filter(|(c, s)| {
if *c == OTHER {
other.is_some()
} else {
*s != fallback
}
})
.copied()
.collect();
out.sort_by_key(|(c, _)| *c);
out
}
pub fn currencies(spec: &CurrenciesSpec<'_>) -> Result<Vec<u8>, WriteError> {
let mut list: Vec<&CurrencySpec<'_>> = spec.currencies.iter().collect();
list.sort_by_key(|c| c.code);
if list
.windows(2)
.any(|w| matches!(w, [a, b] if a.code == b.code))
{
return Err(ERR);
}
let flags = (u8::from(spec.narrow) * F_NARROW) | (u8::from(spec.names) * F_NAMES);
let mut out = Vec::new();
out.push(flags);
out.push(spec.default_digits);
if spec.names {
let other = spec
.name_patterns
.iter()
.find(|(c, _)| *c == OTHER)
.map(|(_, t)| t);
let pats: Vec<(u8, &[TemplatePart<'_>])> = spec
.name_patterns
.iter()
.filter(|(c, t)| *c == OTHER || Some(t) != other)
.map(|(c, t)| (*c, t.as_slice()))
.collect();
forms(&pats, &mut out, &ERR, |parts, out| {
template(parts, out, &ERR)
})?;
}
let n = u16::try_from(list.len()).map_err(|_| ERR)?;
out.extend_from_slice(&n.to_le_bytes());
let mut records = Vec::new();
let mut index = Vec::with_capacity(list.len() * 7);
for c in list {
if c.code.len() != 3 || !c.code.bytes().all(|b| b.is_ascii_uppercase()) {
return Err(ERR);
}
if c.digits > 15 || c.edges > 0x0f {
return Err(ERR);
}
index.extend_from_slice(c.code.as_bytes());
index.extend_from_slice(&u32::try_from(records.len()).map_err(|_| ERR)?.to_le_bytes());
let symbol = (c.symbol != c.code).then_some(c.symbol);
let narrow = (spec.narrow && c.narrow != c.symbol).then_some(c.narrow);
let name = (spec.names && c.name != c.code).then_some(c.name);
let mut head = c.digits;
if c.rounding != 0 {
head |= H_ROUNDING;
}
if symbol.is_some() {
head |= H_SYMBOL;
}
if narrow.is_some() {
head |= H_NARROW;
}
if name.is_some() {
head |= H_NAME;
}
records.push(head);
if c.rounding != 0 {
varint(c.rounding, &mut records);
}
let mut edges = if spec.narrow { c.edges } else { c.edges & 0x03 };
if c.pattern.is_some() {
edges |= E_PATTERN;
}
if c.decimal.is_some() {
edges |= E_DECIMAL;
}
if c.group.is_some() {
edges |= E_GROUP;
}
records.push(edges);
for s in [symbol, narrow].into_iter().flatten() {
str8(s, &mut records, &ERR)?;
}
if let Some(p) = &c.pattern {
let b = body(p)?;
records.push(u8::try_from(b.len()).map_err(|_| ERR)?);
records.extend_from_slice(&b);
}
for s in [c.decimal, c.group, name].into_iter().flatten() {
str8(s, &mut records, &ERR)?;
}
if spec.names {
let list = name_forms(c.name, &c.names);
forms(&list, &mut records, &ERR, |s, out| str8(s, out, &ERR))?;
}
}
out.extend_from_slice(&index);
out.extend_from_slice(&records);
Ok(out)
}