use std::{
    fmt,
    ops::Index,
    slice::{self, SliceIndex},
};

use crate::{
    highlight::LineHighlight,
    text::{Span, Text, TextLike, TextLikeMut},
};

pub struct Line {
    pub text: Text,
    pub highlight: LineHighlight,
    pub highlight_dirty: bool,
}

impl Line {
    pub fn new() -> Self {
        Self::from(Text::new())
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.text.len()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.text.is_empty()
    }
}

impl TextLike for Line {
    type Iter<'a>
        = slice::Iter<'a, char>
    where
        Self: 'a;
    type Span<'a>
        = Span<'a>
    where
        Self: 'a;

    #[inline]
    fn display_width(&self, tab_width: usize) -> usize {
        self.text.display_width(tab_width)
    }
    #[inline]
    fn iter(&self) -> Self::Iter<'_> {
        self.text.iter()
    }
    #[inline]
    fn span<R: SliceIndex<[char], Output = [char]>>(&self, range: R) -> Self::Span<'_> {
        self.text.span(range)
    }
    #[inline]
    fn skip_to_width(&self, offset: usize, tab_width: usize) -> (Self::Span<'_>, usize, usize) {
        self.text.skip_to_width(offset, tab_width)
    }
}

impl TextLikeMut for Line {
    fn split_off(&mut self, at: usize) -> Self {
        let tail = self.text.split_off(at);
        self.highlight_dirty = true;
        Self::from(tail)
    }
    fn extend(&mut self, other: Self) {
        self.text.extend(other.text);
        self.highlight_dirty = true;
    }
    fn remove(&mut self, at: usize) {
        self.text.remove(at);
        self.highlight_dirty = true;
    }
    fn insert(&mut self, at: usize, ch: char) {
        self.text.insert(at, ch);
        self.highlight_dirty = true;
    }
}

impl From<Text> for Line {
    fn from(text: Text) -> Self {
        Self {
            text,
            highlight: LineHighlight::default(),
            highlight_dirty: true,
        }
    }
}

impl Index<usize> for Line {
    type Output = char;

    fn index(&self, index: usize) -> &Self::Output {
        &self.text[index]
    }
}

impl fmt::Display for Line {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.text, f)
    }
}
