use std::{fmt::Display, str::FromStr};
use crate::geometry::ValueEnum;
use crate::math::{EntityKind, IndexMap, Reconstruct, ReconstructCtx, Reindex};
use geo_aid_figure::math_string::{
MathChar, MathIndex, MathSpecial, MathString, ParseErrorKind, SPECIAL_MATH,
};
use geo_aid_figure::{Style, VarIndex};
use crate::span;
use super::math::Entity;
use super::{
math,
parser::{FromProperty, Parse, PropertyValue},
token::{Ident, PointCollectionItem, Span},
unroll::most_similar,
Error,
};
#[derive(Debug, Clone)]
pub struct PointItem {
pub id: VarIndex,
pub label: MathString,
pub display_dot: bool,
}
impl Reindex for PointItem {
fn reindex(&mut self, map: &IndexMap) {
self.id.reindex(map);
}
}
impl Reconstruct for PointItem {
fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
Self {
id: self.id.reconstruct(ctx),
..self
}
}
}
impl From<PointItem> for Item {
fn from(value: PointItem) -> Self {
Self::Point(value)
}
}
#[derive(Debug, Clone)]
pub struct CircleItem {
pub id: VarIndex,
pub label: MathString,
pub style: Style,
}
impl Reindex for CircleItem {
fn reindex(&mut self, map: &IndexMap) {
self.id.reindex(map);
}
}
impl Reconstruct for CircleItem {
fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
Self {
id: self.id.reconstruct(ctx),
..self
}
}
}
impl From<CircleItem> for Item {
fn from(value: CircleItem) -> Self {
Self::Circle(value)
}
}
#[derive(Debug, Clone)]
pub struct LineItem {
pub id: VarIndex,
pub label: MathString,
pub style: Style,
}
impl Reindex for LineItem {
fn reindex(&mut self, map: &IndexMap) {
self.id.reindex(map);
}
}
impl Reconstruct for LineItem {
fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
Self {
id: self.id.reconstruct(ctx),
..self
}
}
}
impl From<LineItem> for Item {
fn from(value: LineItem) -> Self {
Self::Line(value)
}
}
#[derive(Debug, Clone)]
pub struct RayItem {
pub p_id: VarIndex,
pub q_id: VarIndex,
pub label: MathString,
pub style: Style,
}
impl Reindex for RayItem {
fn reindex(&mut self, map: &IndexMap) {
self.p_id.reindex(map);
self.q_id.reindex(map);
}
}
impl Reconstruct for RayItem {
fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
Self {
p_id: self.p_id.reconstruct(ctx),
q_id: self.q_id.reconstruct(ctx),
..self
}
}
}
impl From<RayItem> for Item {
fn from(value: RayItem) -> Self {
Self::Ray(value)
}
}
#[derive(Debug, Clone)]
pub struct SegmentItem {
pub p_id: VarIndex,
pub q_id: VarIndex,
pub label: MathString,
pub style: Style,
}
impl From<SegmentItem> for Item {
fn from(value: SegmentItem) -> Self {
Self::Segment(value)
}
}
impl Reindex for SegmentItem {
fn reindex(&mut self, map: &IndexMap) {
self.p_id.reindex(map);
self.q_id.reindex(map);
}
}
impl Reconstruct for SegmentItem {
fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
Self {
p_id: self.p_id.reconstruct(ctx),
q_id: self.q_id.reconstruct(ctx),
..self
}
}
}
#[derive(Debug, Clone)]
pub enum Item {
Point(PointItem),
Circle(CircleItem),
Line(LineItem),
Ray(RayItem),
Segment(SegmentItem),
}
impl Reindex for Item {
fn reindex(&mut self, map: &IndexMap) {
match self {
Self::Point(v) => v.reindex(map),
Self::Circle(v) => v.reindex(map),
Self::Line(v) => v.reindex(map),
Self::Ray(v) => v.reindex(map),
Self::Segment(v) => v.reindex(map),
}
}
}
impl Reconstruct for Item {
fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
match self {
Self::Point(v) => Self::Point(v.reconstruct(ctx)),
Self::Circle(v) => Self::Circle(v.reconstruct(ctx)),
Self::Line(v) => Self::Line(v.reconstruct(ctx)),
Self::Ray(v) => Self::Ray(v.reconstruct(ctx)),
Self::Segment(v) => Self::Segment(v.reconstruct(ctx)),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct Figure {
pub entities: Vec<EntityKind>,
pub variables: Vec<math::Expr<()>>,
pub items: Vec<Item>,
}
#[derive(Debug, Clone, Default)]
pub struct Generated {
pub entities: Vec<Entity<ValueEnum>>,
pub variables: Vec<math::Expr<ValueEnum>>,
pub items: Vec<Item>,
}
#[derive(Debug, Clone)]
pub struct SpannedMathString {
pub string: MathString,
pub span: Span,
}
impl SpannedMathString {
#[must_use]
pub fn new(span: Span) -> Self {
Self {
string: MathString::new(),
span,
}
}
#[must_use]
pub fn displayed_by_default(&self) -> Option<Self> {
let mut result = MathString::new();
let mut letter = String::new();
let mut chars = self.string.iter().copied().peekable();
while let Some(MathChar::Ascii(c)) = chars.peek().copied() {
chars.next();
letter.push(c);
}
if let Some(special) = MathSpecial::parse(&letter) {
if special.is_alphabetic() {
result.push(MathChar::Special(special));
} else {
return None;
}
} else if letter.len() == 1 {
result.push(MathChar::Ascii(letter.chars().next().unwrap()));
} else {
return None;
}
while Some(MathChar::Prime) == chars.peek().copied() {
chars.next();
result.push(MathChar::Prime);
}
if chars.next() == Some(MathChar::SetIndex(MathIndex::Lower)) {
result.push(MathChar::SetIndex(MathIndex::Lower));
for c in chars.by_ref() {
if c == MathChar::SetIndex(MathIndex::Normal) {
break;
}
result.push(c);
}
result.push(MathChar::SetIndex(MathIndex::Normal));
}
if chars.next().is_none() {
Some(Self {
string: result,
span: self.span,
})
} else {
None
}
}
pub fn parse(content: &str, content_span: Span) -> Result<Self, Error> {
let string = MathString::from_str(content).map_err(|err| {
let error_span = span!(
content_span.start.line,
content_span.start.column + err.span.start,
content_span.end.line,
content_span.end.column + err.span.end
);
match err.kind {
ParseErrorKind::SpecialNotRecognised(special) => {
let suggested = most_similar(&SPECIAL_MATH, &special);
Error::SpecialNotRecognised {
error_span,
code: special,
suggested,
}
}
ParseErrorKind::NestedIndex => Error::LabelIndexInsideIndex { error_span },
ParseErrorKind::UnclosedSpecialTag(special) => Error::UnclosedSpecial {
error_span,
parsed_special: special,
},
}
})?;
Ok(Self {
string,
span: content_span,
})
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.string.is_empty()
}
#[must_use]
pub fn get_span(&self) -> Span {
self.span
}
}
impl FromProperty for SpannedMathString {
fn from_property(property: PropertyValue) -> Result<Self, Error> {
match property {
PropertyValue::Number(n) => Err(Error::StringOrIdentExpected {
error_span: n.get_span(),
}),
PropertyValue::Ident(i) => match i {
Ident::Collection(mut c) => {
if c.len() == 1 {
Ok(Self::from(c.collection.swap_remove(0)))
} else {
Err(Error::InvalidIdentMathString { error_span: c.span })
}
}
Ident::Named(n) => Self::parse(&n.ident, n.span)?
.displayed_by_default()
.ok_or(Error::InvalidIdentMathString { error_span: n.span }),
},
PropertyValue::String(s) => Self::parse(
&s.content,
span!(
s.span.start.line,
s.span.start.column + 1,
s.span.end.line,
s.span.end.column - 1
),
),
PropertyValue::RawString(s) => Ok(Self {
string: MathString::raw(&s.lit.content),
span: s.get_span(),
}),
}
}
}
impl From<PointCollectionItem> for SpannedMathString {
fn from(value: PointCollectionItem) -> Self {
let mut string = MathString::new();
string.push(MathChar::Ascii(value.letter));
if let Some(index) = value.index {
string.extend(index.chars().map(MathChar::Ascii));
}
string.extend([MathChar::Prime].repeat(value.primes.into()));
Self {
string,
span: value.span,
}
}
}
impl Display for SpannedMathString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.string)
}
}