use super::maps::{BREVE_MAP, CIRCUMFLEX_MAP, DYET_MAP, HORN_MAP};
use crate::{editing::get_modification_positions, syllable::Syllable};
const MAX_WORD_LENGTH: usize = 7;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum ToneMark {
Acute,
Grave,
HookAbove,
Tilde,
Underdot,
}
#[derive(Debug, PartialEq, Clone, Default)]
pub enum AccentStyle {
Old,
#[default]
New,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum LetterModification {
Circumflex,
Breve,
Horn,
Dyet,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Transformation {
ToneMarkAdded,
ToneMarkReplaced,
ToneMarkRemoved,
LetterModificationAdded,
LetterModificationReplaced,
LetterModificationRemoved,
Ignored,
}
#[must_use]
pub fn add_tone(syllable: &mut Syllable, tone_mark: &ToneMark) -> Transformation {
if syllable.is_empty() || syllable.len() > MAX_WORD_LENGTH {
return Transformation::Ignored;
}
if syllable.vowel.is_empty() {
return Transformation::Ignored;
}
if let Some(existing_tone_mark) = syllable.tone_mark {
if existing_tone_mark == *tone_mark {
syllable.tone_mark = None;
Transformation::ToneMarkRemoved
} else {
syllable.tone_mark = Some(*tone_mark);
Transformation::ToneMarkReplaced
}
} else {
syllable.tone_mark = Some(*tone_mark);
Transformation::ToneMarkAdded
}
}
#[must_use]
pub fn modify_letter(syllable: &mut Syllable, modification: &LetterModification) -> Transformation {
if syllable.is_empty() || syllable.len() > MAX_WORD_LENGTH {
return Transformation::Ignored;
}
if syllable.contains_modification(modification) {
if let LetterModification::Horn = modification {
let current_modifications: Vec<&(usize, LetterModification)> = syllable
.letter_modifications
.iter()
.filter(|(_, modification)| *modification == LetterModification::Horn)
.collect();
let horn_modification_count = current_modifications.len();
let vowel = syllable.vowel.to_lowercase();
let vowel_index = syllable.initial_consonant.chars().count();
let modification_possibilities: Vec<usize> = [vowel.find('u'), vowel.find('o')]
.iter()
.filter_map(|index| index.map(|index| vowel_index + index))
.collect();
let max_horn_modification_possible = modification_possibilities.len();
if horn_modification_count < max_horn_modification_possible {
let other_modification_position = modification_possibilities
.iter()
.find(|index| {
current_modifications
.iter()
.any(|(current_index, _)| *current_index != **index)
})
.unwrap();
syllable
.letter_modifications
.push((*other_modification_position, LetterModification::Horn));
return Transformation::LetterModificationAdded;
}
}
syllable.letter_modifications.remove(
syllable
.letter_modifications
.iter()
.position(|(_, m)| m == modification)
.unwrap(),
);
return Transformation::LetterModificationRemoved;
}
if *modification == LetterModification::Dyet {
if let Some(first_char) = syllable.initial_consonant.chars().next() {
if DYET_MAP.contains_key(&first_char) {
syllable
.letter_modifications
.push((0, LetterModification::Dyet));
return Transformation::LetterModificationAdded;
}
}
return Transformation::Ignored;
}
if *modification == LetterModification::Horn && syllable.vowel.to_lowercase() == "oa" {
return Transformation::Ignored;
}
let map = match modification {
LetterModification::Horn => &HORN_MAP,
LetterModification::Breve => &BREVE_MAP,
LetterModification::Circumflex => &CIRCUMFLEX_MAP,
LetterModification::Dyet => &DYET_MAP,
};
if syllable.letter_modifications.is_empty()
|| (syllable.letter_modifications.len() == 1
&& syllable.contains_modification(&LetterModification::Dyet))
{
if !syllable.vowel.contains(|c| map.contains_key(&c)) {
return Transformation::Ignored;
}
let positions = get_modification_positions(syllable, modification);
if positions.is_empty() {
return Transformation::Ignored;
}
for position in positions {
syllable
.letter_modifications
.push((position, *modification));
}
return Transformation::LetterModificationAdded;
}
if !syllable.vowel.contains(|c| map.contains_key(&c)) {
return Transformation::Ignored;
}
let positions = get_modification_positions(syllable, modification);
syllable
.letter_modifications
.retain(|(_, modification)| *modification == LetterModification::Dyet);
for position in positions {
syllable
.letter_modifications
.push((position, *modification));
}
Transformation::LetterModificationReplaced
}
pub fn remove_tone(input: &mut Syllable) -> Transformation {
if input.len() > MAX_WORD_LENGTH {
return Transformation::Ignored;
}
let tone_removed = input.tone_mark.is_some();
input.tone_mark = None;
if tone_removed {
Transformation::ToneMarkRemoved
} else {
Transformation::Ignored
}
}