use std::{
    cmp::{max, min},
    fmt::{self, Display},
};

mod annotatedstringiterator;
mod annotatedstringpart;

use super::{Annotation, AnnotationType};
use crate::prelude::*;
use annotatedstringiterator::AnnotatedStringIterator;
use annotatedstringpart::AnnotatedStringPart;

#[derive(Debug, Default)]
pub struct AnnotatedString {
    string: String,
    annotattions: Vec<Annotation>,
}

impl Display for AnnotatedString {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", self.string)
    }
}

impl<'a> IntoIterator for &'a AnnotatedString {
    type Item = AnnotatedStringPart<'a>;
    type IntoIter = AnnotatedStringIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        AnnotatedStringIterator {
            annotated_string: self,
            current_idx: 0,
        }
    }
}

impl AnnotatedString {
    pub fn from(string: &str) -> Self {
        Self {
            string: String::from(string),
            annotattions: Vec::new(),
        }
    }

    pub fn add_annotation(
        &mut self,
        annotation_type: AnnotationType,
        start: ByteIdx,
        end: ByteIdx,
    ) {
        debug_assert!(start <= end);

        self.annotattions.push(Annotation {
            annotation_type,
            start,
            end,
        });
    }

    pub fn replace(&mut self, start: ByteIdx, end: ByteIdx, new_string: &str) {
        let end = min(end, self.string.len());

        debug_assert!(start <= end);
        debug_assert!(start <= self.string.len());

        if start > end {
            return;
        }

        self.string.replace_range(start..end, new_string);

        let replaced_range_len = end.saturating_sub(start);
        let shortened = new_string.len() < replaced_range_len;
        let len_differences = new_string.len().abs_diff(replaced_range_len);

        if len_differences == 0 {
            return;
        }

        self.annotattions.iter_mut().for_each(|annotation| {
            annotation.start = if annotation.start >= end {
                if shortened {
                    annotation.start.saturating_sub(len_differences)
                } else {
                    annotation.start.saturating_add(len_differences)
                }
            } else if annotation.start >= start {
                if shortened {
                    max(start, annotation.start.saturating_sub(len_differences))
                } else {
                    min(end, annotation.start.saturating_add(len_differences))
                }
            } else {
                annotation.start
            };

            annotation.end = if annotation.end >= end {
                if shortened {
                    annotation.end.saturating_sub(len_differences)
                } else {
                    annotation.end.saturating_add(len_differences)
                }
            } else if annotation.end >= start {
                if shortened {
                    max(start, annotation.end.saturating_sub(len_differences))
                } else {
                    min(end, annotation.end.saturating_add(len_differences))
                }
            } else {
                annotation.end
            }
        });

        self.annotattions.retain(|annotation| {
            annotation.start < annotation.end && annotation.start < self.string.len()
        });
    }

    pub fn truncate_left_until(&mut self, until: ByteIdx) {
        self.replace(0, until, "");
    }

    pub fn truncate_right_from(&mut self, from: ByteIdx) {
        self.replace(from, self.string.len(), "");
    }
}
