use crate::{ir, utility::data_type};
use std::{collections::HashMap, str};
mod function;
mod register_assign;
#[derive(Debug, Default)]
pub struct Context {
    pub struct_definitions: HashMap<String, ir::TypeDefinition>,
}
pub trait HasSize {
    fn size(&self, ctx: &Context) -> usize;
}
impl HasSize for data_type::Type {
    fn size(&self, ctx: &Context) -> usize {
        match self {
            data_type::Type::Integer(integer) => integer.width,
            data_type::Type::StructRef(name) => {
                let struct_definition = ctx.struct_definitions.get(name).unwrap();
                struct_definition
                    .fields
                    .iter()
                    .map(|field_type| field_type.size(ctx))
                    .sum()
            }
            data_type::Type::None => 0,
            data_type::Type::Address => 32,
        }
    }
}
impl Context {
    pub fn field_offset(&self, struct_name: &str, field_index: usize) -> usize {
        let struct_definition = self.struct_definitions.get(struct_name).unwrap();
        let mut offset = 0;
        for i in 0..field_index {
            offset += struct_definition.fields[i].size(self);
        }
        offset
    }
}
pub fn emit_asm(ir: &[ir::IR]) -> String {
    let mut code = ".section .text\n".to_string();
    let mut ctx = Context {
        struct_definitions: HashMap::new(),
    };
    for ir in ir {
        match ir {
            ir::IR::FunctionDefinition(function_definition) => {
                code.push_str(function::emit_code(function_definition, &mut ctx).as_str());
            }
            ir::IR::TypeDefinition(type_definition) => {
                ctx.struct_definitions
                    .insert(type_definition.name.clone(), type_definition.clone());
            }
            ir::IR::GlobalDefinition(_) => todo!(),
        }
    }
    code
}