use std::fmt;

use serde_derive::Serialize;

use super::fmt::{Span, TokenFmt};

#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(transparent)]
pub struct DocString {
    pub text: String,
}

impl DocString {
    pub fn new(text: impl Into<String>) -> DocString {
        DocString { text: text.into() }
    }
}

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

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
enum DocState {
    Plain,
    Link,
    Date,
}

struct DocParser<'a> {
    tokens: Vec<Span<'a>>,
    current: String,
    state: DocState,
}

impl<'a> DocParser<'a> {
    fn finish(&mut self) {
        if !self.current.is_empty() {
            let current = std::mem::replace(&mut self.current, String::new());
            match self.state {
                DocState::Plain => self.tokens.push(Span::doc_string(current)),
                DocState::Date => self.tokens.push(Span::date_time(current)),
                DocState::Link => self.tokens.push(Span::link(current)),
            }
            self.state = DocState::Plain;
        }
    }
}

impl<'a> TokenFmt<'a> for DocString {
    fn to_spans(&'a self) -> Vec<Span<'a>> {
        let mut parser = DocParser {
            tokens: vec![],
            current: String::new(),
            state: DocState::Plain,
        };

        for ch in self.text.chars() {
            match ch {
                '>' if parser.state == DocState::Link => parser.finish(),
                _ if parser.state == DocState::Link => parser.current.push(ch),
                '#' if parser.state == DocState::Date => parser.finish(),
                _ if parser.state == DocState::Date => parser.current.push(ch),
                '<' => {
                    parser.finish();
                    parser.state = DocState::Link;
                }
                '#' => {
                    parser.finish();
                    parser.state = DocState::Date;
                }
                _ => parser.current.push(ch),
            }
        }
        parser.finish();

        parser.tokens
    }
}
