pub mod gtk;

use bstr::ByteSlice;

#[derive(Debug)]
pub enum Style {
    Highlight,
    Color(u32),
    Italic,
    Monospace,
    Strikethrough,
}

#[derive(Debug)]
struct Attr {
    start: usize,
    end: Option<usize>,
    style: Style,
}

#[derive(Debug, Default)]
pub struct Formatted {
    text: String,
    attrs: Vec<Attr>,
}

pub struct FormattedSlice<'a> {
    text: &'a str,
    attrs: &'a [Attr],
}

pub(crate) trait PushToString {
    fn push_to_string(self, dest: &mut String);
}


impl Formatted {
    pub(crate) fn ends_with_nl(&self) -> bool {
        self.text.ends_with('\n')
    }

    pub(crate) fn push(&mut self, text: impl PushToString) {
        text.push_to_string(&mut self.text);
    }

    pub(crate) fn push_style(&mut self, text: impl PushToString, style: Style) {
        let start = self.text.len();
        text.push_to_string(&mut self.text);
        let end = Some(self.text.len());
        self.attrs.push(Attr {
            start,
            end,
            style,
        });
    }

    pub(crate) fn push_styles(
        &mut self,
        text: impl PushToString,
        styles: impl IntoIterator<Item = Style>,
    ) {
        let start = self.text.len();
        text.push_to_string(&mut self.text);
        let end = Some(self.text.len());
        self.attrs.extend(styles.into_iter().map(|style|
            Attr {
                start,
                end,
                style,
            }
        ));
    }

    pub(crate) fn from_string(text: String) -> Self {
        Self {
            text,
            attrs: Vec::new(),
        }
    }

    pub(crate) fn from_string_style(text: String, style: Style) -> Self {
        let attr = Attr {
            start: 0,
            end: Some(text.len()),
            style,
        };
        Self {
            text,
            attrs: vec![attr],
        }
    }

    pub fn as_str(&self) -> &str {
        &self.text
    }

    fn as_slice(&self) -> FormattedSlice<'_> {
        FormattedSlice {
            text: &self.text,
            attrs: &self.attrs,
        }
    }

    pub(crate) fn as_slice_nonl(&self) -> FormattedSlice<'_> {
        FormattedSlice {
            text: &self.text.trim_end_matches('\n'),
            attrs: &self.attrs,
        }
    }
}

impl From<String> for Formatted {
    fn from(value: String) -> Self {
        Self::from_string(value)
    }
}

impl<'a> FormattedSlice<'a> {
    pub(crate) fn from_str(text: &'a str) -> Self {
        static EMPTY: [Attr; 0] = [];
        Self {
            text,
            attrs: &EMPTY,
        }
    }

    pub(crate) fn from_str_highlighted(text: &'a str) -> Self {
        static HIGHLIGHTED: [Attr; 1] = [Attr {
            start: 0,
            end: None,
            style: Style::Highlight,
        }];
        Self {
            text,
            attrs: &HIGHLIGHTED,
        }
    }

}

impl PushToString for &str {
    fn push_to_string(self, dest: &mut String) {
        dest.push_str(self);
    }
}

impl PushToString for &[u8] {
    fn push_to_string(self, dest: &mut String) {
        dest.push_str(&self.to_str_lossy());
    }
}

impl PushToString for &Vec<u8> {
    fn push_to_string(self, dest: &mut String) {
        dest.push_str(&self.as_slice().to_str_lossy());
    }
}

impl PushToString for &[char] {
    fn push_to_string(self, dest: &mut String) {
        dest.extend(self);
    }
}
