//! 资源管理：model3.json 解析 + PNG 纹理加载

use anyhow::{anyhow, Context, Result};
use serde::Deserialize;
use std::path::{Path, PathBuf};

/// model3.json 中的 Groups 条目
#[derive(Deserialize, Clone, Debug)]
pub struct ModelGroup {
    #[serde(rename = "Target")]
    pub target: String,
    #[serde(rename = "Name")]
    pub name: String,
    #[serde(rename = "Ids")]
    pub ids: Vec<String>,
}

/// model3.json 文件引用
#[derive(Deserialize, Clone, Debug)]
pub struct FileReferences {
    #[serde(rename = "Moc")]
    pub moc: String,
    #[serde(rename = "Textures", default)]
    pub textures: Vec<String>,
    #[serde(rename = "Physics", default)]
    pub physics: Option<String>,
    #[serde(rename = "Pose", default)]
    pub pose: Option<String>,
    #[serde(rename = "DisplayInfo", default)]
    pub display_info: Option<String>,
}

/// model3.json 根结构
#[derive(Deserialize, Clone, Debug)]
pub struct Model3Json {
    #[serde(rename = "Version")]
    pub version: i32,
    #[serde(rename = "FileReferences")]
    pub file_references: FileReferences,
    #[serde(rename = "Groups", default)]
    pub groups: Vec<ModelGroup>,
}

impl Model3Json {
    /// 从文件解析
    pub fn from_path(path: &Path) -> Result<Self> {
        let text = std::fs::read_to_string(path)
            .with_context(|| format!("读取 model3.json 失败: {}", path.display()))?;
        let json: Model3Json = serde_json::from_str(&text)
            .with_context(|| format!("解析 model3.json 失败: {}", path.display()))?;
        Ok(json)
    }

    /// 获取 LipSync 参数 ID 列表
    pub fn lip_sync_ids(&self) -> Vec<String> {
        self.groups
            .iter()
            .find(|g| g.target == "Parameter" && g.name == "LipSync")
            .map(|g| g.ids.clone())
            .unwrap_or_else(|| vec!["ParamMouthOpenY".to_string()])
    }

    /// 获取 EyeBlink 参数 ID 列表
    pub fn eye_blink_ids(&self) -> Vec<String> {
        self.groups
            .iter()
            .find(|g| g.target == "Parameter" && g.name == "EyeBlink")
            .map(|g| g.ids.clone())
            .unwrap_or_else(|| {
                vec!["ParamEyeLOpen".to_string(), "ParamEyeROpen".to_string()]
            })
    }
}

/// 加载的纹理图像（RGBA）
pub struct TextureImage {
    pub width: u32,
    pub height: u32,
    pub rgba: Vec<u8>,
}

/// 加载 PNG 纹理（使用 image crate）
pub fn load_texture(path: &Path) -> Result<TextureImage> {
    let img = image::open(path)
        .with_context(|| format!("加载纹理失败: {}", path.display()))?
        .to_rgba8();
    let width = img.width();
    let height = img.height();
    let rgba = img.into_raw();
    Ok(TextureImage {
        width,
        height,
        rgba,
    })
}

/// 加载 model3.json 引用的所有纹理（返回按 texture_00, texture_01 顺序的列表）
pub fn load_all_textures(
    model3_dir: &Path,
    file_refs: &FileReferences,
) -> Result<Vec<TextureImage>> {
    let mut textures = Vec::new();
    for tex_rel in &file_refs.textures {
        let tex_path = model3_dir.join(tex_rel);
        let img = load_texture(&tex_path)?;
        println!(
            "[Live2D] 纹理加载: {} ({}x{})",
            tex_rel, img.width, img.height
        );
        textures.push(img);
    }
    Ok(textures)
}

/// 解析 model3.json 并返回完整路径集
pub struct ModelAssetPaths {
    pub model3_json: PathBuf,
    pub model3_dir: PathBuf,
    pub moc3: PathBuf,
    pub textures: Vec<PathBuf>,
}

/// 从 model3.json 相对路径（如 "haru/Haru.model3.json"）解析资源路径
pub fn resolve_asset_paths(
    live2d_base_dir: &Path,
    model3_json_rel: &str,
) -> Result<ModelAssetPaths> {
    let model3_json = live2d_base_dir.join(model3_json_rel);
    if !model3_json.exists() {
        return Err(anyhow!(
            "model3.json 不存在: {}",
            model3_json.display()
        ));
    }
    let model3_dir = model3_json
        .parent()
        .ok_or_else(|| anyhow!("无法获取 model3.json 的父目录"))?
        .to_path_buf();

    let model3 = Model3Json::from_path(&model3_json)?;
    let moc3 = model3_dir.join(&model3.file_references.moc);
    let textures = model3
        .file_references
        .textures
        .iter()
        .map(|t| model3_dir.join(t))
        .collect();

    Ok(ModelAssetPaths {
        model3_json,
        model3_dir,
        moc3,
        textures,
    })
}
