late-java-core 2.2.9

A Rust library for launching Minecraft Java Edition
use crate::error::{Result, LateJavaCoreError};
use crate::minecraft_loader::{LoaderDownloader, LoaderType};
use std::path::Path;

/// Opciones del loader
#[derive(Debug, Clone)]
pub struct LoaderOptions {
    pub path: String,
    pub loader: LoaderConfig,
    pub download_file_multiple: Option<u32>,
}

#[derive(Debug, Clone)]
pub struct LoaderConfig {
    pub path: Option<String>,
    pub loader_type: Option<String>,
    pub build: Option<String>,
}

/// JSON de loader
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LoaderJson {
    pub libraries: Vec<LoaderLibrary>,
    pub arguments: Option<LoaderArguments>,
    pub main_class: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LoaderLibrary {
    pub loader: Option<String>,
    pub name: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct LoaderArguments {
    pub game: Option<Vec<String>>,
    pub jvm: Option<Vec<String>>,
}

/// Clase para manejar loaders de Minecraft
pub struct MinecraftLoader {
    options: LoaderOptions,
    loader_path: String,
}

impl MinecraftLoader {
    pub fn new(options: LoaderOptions) -> Self {
        let loader_path = Path::new(&options.path)
            .join(options.loader.path.as_deref().unwrap_or("./loader"))
            .to_string_lossy()
            .to_string();

        Self {
            options,
            loader_path,
        }
    }

    /// Instalar el loader para una versión de Minecraft dada
    pub async fn get_loader(&self, version: &str, java_path: &str) -> Result<LoaderJson> {
        let loader_type = self.options.loader.loader_type.as_deref()
            .ok_or_else(|| LateJavaCoreError::Minecraft("Loader type not specified".to_string()))?;

        let loader_type_enum = match loader_type.to_lowercase().as_str() {
            "forge" => LoaderType::Forge,
            "neoforge" => LoaderType::NeoForge,
            "fabric" => LoaderType::Fabric,
            "legacyfabric" => LoaderType::LegacyFabric,
            "quilt" => LoaderType::Quilt,
            _ => return Err(LateJavaCoreError::Minecraft(format!("Unknown loader type: {}", loader_type))),
        };

        let loader = LoaderDownloader::new(crate::minecraft_loader::LoaderDownloaderOptions {
            path: self.loader_path.clone(),
            download_file_multiple: self.options.download_file_multiple,
            loader: crate::minecraft_loader::LoaderConfig {
                loader_type: loader_type_enum,
                version: version.to_string(),
                build: self.options.loader.build.clone(),
                config: crate::minecraft_loader::LoaderConfigData {
                    java_path: java_path.to_string(),
                    minecraft_jar: format!("{}/versions/{}/{}.jar", self.options.path, version, version),
                    minecraft_json: format!("{}/versions/{}/{}.json", self.options.path, version, version),
                },
            },
        });

        let json = loader.install().await?;

        // Inyectar la ruta del loader en cada librería si es necesario
        let mut modified_json = json;
        for lib in &mut modified_json.libraries {
            lib.loader = Some(self.loader_path.clone());
        }

        Ok(modified_json)
    }

    /// Construir argumentos del juego y JVM basados en los datos JSON del loader
    pub async fn get_arguments(&self, json: Option<&LoaderJson>, version: &str) -> Result<LoaderArgumentsResult> {
        // Si no se proporciona JSON de loader, devolver arrays vacíos
        if json.is_none() {
            return Ok(LoaderArgumentsResult {
                game: Vec::new(),
                jvm: Vec::new(),
                main_class: None,
            });
        }

        let json = json.unwrap();
        let modded_args = &json.arguments;
        
        // Si no hay campo de argumentos presente, devolver arrays vacíos
        if modded_args.is_none() {
            return Ok(LoaderArgumentsResult {
                game: Vec::new(),
                jvm: Vec::new(),
                main_class: None,
            });
        }

        let modded_args = modded_args.unwrap();
        let mut args = LoaderArgumentsResult {
            game: modded_args.game.clone().unwrap_or_default(),
            jvm: Vec::new(),
            main_class: json.main_class.clone(),
        };

        if let Some(jvm_args) = &modded_args.jvm {
            // Reemplazar placeholders en los argumentos JVM
            args.jvm = jvm_args.iter()
                .map(|jvm_arg| {
                    jvm_arg
                        .replace("${version_name}", version)
                        .replace("${library_directory}", &format!("{}/libraries", self.loader_path))
                        .replace("${classpath_separator}", if std::env::consts::OS == "windows" { ";" } else { ":" })
                })
                .collect();
        }

        Ok(args)
    }
}

#[derive(Debug, Clone)]
pub struct LoaderArgumentsResult {
    pub game: Vec<String>,
    pub jvm: Vec<String>,
    pub main_class: Option<String>,
}