use super::super::Renderer;
use crate::render::ProcTemplateComponent;
use crate::render::bibliography::{append_rendered_component, component_starts_new_sentence};
use crate::render::component::render_component_with_format;
use crate::render::punctuation::strong_terminal_comma_policy;
use crate::values::RenderContext;
use citum_schema::NoteStartTextCase;
use citum_schema::locale::GeneralTerm;
use citum_schema::options::titles::TextCase;
use citum_schema::template::TemplateComponent;
impl Renderer<'_> {
pub(super) fn apply_sentence_initial_context<F>(
&self,
components: &mut [ProcTemplateComponent],
context: RenderContext,
note_start_text_case: Option<NoteStartTextCase>,
) where
F: crate::render::format::OutputFormat<Output = String>,
{
match context {
RenderContext::Bibliography => {
self.apply_bibliography_sentence_initial_context::<F>(components);
}
RenderContext::Citation => {
self.apply_note_start_sentence_initial_context(components, note_start_text_case);
}
}
}
fn apply_bibliography_sentence_initial_context<F>(
&self,
components: &mut [ProcTemplateComponent],
) where
F: crate::render::format::OutputFormat<Output = String>,
{
let punctuation_in_quote = components
.first()
.and_then(|component| component.config.as_ref())
.is_some_and(|config| config.punctuation_in_quote);
let strong_terminal_comma_policy = strong_terminal_comma_policy(
components
.first()
.and_then(|component| component.config.as_deref()),
);
let default_separator = components
.first()
.and_then(|component| component.bibliography_config.as_ref())
.and_then(|bib| bib.separator.as_deref())
.unwrap_or(". ")
.to_string();
let mut entry_output = String::new();
for component in components.iter_mut() {
let rendered = render_component_with_format::<F>(component);
if rendered.is_empty() {
continue;
}
if component_starts_new_sentence::<F>(
&entry_output,
&rendered,
&default_separator,
punctuation_in_quote,
) {
component.sentence_initial = true;
self.apply_sentence_initial_transform(component, None);
}
let rendered = render_component_with_format::<F>(component);
append_rendered_component::<F>(
&mut entry_output,
&rendered,
&default_separator,
punctuation_in_quote,
strong_terminal_comma_policy,
);
}
}
fn apply_note_start_sentence_initial_context(
&self,
components: &mut [ProcTemplateComponent],
note_start_text_case: Option<NoteStartTextCase>,
) {
let Some(text_case) = note_start_text_case else {
return;
};
for component in components.iter_mut() {
if !self.is_note_start_term_component(component) {
continue;
}
component.sentence_initial = true;
self.apply_sentence_initial_transform(component, Some(text_case));
break;
}
}
fn apply_sentence_initial_transform(
&self,
component: &mut ProcTemplateComponent,
note_start_text_case: Option<NoteStartTextCase>,
) {
if !component.sentence_initial {
return;
}
let locale = Some(self.locale.locale.as_str());
match &component.template_component {
TemplateComponent::Contributor(contributor) => {
let case =
crate::values::text_case::resolve_text_case(TextCase::CapitalizeFirst, locale);
if let Some(prefix) = component.prefix.as_mut() {
*prefix = crate::values::text_case::apply_text_case(prefix, case);
} else if contributor.rendering.prefix.is_some() {
} else {
component.value = if component.pre_formatted {
crate::values::text_case::apply_text_case_markup_aware(
&component.value,
case,
)
} else {
crate::values::text_case::apply_text_case(&component.value, case)
};
}
}
TemplateComponent::Group(group) if group_supports_sentence_initial_casing(group) => {
let case =
crate::values::text_case::resolve_text_case(TextCase::CapitalizeFirst, locale);
component.value =
crate::values::text_case::apply_text_case_markup_aware(&component.value, case);
}
TemplateComponent::Message(message) if !message.message.starts_with("term.") => {
let case =
crate::values::text_case::resolve_text_case(TextCase::CapitalizeFirst, locale);
component.value = if component.pre_formatted {
crate::values::text_case::apply_text_case_markup_aware(&component.value, case)
} else {
crate::values::text_case::apply_text_case(&component.value, case)
};
}
TemplateComponent::Term(_) | TemplateComponent::Message(_)
if self.is_note_start_term_component(component) =>
{
if let Some(case) = note_start_text_case {
component.value = crate::values::text_case::apply_note_start_text_case(
&component.value,
case,
locale,
);
}
}
_ => {}
}
}
fn is_note_start_term_component(&self, component: &ProcTemplateComponent) -> bool {
matches!(
&component.template_component,
TemplateComponent::Term(term) if term.term == GeneralTerm::Ibid
) || matches!(
&component.template_component,
TemplateComponent::Message(message) if message.message == "term.ibid"
)
}
}
fn group_supports_sentence_initial_casing(group: &citum_schema::template::TemplateGroup) -> bool {
group.group.iter().find_map(first_sentence_casing_candidate) == Some(true)
}
fn first_sentence_casing_candidate(component: &TemplateComponent) -> Option<bool> {
if component.rendering().suppress == Some(true) {
return None;
}
match component {
TemplateComponent::Contributor(contributor) => Some(matches!(
contributor.form,
citum_schema::template::ContributorForm::Verb
| citum_schema::template::ContributorForm::VerbShort
)),
TemplateComponent::Title(_) => Some(true),
TemplateComponent::Group(group) => {
group.group.iter().find_map(first_sentence_casing_candidate)
}
_ => Some(false),
}
}