use bit_struct::u10;

use crate::assembly_error::AssemblyError;
use crate::components::address::{AddrRepr, Address};
use crate::Labels;

/// An enum that contains the address.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Location {
    /// Specific address.
    Address(Address),
    /// Offset from the instruction's address.
    Offset(i32),
    /// Address from the label.
    Label(String)
}

impl Location {
    /// Gets the address from the location.
    pub fn get_address(&self, address: Address, labels: &Labels) -> Result<Address, AssemblyError> {
        match self {
            Location::Address(address) => Ok(*address),
            Location::Offset(offset) => {
                let address = address.0.value() as usize;

                let address = address.overflowing_add_signed(*offset as isize).0;
                let address = address.rem_euclid(AddrRepr::MAX as usize + 1);

                let address = unsafe { u10::new_unchecked(address as u16) };
                Ok(Address(address))
            },
            Location::Label(label) => {
                match labels.get(label) {
                    Some(value) => Ok(*value),
                    None => Err(AssemblyError::new(format!("Unknown label '{}'", label), None))
                }
            }
        }
    }
}
