//! Parser for ds2sf's emitted JSONs.

use anyhow::{Context as _, Result};
use serde_json::Value;
use std::fs;
use std::path::Path;

use super::{Pad, PackageInfo, Part, Pin};

pub fn parse(symbol_path: &Path, footprint_path: Option<&Path>) -> Result<Part> {
    let sym_raw = fs::read_to_string(symbol_path)
        .with_context(|| format!("reading {}", symbol_path.display()))?;
    let sym: Value = serde_json::from_str(&sym_raw).context("parsing ds2sf symbol JSON")?;

    let source_part_name = sym.get("symbolName").and_then(|v| v.as_str()).map(String::from);
    let pins: Vec<Pin> = sym
        .get("pins")
        .and_then(|v| v.as_array())
        .map(|arr| {
            arr.iter()
                .map(|p| {
                    let number = p.get("number").and_then(|v| v.as_str()).unwrap_or("").to_string();
                    let name = p.get("name").and_then(|v| v.as_str()).unwrap_or("").to_string();
                    let is_stub_name = name.is_empty() || name == number;
                    Pin { number, name, is_stub_name }
                })
                .collect()
        })
        .unwrap_or_default();

    let (pads, package) = if let Some(fp_path) = footprint_path {
        let fp_raw = fs::read_to_string(fp_path)
            .with_context(|| format!("reading {}", fp_path.display()))?;
        let fp: Value = serde_json::from_str(&fp_raw).context("parsing ds2sf footprint JSON")?;

        let pads = fp
            .get("padDescriptions")
            .and_then(|v| v.as_object())
            .map(|m| m.keys().map(|k| Pad { number: k.clone() }).collect())
            .unwrap_or_default();

        let package_name = fp
            .get("package")
            .and_then(|v| v.as_str())
            .unwrap_or("")
            .to_string();
        let body_x = fp.pointer("/bodyDimensions/x").and_then(|v| v.as_f64());
        let body_y = fp.pointer("/bodyDimensions/y").and_then(|v| v.as_f64());

        let package = if !package_name.is_empty() || body_x.is_some() || body_y.is_some() {
            Some(PackageInfo { raw_name: package_name, body_x_mm: body_x, body_y_mm: body_y })
        } else {
            None
        };
        (pads, package)
    } else {
        (Vec::new(), None)
    };

    Ok(Part {
        source: "ds2sf".to_string(),
        source_part_name,
        pads,
        pins,
        package,
    })
}