use crate::session::messages as msg;
use crate::session::question::{Question, QuestionStep};
use crate::session::state::ClassificationState;
use crate::types::{Language, OrganicInorganic, PhysicalForm};
pub fn next_question(
state: &ClassificationState,
lang: Language,
) -> Option<(Question, QuestionStep)> {
if !state.has_identifier() {
let (prompt, example) = msg::q_identifier(lang);
return Some((
Question::Text { prompt, example: Some(example) },
QuestionStep::Identifier,
));
}
if state.is_mixture.is_none() {
return Some((
Question::YesNo { prompt: msg::q_is_mixture(lang) },
QuestionStep::IsMixture,
));
}
if state.is_mixture == Some(true) {
if state.component_count.is_none() {
let (prompt, unit) = msg::q_component_count(lang);
return Some((
Question::Number { prompt, unit, min: 2.0, max: 20.0 },
QuestionStep::ComponentCount,
));
}
let expected = state.component_count.unwrap_or(0);
let idx = state.current_component_index;
if idx < expected && !state.current_component_has_identifier() {
let (prompt, example) = msg::q_component_identifier(lang, idx + 1);
return Some((
Question::Text { prompt, example: Some(example) },
QuestionStep::ComponentIdentifier,
));
}
if idx < expected && !state.current_component_has_fraction() {
let name = state
.components
.get(idx)
.and_then(|c| c.identifier.iupac_name.clone())
.or_else(|| state.components.get(idx).and_then(|c| c.identifier.cas.clone()))
.unwrap_or_else(|| format!("component {}", idx + 1));
let (prompt, unit) = msg::q_component_fraction(lang, &name);
return Some((
Question::Number { prompt, unit, min: 0.0, max: 100.0 },
QuestionStep::ComponentFraction,
));
}
if state.components.len() < expected {
let next_idx = state.components.len();
let (prompt, example) = msg::q_next_component_identifier(lang, next_idx + 1);
return Some((
Question::Text { prompt, example: Some(example) },
QuestionStep::ComponentIdentifier,
));
}
return None;
}
if state.physical_form.is_none() {
let (prompt, options) = msg::q_physical_form(lang);
return Some((
Question::Choice { prompt, options },
QuestionStep::PhysicalForm,
));
}
if let Some(PhysicalForm::Solution { concentration_pct_ww: None, .. }) = &state.physical_form {
let (prompt, unit) = msg::q_solution_concentration(lang);
return Some((
Question::Number { prompt, unit, min: 0.0, max: 100.0 },
QuestionStep::SolutionConcentration,
));
}
if state.intended_use.is_none() {
let (prompt, options) = msg::q_intended_use(lang);
return Some((
Question::Choice { prompt, options },
QuestionStep::IntendedUse,
));
}
if state.organic_inorganic.is_none() && state.identifier.smiles.is_none() {
let (prompt, options) = msg::q_organic_inorganic(lang);
return Some((
Question::Choice { prompt, options },
QuestionStep::OrganicInorganic,
));
}
if state.identifier.smiles.is_none()
&& matches!(state.organic_inorganic, Some(OrganicInorganic::Organic))
&& state.detected_functional_groups.is_empty()
&& state.chapter_hint.is_none()
{
let (prompt, options) = msg::q_functional_groups(lang);
return Some((
Question::MultiChoice { prompt, options, include_unknown: true },
QuestionStep::FunctionalGroups,
));
}
None
}
pub fn choice_index_to_physical_form(index: usize) -> PhysicalForm {
match index {
0 => PhysicalForm::Solid,
1 => PhysicalForm::Powder { particle_size_um: None },
2 => PhysicalForm::Granules,
3 => PhysicalForm::Liquid,
4 => PhysicalForm::Solution { solvent: None, concentration_pct_ww: None },
5 => PhysicalForm::Gas,
6 => PhysicalForm::Foil { thickness_mm: None },
7 => PhysicalForm::Ingot,
_ => PhysicalForm::Unknown,
}
}
pub fn choice_index_to_intended_use(index: usize) -> crate::types::IntendedUse {
use crate::types::IntendedUse;
match index {
0 => IntendedUse::Industrial,
1 => IntendedUse::Pharmaceutical,
2 => IntendedUse::Agricultural,
3 => IntendedUse::Food,
4 => IntendedUse::Cosmetic,
_ => IntendedUse::Other("unknown".to_string()),
}
}
pub fn choice_index_to_organic_inorganic(index: usize) -> OrganicInorganic {
match index {
0 => OrganicInorganic::Organic,
1 => OrganicInorganic::Inorganic,
_ => OrganicInorganic::Unknown,
}
}
pub fn multi_choice_indices_to_functional_groups(indices: &[usize]) -> Vec<String> {
const GROUPS: &[&str] = &[
"carboxylic_acid",
"alcohol",
"phenol",
"aldehyde",
"ketone",
"amine",
"amide",
"nitrile",
"halide",
"ester",
"aromatic",
];
indices
.iter()
.filter_map(|&i| GROUPS.get(i).map(|s| s.to_string()))
.collect()
}