use std::path::PathBuf;

use common::asm::{AsmInstruction, AsmOpcode};

use crate::assembler::Symbol;

use crate::{expect_tt, expect_value};

use super::{Token, error::AssembleError};

pub struct AsmNode {
    pub label: Option<Symbol>,
    pub instruction: Option<AsmInstruction>,
}

pub struct Parser {
    tokens: Vec<Token>,
    nodes: Option<Vec<AsmInstruction>>,
    dependencies: Vec<PathBuf>,
}

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Self {
        Parser {
            tokens,
            nodes: Some(vec![]),
            dependencies: vec![],
        }
    }

    pub fn parse(&mut self) -> Result<Vec<AsmInstruction>, AssembleError> {
        Ok(vec![])
    }

    fn parse_instruction(&mut self) -> Result<AsmNode, AssembleError> {
        let label = expect_value!(self.peek_next()?, Symbol).ok();
        if label.is_some() {
            self.next()?;
        }

        let opcode = expect_value!(self.next()?, Opcode)?;

        let ins = match opcode {
            AsmOpcode::Mov => {
                let src = expect_value!(self.next()?, Register)?;
                let dest = expect_value!(self.next()?, Register)?;
                AsmInstruction::Mov { src, dest }
            }
            AsmOpcode::CMov => {
                let src = expect_value!(self.next()?, Register)?;
                let dest = expect_value!(self.next()?, Register)?;
                let condition = expect_value!(self.next()?, Register)?;
                AsmInstruction::CMov {
                    src,
                    dest,
                    condition,
                }
            }

            // AsmOpcode::Ldb => {
            //     let src = expect_value!(self.next()?, Register)?;
            //     let dest = expect_value!(self.next()?, Register)?;
            //     AsmInstruction::Ldb { src, dest }
            // }
            _ => {
                todo!()
            }
        };

        Ok(AsmNode {
            label,
            instruction: Some(ins),
        })
    }

    fn next(&mut self) -> Result<Token, AssembleError> {
        if self.tokens.is_empty() {
            Err(AssembleError::UnexpectedEof)
        } else {
            Ok(self
                .tokens
                .pop()
                .expect("tokens vector was unexpectedly empty in next()"))
        }
    }

    fn peek_next(&self) -> Result<Token, AssembleError> {
        if self.tokens.is_empty() {
            Err(AssembleError::UnexpectedEof)
        } else {
            Ok(self
                .tokens
                .last()
                .expect("peek_next called on empty tokens vector")
                .clone())
        }
    }
}

#[macro_export]
macro_rules! expect_tt {
    ($token:expr, $($variant:ident),+) => {{
        let tok = $token;
        let tt = token.tt().to_string();

        match tt.as_str() {
            $(
                stringify!($variant) => Ok(token),
            )+
            _ => {
                // let expected = format!("[{}]", vec![$(stringify!($variant)),+].join(" | "));
                Err(AssembleError::unexpected_token(
                    tok,
                    format!("[{}]", vec![$(stringify!($variant)),+].join(" | ")).as_str())
                )
            }
        }
    }};
}

#[macro_export]
macro_rules! expect_value {
    ($token:expr, $variant:ident) => {{
        let tok = $token;
        match tok.clone() {
            Token::$variant(first, ..) => Ok(first),
            _ => Err(AssembleError::unexpected_token(tok, stringify!($variant))),
        }
    }};
}
