// rasm-core/src/codegen/aot/mod.rs
//
//! # AOT-компилятор RASM
//!
//! | Формат  | Платформа | Модуль      |
//! |---------|-----------|-------------|
//! | ELF64   | Linux     | `elf.rs`    |
//! | PE32+   | Windows   | `pe.rs`     |
//! | Mach-O  | macOS     | `mach_o.rs` |
pub mod coff;
pub mod elf;
pub mod mach_o;
pub mod pe;

/// Форматы для записи бинарного кода в файл
#[derive(Debug, Clone, Copy)]
pub enum Formats {
    /// # Бинарный формат (`.bin`)
    ///
    /// Просто сырой машинный код всех секций модуля подряд (в порядке
    /// `module.sections`), без заголовков и метаданных — не исполняемый
    /// напрямую операционной системой, в основном нужен для создания ОС
    /// (например, как payload для собственного загрузчика/бутлоадера).
    BIN,

    /// # PE (`.exe`)
    ///
    /// Формат для исполняемых файлов на Windows.
    PE,

    /// # ELF
    ///
    /// Формат для исполняемых файлов на Linux.
    /// У итогового файла может быть любое расширение.
    ELF,

    /// # Mach-O
    ///
    /// Формат для исполняемых файлов на macOS.
    MachO,

    /// # COFF64 (`.obj`)
    ///
    /// Объектный файл для Windows (x86_64). Содержит секции, символы и
    /// релокации. Может быть передан линкеру (link.exe / lld-link).
    COFF,
}

use std::io::Write;

pub use coff::COFFWriter;
pub use elf::ElfWriter;
pub use mach_o::MachOWriter;
pub use pe::PeWriter;

use crate::object::CompiledModule;

/// Записывает исполняемый файл в формате текущей платформы из готового
/// [`CompiledModule`] (секции, символы, релокации, точка входа).
///
/// На Linux — ELF64, на Windows — PE32+. Оба формата сами вычисляют адреса
/// загрузки и патчат `Abs64`-релокации (`.quad my_label`) с учётом реальной
/// базы образа — раньше это патчилось заранее в кодогенераторе с допущением
/// `base = 0`, что давало неверный адрес для AOT-целей.
pub fn write_native_executable(
    module: &CompiledModule,
    path: &str,
    file_format: Formats,
) -> std::io::Result<()> {
    use std::fs::File;

    let mut file = File::create(path)?;

    match file_format {
        Formats::BIN => {
            for section in &module.sections {
                file.write_all(&section.bytes)?;
            }
            Ok(())
        }
        Formats::PE => {
            let writer = PeWriter::new(module);
            writer.write(&mut file)
        }
        Formats::ELF => {
            let writer = ElfWriter::new(module);
            writer.write(&mut file)?;

            // chmod +x на Unix
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt;
                if let Ok(meta) = std::fs::metadata(path) {
                    let mut perms = meta.permissions();
                    perms.set_mode(perms.mode() | 0o111);
                    let _ = std::fs::set_permissions(path, perms);
                }
            }

            Ok(())
        }
        Formats::COFF => {
            let writer = COFFWriter::new(module);
            writer.write(&mut file)
        }
        Formats::MachO => {
            let writer = MachOWriter::new(module);
            writer.write(&mut file)?;

            // chmod +x на Unix
            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt;
                if let Ok(meta) = std::fs::metadata(path) {
                    let mut perms = meta.permissions();
                    perms.set_mode(perms.mode() | 0o111);
                    let _ = std::fs::set_permissions(path, perms);
                }
            }

            Ok(())
        }
    }
}
