use core::fmt;

use crate::{RopeCursor, TextRange};

pub trait ParserCheckpoint: Clone + Eq + fmt::Debug + 'static {}

impl<T> ParserCheckpoint for T where T: Clone + Eq + fmt::Debug + 'static {}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SyntaxFragment<C, T, D> {
    pub range: TextRange,
    pub start_checkpoint: C,
    pub end_checkpoint: C,
    pub tree: T,
    pub diagnostics: Vec<D>,
    pub grammar_revision: u64,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseTree<T> {
    pub root: T,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseInvalidation<C> {
    pub resume_at: usize,
    pub restart_checkpoint: C,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseResult<C, T, D> {
    pub tree: ParseTree<T>,
    pub fragments: Vec<SyntaxFragment<C, T, D>>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ParseError {
    pub message: String,
}

pub type ParserResult<C, T, D> = Result<ParseResult<C, T, D>, ParseError>;

impl ParseError {
    pub fn new(message: impl Into<String>) -> Self {
        Self {
            message: message.into(),
        }
    }
}

pub trait IncrementalParser {
    type Checkpoint: ParserCheckpoint;
    type Tree: Clone + fmt::Debug + 'static;
    type Diagnostic: Clone + fmt::Debug + 'static;

    fn grammar_revision(&self) -> u64;

    fn initial_checkpoint(&self) -> Self::Checkpoint;

    fn invalidate(
        &self,
        delta_range: TextRange,
        previous_fragments: &[SyntaxFragment<Self::Checkpoint, Self::Tree, Self::Diagnostic>],
    ) -> ParseInvalidation<Self::Checkpoint>;

    fn parse(
        &self,
        cursor: RopeCursor,
        fragments: &[SyntaxFragment<Self::Checkpoint, Self::Tree, Self::Diagnostic>],
        invalidation: ParseInvalidation<Self::Checkpoint>,
    ) -> ParserResult<Self::Checkpoint, Self::Tree, Self::Diagnostic>;
}

#[cfg(test)]
mod tests {
    use std::cell::Cell;
    use std::rc::Rc;

    use ruin_reactivity::Reactor;

    use super::{
        IncrementalParser, ParseError, ParseInvalidation, ParseResult, ParseTree, SyntaxFragment,
    };
    use crate::{ReactiveRope, RopeCursor, TextRange};

    #[derive(Clone, Debug)]
    struct DummyParser {
        parse_calls: Rc<Cell<usize>>,
    }

    impl IncrementalParser for DummyParser {
        type Checkpoint = u8;
        type Tree = String;
        type Diagnostic = String;

        fn grammar_revision(&self) -> u64 {
            7
        }

        fn initial_checkpoint(&self) -> Self::Checkpoint {
            0
        }

        fn invalidate(
            &self,
            delta_range: TextRange,
            _previous_fragments: &[SyntaxFragment<
                Self::Checkpoint,
                Self::Tree,
                Self::Diagnostic,
            >],
        ) -> ParseInvalidation<Self::Checkpoint> {
            ParseInvalidation {
                resume_at: delta_range.start.saturating_sub(1),
                restart_checkpoint: 1,
            }
        }

        fn parse(
            &self,
            mut cursor: RopeCursor,
            _fragments: &[SyntaxFragment<Self::Checkpoint, Self::Tree, Self::Diagnostic>],
            invalidation: ParseInvalidation<Self::Checkpoint>,
        ) -> Result<ParseResult<Self::Checkpoint, Self::Tree, Self::Diagnostic>, ParseError>
        {
            self.parse_calls.set(self.parse_calls.get() + 1);
            let text = cursor.read_bytes(usize::MAX);
            Ok(ParseResult {
                tree: ParseTree { root: text.clone() },
                fragments: vec![SyntaxFragment {
                    range: TextRange::new(
                        invalidation.resume_at,
                        invalidation.resume_at + text.len(),
                    ),
                    start_checkpoint: invalidation.restart_checkpoint,
                    end_checkpoint: 2,
                    tree: text,
                    diagnostics: Vec::new(),
                    grammar_revision: self.grammar_revision(),
                }],
            })
        }
    }

    #[test]
    fn parse_error_constructor_sets_message() {
        let error = ParseError::new("bad");
        assert_eq!(error.message, "bad");
    }

    #[test]
    fn parser_trait_round_trip_produces_fragments() {
        let reactor = Reactor::new();
        let rope = ReactiveRope::from_text(&reactor, "{\"a\":1}");
        let parser = DummyParser {
            parse_calls: Rc::new(Cell::new(0)),
        };

        let invalidation = parser.invalidate(TextRange::new(3, 4), &[]);
        assert_eq!(invalidation.resume_at, 2);
        assert_eq!(invalidation.restart_checkpoint, 1);

        let result = parser
            .parse(rope.cursor(0), &[], invalidation)
            .expect("dummy parse should succeed");

        assert_eq!(result.tree.root, "{\"a\":1}");
        assert_eq!(result.fragments.len(), 1);
        assert_eq!(result.fragments[0].grammar_revision, 7);
        assert_eq!(parser.parse_calls.get(), 1);
    }
}
