pub mod global;
pub mod local;
pub use crate::ir::quantity::{global::GlobalVariableName, local::RegisterName};
use crate::utility::parsing;
use enum_dispatch::enum_dispatch;
use nom::{branch::alt, combinator::map, IResult};
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
#[enum_dispatch]
trait IsQuantity {}
#[enum_dispatch(IsQuantity)]
#[derive(Debug, Eq, PartialEq, Clone, Hash, Deserialize, Serialize)]
pub enum Quantity {
    RegisterName,
    GlobalVariableName,
    NumberLiteral(i64),
}
impl Quantity {
    pub fn unwrap_local(self) -> RegisterName {
        if let Quantity::RegisterName(register_name) = self {
            register_name
        } else {
            panic!("Not local")
        }
    }
    pub fn as_local(&self) -> Option<&RegisterName> {
        if let Quantity::RegisterName(register_name) = self {
            Some(register_name)
        } else {
            None
        }
    }
}
impl Display for Quantity {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Quantity::GlobalVariableName(global) => write!(f, "{global}"),
            Quantity::RegisterName(local) => write!(f, "{local}"),
            Quantity::NumberLiteral(number) => write!(f, "{number}"),
        }
    }
}
pub fn parse(code: &str) -> IResult<&str, Quantity> {
    alt((
        map(local::parse, Quantity::RegisterName),
        map(global::parse, Quantity::GlobalVariableName),
        map(parsing::integer, Quantity::NumberLiteral),
    ))(code)
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_parse() {
        let result = parse("%foo").unwrap().1;
        assert_eq!(
            result,
            Quantity::RegisterName(RegisterName("foo".to_string()))
        );
        let result = parse("%0").unwrap().1;
        assert_eq!(
            result,
            Quantity::RegisterName(RegisterName("0".to_string()))
        );
        let result = parse("@foo").unwrap().1;
        assert_eq!(
            result,
            Quantity::GlobalVariableName(GlobalVariableName("foo".to_string()))
        );
        let result = parse("123").unwrap().1;
        assert_eq!(result, Quantity::NumberLiteral(123));
    }
}