use drop_bomb::DropBomb;

use self::{
    error::SyntaxError,
    events::{Event, NodeKind},
    input::Input,
    syntax_kind::SyntaxKind,
};
use std::cell::Cell;

pub mod syntax_kind;
#[cfg(test)]
mod tests;

pub mod error;
pub mod events;
pub mod grammar;
pub mod input;
pub mod output;

const PARSER_STEP_LIMIT: u32 = 4096;

pub struct Parser<'src, 'toks> {
    input: Input<'src, 'toks>,
    pos: usize,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'src, 'toks> Parser<'src, 'toks> {
    pub fn new(input: Input<'src, 'toks>) -> Self {
        Self {
            input,
            pos: 0,
            events: Vec::new(),
            steps: Cell::new(0),
        }
    }

    pub fn finish(self) -> Vec<Event> {
        self.events
    }

    pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
        self.step();
        self.input.kind(self.pos + n)
    }

    pub fn eat_succeeding_ws(&mut self) {
        self.push_ev(Event::Eat {
            count: self.input.meaningless_tail_len(),
        });
    }

    pub(crate) fn current(&self) -> SyntaxKind {
        self.step();
        self.input.kind(self.pos)
    }

    pub(crate) fn start(&mut self, name: &str) -> Marker {
        let pos = self.events.len();
        self.push_ev(Event::tombstone());
        Marker::new(pos, name)
    }

    pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
        self.nth_at(0, kind)
    }

    pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
        if !self.at(kind) {
            return false;
        }

        self.do_bump();
        true
    }

    pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
        self.nth(n) == kind
    }

    fn do_bump(&mut self) {
        self.push_ev(Event::Eat {
            count: self.input.preceding_meaningless(self.pos),
        });
        self.pos += 1;
    }

    fn push_ev(&mut self, event: Event) {
        self.events.push(event)
    }

    fn step(&self) {
        let steps = self.steps.get();
        assert!(steps <= PARSER_STEP_LIMIT, "the parser seems stuck...");
        self.steps.set(steps + 1);
    }
}

pub(crate) struct Marker {
    pos: usize,
    bomb: DropBomb,
}

impl Marker {
    pub(crate) fn new(pos: usize, name: &str) -> Self {
        Self {
            pos,
            bomb: DropBomb::new(format!("Marker {name} must be completed or abandoned")),
        }
    }

    fn close_node(mut self, p: &mut Parser, kind: NodeKind) -> CompletedMarker {
        self.bomb.defuse();
        match &mut p.events[self.pos] {
            Event::Start { kind: slot, .. } => *slot = kind.clone(),
            _ => unreachable!(),
        }

        p.push_ev(Event::Finish);

        CompletedMarker {
            pos: self.pos,
            kind,
        }
    }

    pub(crate) fn complete(self, p: &mut Parser<'_, '_>, kind: SyntaxKind) -> CompletedMarker {
        self.close_node(p, NodeKind::Syntax(kind))
    }

    pub(crate) fn error(self, p: &mut Parser, kind: SyntaxError) -> CompletedMarker {
        self.close_node(p, NodeKind::Error(kind))
    }

    pub(crate) fn abandon(mut self, p: &mut Parser<'_, '_>) {
        self.bomb.defuse();
        if self.pos == p.events.len() - 1 {
            match p.events.pop() {
                Some(Event::Start {
                    kind: NodeKind::Syntax(SyntaxKind::TOMBSTONE),
                    forward_parent: None,
                }) => (),
                _ => unreachable!(),
            }
        }
    }
}

pub(crate) struct CompletedMarker {
    pos: usize,
    kind: NodeKind,
}

impl CompletedMarker {
    pub(crate) fn precede(self, p: &mut Parser<'_, '_>, name: &str) -> Marker {
        let new_pos = p.start(name);

        match &mut p.events[self.pos] {
            Event::Start { forward_parent, .. } => {
                *forward_parent = Some(new_pos.pos - self.pos);
            }
            _ => unreachable!(),
        }

        new_pos
    }
}
