use itertools::Itertools;
use crate::{place_not::PnBlockParseError, Block, PnBlock, Row, RowBuf, Stage};
use self::class::FullClass;
pub mod class;
pub const LABEL_LEAD_END: &str = "LE";
#[derive(Debug, Clone)]
pub struct Method {
pub name: String,
omit_class: bool, class: FullClass,
first_lead: Block<Vec<String>>,
}
impl Method {
pub fn new(
name: String,
class: FullClass,
omit_class: bool,
first_lead: Block<Vec<String>>,
) -> Self {
Self {
name,
omit_class,
class,
first_lead,
}
}
pub fn with_name(name: String, first_lead: Block<Vec<String>>) -> Self {
let class = FullClass::classify(&first_lead);
Self {
name,
omit_class: false,
class,
first_lead,
}
}
pub fn from_place_not_string(
name: String,
stage: Stage,
place_notation: &str,
) -> Result<Self, PnBlockParseError> {
Ok(Self::with_name(
name,
PnBlock::parse(place_notation, stage)?.to_block_from_rounds(),
))
}
pub fn with_lead_end(name: String, block: &PnBlock) -> Self {
let mut first_lead: Block<Vec<String>> = block.to_block_from_rounds();
first_lead
.get_annot_mut(0)
.unwrap()
.push(LABEL_LEAD_END.to_owned());
Self::with_name(name, first_lead)
}
#[inline]
pub fn first_lead(&self) -> &Block<Vec<String>> {
&self.first_lead
}
#[inline]
#[track_caller]
pub fn lead_end(&self) -> &Row {
self.first_lead
.rows()
.last()
.expect("`Method::lead_end` called on a method with a 0-length lead")
}
#[inline]
pub fn lead_head(&self) -> &Row {
self.first_lead.leftover_row()
}
#[inline]
pub fn lead_len(&self) -> usize {
self.first_lead.len()
}
pub fn course_len(&self) -> usize {
self.lead_len() * self.lead_head().order()
}
#[inline]
pub fn stage(&self) -> Stage {
self.first_lead.stage()
}
#[inline]
pub fn class(&self) -> FullClass {
self.class
}
pub fn title(&self) -> String {
generate_title(&self.name, self.class, self.omit_class, self.stage())
}
pub fn row_in_plain_lead(&self, idx: usize) -> &Row {
&self.first_lead.row_vec()[idx]
}
pub fn row_in_plain_course(&self, idx: usize) -> RowBuf {
let num_leads = idx / self.lead_len();
let sub_lead_idx = idx % self.lead_len();
let lead_head = self.lead_head().pow_u(num_leads);
let row_within_lead = self.row_in_plain_lead(sub_lead_idx);
lead_head.as_row() * row_within_lead
}
pub fn plain_course(&self) -> Block<RowAnnot> {
let first_lead_with_indices = self
.first_lead
.clone_map_annots_with_index(|i, labels| RowAnnot::new(i, labels));
let mut plain_course = first_lead_with_indices;
while !plain_course.leftover_row().is_rounds() {
plain_course.extend_from_within(0..self.lead_len());
}
plain_course
}
pub fn add_label(&mut self, index: usize, label: String) {
let labels = self.first_lead.get_annot_mut(index).unwrap();
if !labels.contains(&label) {
labels.push(label);
}
}
pub fn set_lead_end_label(&mut self) {
self.add_label(0, LABEL_LEAD_END.to_owned())
}
pub fn get_labels(&self, index: usize) -> &[String] {
self.first_lead.get_annot(index).unwrap()
}
pub fn label_indices<'s>(&'s self, label: &'s str) -> impl Iterator<Item = usize> + 's {
self.first_lead
.annots()
.positions(move |labels| labels.iter().any(|l| l == label))
}
pub fn all_label_indices(&self) -> Vec<(&str, usize)> {
let mut label_indices = Vec::new();
for (sub_lead_idx, labels) in self.first_lead.annots().enumerate() {
for label in labels {
label_indices.push((label.as_str(), sub_lead_idx));
}
}
label_indices
}
pub fn next_labels(&self, sub_lead_idx: usize) -> (&[String], usize) {
for distance in 1..=self.lead_len() {
let labels = self.get_labels((sub_lead_idx + distance) % self.lead_len());
if !labels.is_empty() {
return (labels, distance);
}
}
panic!("`next_labels` called on a method with no labels")
}
pub fn prev_labels(&self, sub_lead_idx: usize) -> (&[String], usize) {
for distance in 0..self.lead_len() {
let labels =
self.get_labels((sub_lead_idx + self.lead_len() - distance) % self.lead_len());
if !labels.is_empty() {
return (labels, distance);
}
}
panic!("`prev_labels` called on a method with no labels")
}
}
pub fn generate_title(name: &str, class: FullClass, omit_class: bool, stage: Stage) -> String {
let mut s = String::new();
s.push_str(name);
if !name.is_empty() {
s.push(' ');
}
if !omit_class {
let len_before_class = s.len();
class.fmt_name(&mut s, true).unwrap();
if s.len() > len_before_class {
s.push(' ');
}
}
s.push_str(stage.name().expect("Stage was too big to generate a name"));
s
}
#[derive(Debug, Clone)]
pub struct RowAnnot<'meth> {
pub sub_lead_idx: usize,
pub labels: &'meth [String],
}
impl<'meth> RowAnnot<'meth> {
fn new(sub_lead_idx: usize, labels: &'meth [String]) -> Self {
Self {
sub_lead_idx,
labels,
}
}
}