use crate::*;
use SyntacticTokenVariant as SynVar;

use std::collections::VecDeque;

use indexmap::IndexMap;


macro_rules! fn_is_syn_variant {
    ($name:ident, $variant:ty) => { paste::paste! {
        fn [< is_ $name >](token: &SyntacticToken) -> bool {
            match token.variant { $variant => true, _ => false, }
    } } }; }
fn_is_syn_variant!(block_open, SyntacticTokenVariant::BlockOpen);
fn_is_syn_variant!(block_close, SyntacticTokenVariant::BlockClose);
fn_is_syn_variant!(separator, SyntacticTokenVariant::Separator);
fn_is_syn_variant!(terminator, SyntacticTokenVariant::MacroDefinitionTerminator);


pub struct SemanticParser {
    tokens: Tokens,
    macro_definitions: IndexMap<String, MacroDefinition>,
    label_definitions: IndexMap<String, LabelDefinition>,
    body: Vec<SemanticToken>,
}

impl SemanticParser {
    pub fn new(syntactic_tokens: Vec<SyntacticToken>) -> Self {
        // Gather all labels ahead of time.
        let mut label_definitions = IndexMap::new();
        for token in &syntactic_tokens {
            if let SyntacticTokenVariant::LabelDefinition(name) = &token.variant {
                let definition = LabelDefinition {
                    source: token.source.clone(),
                    name: name.clone(),
                };
                let None = label_definitions.insert(name.to_string(), definition) else {
                    unreachable!("Duplicate definition for label {name:?}");
                };
            }
        }
        Self {
            tokens: Tokens::new(syntactic_tokens),
            macro_definitions: IndexMap::new(),
            label_definitions,
            body: Vec::new(),
        }
    }

    pub fn parse(mut self) -> SemanticProgram {
        while let Some(syn) = self.tokens.pop() {
            match syn.variant {
                SynVar::MacroDefinition(name) => {
                    let Ok(definition_tokens) = self.tokens.pull_until(is_terminator) else {
                        let variant = SemanticParseErrorVariant::UnterminatedMacroDefinition(name);
                        let error = SemanticParseError { source: syn.source, variant };
                        self.body.push(SemanticToken::Error(error));
                        break;
                    };
                    let definition = MacroDefinitionParser::new(syn.source, definition_tokens).parse();
                    let None = self.macro_definitions.insert(name.clone(), definition) else {
                        unreachable!("Duplicate definition for macro {name}");
                    };
                }
                SynVar::LabelDefinition(name) => {
                    let label_definition = LabelDefinition { source: syn.source, name };
                    self.body.push(SemanticToken::LabelDefinition(label_definition));
                }
                SynVar::PinnedAddress(address) => {
                    let pinned_address = PinnedAddress { source: syn.source, address };
                    self.body.push(SemanticToken::PinnedAddress(pinned_address));
                }
                SynVar::Symbol(name) => {
                    let invocation = InvocationParser::new(name, syn.source, &mut self.tokens).parse();
                    self.body.push(SemanticToken::Invocation(invocation));
                }
                SynVar::PackedBinaryLiteral(pbl) => {
                    self.body.push(SemanticToken::Word(pbl));
                }
                _ => {
                    let variant = SemanticParseErrorVariant::InvalidToken;
                    let error = SemanticParseError { source: syn.source, variant };
                    self.body.push(SemanticToken::Error(error));
                }
            }
        }

        SemanticProgram {
            macro_definitions: self.macro_definitions,
            label_definitions: self.label_definitions,
            body: self.body,
        }
    }
}


pub struct MacroDefinitionParser {
    source: SourceSpan,
    tokens: Tokens,
    arguments: Vec<ArgumentDefinition>,
    errors: Vec<SemanticParseError>,
}

impl MacroDefinitionParser {
    pub fn new(source: SourceSpan, tokens: Tokens) -> Self {
        Self {
            tokens,
            source,
            arguments: Vec::new(),
            errors: Vec::new(),
        }
    }

    pub fn parse(mut self) -> MacroDefinition {
        while let Some(definition) = self.parse_argument_definition() {
            self.arguments.push(definition)
        }
        MacroDefinition {
            value: self.parse_body(),
            source: self.source,
            arguments: self.arguments,
            errors: self.errors,
        }
    }

    fn parse_argument_definition(&mut self) -> Option<ArgumentDefinition> {
        // Only continue if the first token is a separator.
        self.tokens.pop_if(is_separator)?;

        // Pop argument tokens.
        let is_block = match self.tokens.pop_if(is_block_open) {
            Some(_) => true,
            None => false,
        };
        let token = self.tokens.pop();
        if is_block {
            self.tokens.pop_if(is_block_close);
        }
        // Parse argument token.
        let token = token?;
        let source = token.source;
        if let SynVar::Symbol(name) = token.variant {
            let variant = match is_block {
                true => ArgumentVariant::Block,
                false => ArgumentVariant::Integer,
            };
            Some(ArgumentDefinition { name, source, variant })
        } else {
            let variant = SemanticParseErrorVariant::InvalidToken;
            self.errors.push(SemanticParseError { source, variant});
            None
        }
    }

    fn parse_body(&mut self) -> Value {
        // Attempt to parse an Integer.
        if self.tokens.len() == 1 {
            let token = self.tokens.pop().unwrap();
            match token.variant {
                SynVar::IntegerLiteral(value) => {
                    let integer = TrackedInteger { source: token.source, value };
                    return Value::Integer(Integer::Literal(integer));
                }
                SynVar::Expression(expr) => {
                    return Value::Integer(Integer::Expression(expr));
                }
                _ => (),
            }
            self.tokens.unpop(token);
        }
        // Parse a Block.
        let mut block = BlockParser::new(self.tokens.take()).parse();
        // If the block contains a single invocation, unwrap it.
        if block.len() == 1 {
            match block.pop() {
                Some(SemanticToken::Invocation(invocation)) => return Value::Invocation(invocation),
                Some(other) => block.push(other),
                None => (),
            };
        }
        return Value::Block(block);
    }
}


/// Parse an entire block, excluding delimiters.
pub struct BlockParser {
    tokens: Tokens,
    semantic_tokens: Vec<SemanticToken>,
}

impl BlockParser {
    pub fn new(tokens: Tokens) -> Self {
        Self { tokens, semantic_tokens: Vec::new() }
    }

    pub fn parse(mut self) -> Vec<SemanticToken> {
        while let Some(token) = self.tokens.pop() {
            let source = token.source;
            match token.variant {
                SynVar::Symbol(name) => {
                    let invocation = InvocationParser::new(name, source, &mut self.tokens).parse();
                    self.semantic_tokens.push(SemanticToken::Invocation(invocation));
                }
                SynVar::PackedBinaryLiteral(pbl) => {
                    self.semantic_tokens.push(SemanticToken::Word(pbl));
                }
                SynVar::LabelDefinition(name) => {
                    let label_definition = LabelDefinition { source, name };
                    self.semantic_tokens.push(SemanticToken::LabelDefinition(label_definition));
                }
                _ => {
                    let variant = SemanticParseErrorVariant::InvalidToken;
                    let error = SemanticParseError { source, variant };
                    self.semantic_tokens.push(SemanticToken::Error(error));
                }
            }
        }
        return self.semantic_tokens;
    }
}


struct InvocationParser<'a> {
    name: String,
    source: SourceSpan,
    tokens: &'a mut Tokens,
    arguments: Vec<ArgumentInvocation>,
    errors: Vec<SemanticParseError>,
}

impl<'a> InvocationParser<'a> {
    pub fn new(name: String, source: SourceSpan, tokens: &'a mut Tokens) -> Self {
        Self { name, source, tokens, arguments: Vec::new(), errors: Vec::new() }
    }

    pub fn parse(mut self) -> Invocation {
        while let Some(argument) = self.parse_invocation_argument() {
            self.arguments.push(argument);
        }
        Invocation {
            name: self.name,
            source: self.source,
            arguments: self.arguments,
            errors: self.errors,
        }
    }

    fn parse_invocation_argument(&mut self) -> Option<ArgumentInvocation> {
        // Only continue if the first token is a separator.
        self.tokens.pop_if(is_separator)?;

        if let Some(block_open) = self.tokens.pop_if(is_block_open) {
            let source = block_open.source;
            let mut depth = 1;
            let is_matching_block_close = |token: &SyntacticToken| {
                match token.variant {
                    SyntacticTokenVariant::BlockOpen => {
                        depth += 1; false }
                    SyntacticTokenVariant::BlockClose => {
                        depth -= 1; depth == 0 }
                    _ => false,
                }
            };
            if let Ok(block_tokens) = self.tokens.pull_until(is_matching_block_close) {
                let block = BlockParser::new(block_tokens).parse();
                Some(ArgumentInvocation { source, value: Value::Block(block) })
            } else {
                let variant = SemanticParseErrorVariant::UnterminatedBlock;
                self.errors.push(SemanticParseError { source, variant });
                None
            }
        } else {
            let token = self.tokens.pop()?;
            let source = token.source;
            match token.variant {
                SynVar::Symbol(name) => {
                    let arguments = Vec::new();
                    let errors = Vec::new();
                    let invocation = Invocation { source: source.clone(), name, arguments, errors };
                    let value = Value::Invocation(invocation);
                    Some(ArgumentInvocation { source, value })
                }
                SynVar::IntegerLiteral(value) => {
                    let integer = TrackedInteger { source: source.clone(), value };
                    let value = Value::Integer(Integer::Literal(integer));
                    Some(ArgumentInvocation { source, value })
                }
                SynVar::String(string) => {
                    let value = Value::Integer(Integer::String(string));
                    Some(ArgumentInvocation { source, value })
                }
                SynVar::Expression(expr) => {
                    let value = Value::Integer(Integer::Expression(expr));
                    Some(ArgumentInvocation { source, value })
                }
                _ => {
                    let variant = SemanticParseErrorVariant::InvalidToken;
                    self.errors.push(SemanticParseError { source, variant });
                    None
                }
            }
        }
    }
}


pub struct Tokens {
    tokens: VecDeque<SyntacticToken>,
}

impl Tokens {
    pub fn new<T: Into<VecDeque<SyntacticToken>>>(tokens: T) -> Self {
        Self { tokens: tokens.into() }
    }

    pub fn pop(&mut self) -> Option<SyntacticToken> {
        self.tokens.pop_front()
    }

    pub fn pop_if(&mut self, predicate: fn(&SyntacticToken) -> bool) -> Option<SyntacticToken> {
        match predicate(self.tokens.front()?) {
            true => self.tokens.pop_front(),
            false => None,
        }
    }

    pub fn unpop(&mut self, token: SyntacticToken) {
        self.tokens.push_front(token);
    }

    /// Pull tokens until the predicate returns true, otherwise return Err.
    pub fn pull_until(&mut self, mut predicate: impl FnMut(&SyntacticToken) -> bool) -> Result<Self, ()> {
        let mut output = VecDeque::new();
        while let Some(token) = self.tokens.pop_front() {
            match predicate(&token) {
                true => return Ok(Self::new(output)),
                false => output.push_back(token),
            };
        }
        return Err(());
    }

    pub fn take(&mut self) -> Self {
        Self { tokens: std::mem::take(&mut self.tokens) }
    }

    pub fn len(&self) -> usize {
        self.tokens.len()
    }
}