Documentation
use svd_parser as svd;
use svd_parser::svd::registercluster::RegisterCluster;

use log::*;

use serde::Deserialize;
use std::fs::File;
use std::io::Read;

use crate::info::*;

#[derive(Deserialize, Debug)]
pub struct Config {}

fn parse_field(config: &Config, field: &svd_parser::svd::fieldinfo::FieldInfo) -> Field {
    log::trace!(
        "Field {} from {}:{}",
        field.name,
        field.bit_range.msb(),
        field.bit_range.lsb()
    );
    if field.enumerated_values.len() > 1 {
        unimplemented!("SVD fields with multiple enumerated values is unimplemented!");
    }

    let enumerations = if field.enumerated_values.len() == 1 {
        let values: Vec<_> = field.enumerated_values[0]
            .values
            .iter()
            .map(|value| {
                if value.value.is_none() {
                    unimplemented!("Enumeration values have to have a value!");
                }
                Enumeration {
                    name: value.name.clone(),
                    value: value.value.unwrap() as u64,
                }
            })
            .collect();
        values
    } else {
        vec![]
    };

    Field {
        name: field.name.clone(),
        lsb: field.bit_range.lsb(),
        msb: field.bit_range.msb(),
        enumerations: enumerations,
    }
}

fn parse_reg(config: &Config, reg: &svd_parser::svd::registerinfo::RegisterInfo) -> Register {
    log::debug!(
        "Found register {} with {} fields at offset {}",
        reg.name,
        reg.fields.as_deref().unwrap().len(),
        reg.address_offset
    );
    let fields = reg
        .fields
        .as_deref()
        .unwrap()
        .iter()
        .map(|field| match field {
            svd_parser::svd::field::Field::Single(info) => parse_field(config, info),
            svd_parser::svd::field::Field::Array(info, dims) => {
                unimplemented!("Field arrays are unimplemented!");
            }
        })
        .collect();
    Register {
        name: reg.name.clone(),
        offset: reg.address_offset as u64,
        is_raw: false,
        fields: fields,
    }
}

fn parse_periph(config: &Config, regs: &[RegisterCluster]) -> Vec<Register> {
    log::debug!("Parsing {} RegisterClusters", regs.len());
    //for reg in regs.iter() {
    let regs = regs
        .iter()
        .map(|reg| {
            match reg {
                RegisterCluster::Register(svd_parser::svd::register::Register::Single(reg)) => {
                    //println!("- reg");
                    parse_reg(config, reg)
                }
                RegisterCluster::Register(svd_parser::svd::register::Register::Array(
                    reg,
                    dims,
                )) => {
                    unimplemented!("Register arrays are unimplemented!");
                    println!("- reg array dims={:?}", dims);
                    parse_reg(config, reg);
                    println!("");
                }
                RegisterCluster::Cluster(svd_parser::svd::cluster::Cluster::Single(cluster)) => {
                    unimplemented!("Clusters are unimplemented!");
                    println!("- cluster");
                }
                RegisterCluster::Cluster(svd_parser::svd::cluster::Cluster::Array(
                    cluster,
                    dims,
                )) => {
                    unimplemented!("Cluster arrays are unimplemented!");
                    println!("- cluster array: {} dims={:?}", cluster.name, dims);
                    parse_periph(config, &cluster.children);
                    println!("");
                }
            }
        })
        .collect();
    regs
}

pub fn parse_svd(filename: &str, periph: &str, config: &Config) -> Peripheral {
    let periph_name = periph;

    let xml = &mut String::new();
    File::open(filename).unwrap().read_to_string(xml).unwrap();

    let res = svd::parse(xml).unwrap();

    let periph: Vec<_> = res
        .peripherals
        .iter()
        .filter(|p| p.name == periph)
        .collect();
    if periph.len() != 1 {
        panic!("Unknown periph length {}!", periph.len());
    }
    let periph = periph[0];

    println!("{}", periph.name);
    if let Some(parent) = &periph.derived_from {
        println!("- Derived from {}", parent);
    }
    let mut regs_out = vec![];
    if let Some(regs) = &periph.registers {
        regs_out = parse_periph(config, regs);
    }
    Peripheral {
        name: periph_name.to_string(),
        word_size: 32,
        regs: regs_out,
    }
}