use super::{OverlayDesc, OverlaySection, OverlaySlot};
use tool_helper::{Error, format_if_error, Input};

pub fn read_config(input: Input) -> Result<OverlayDesc, Error> {
    let json_reader:serde_json::Value = format_if_error!(serde_json::from_reader(input), "Parsing JSON failed with: {error_text}")?;
    let mut overlay_desc              = OverlayDesc::new();

    if let Some(objects) = json_reader.as_object() {
        for object in objects {
            let slot = read_as_slot(object)?;
            if !slot.is_commented_out() && slot.has_sections() {
                overlay_desc.push(slot);
            }
        }

        Ok(overlay_desc)
    }

    else {
        Err(Error::from_str("JSON must start with an object"))
    }
}

fn read_as_slot(json_value: (&String, &serde_json::Value)) -> Result<OverlaySlot, Error> {
    let (slot_name, json_value) = json_value;
    let mut slot                = OverlaySlot::new(slot_name.clone());

    if let Some(json_value) = json_value.as_object() {
        for (name, json_value) in json_value {
            let section = read_as_section(name.clone(), json_value)?;
            if !section.is_commented_out() {
                slot.add_section(section);
            }
        }
    }

    Ok(slot)
}

fn read_as_section(section_name: String, json_value: &serde_json::Value) -> Result<OverlaySection, Error> {
    fn create_pattern_error<T>(section_name: &String) -> Result<T, Error> {
        Err(Error::from_text(format!("Pattern element of section declaration \"{}\" must contain only strings", section_name)))
    }

    fn read_pattern(section_name: &String, json_value: &serde_json::Value) -> Result<String, Error> {
        if let Some(pattern) = json_value.as_str() {
            Ok(pattern.to_owned())
        }

        else {
            create_pattern_error(section_name)
        }
    }

    let mut section = OverlaySection::new(section_name);

    if let Some(pattern_json) = json_value.get("pattern") {
        if let Some(pattern) = pattern_json.as_array() {
            for cur_pattern in pattern {
                section.add_file_pattern(read_pattern(&section.name, cur_pattern)?);
            }
            Ok(section)
        }
        
        else if let Some(pattern) = pattern_json.as_str() {
            section.add_file_pattern(pattern.to_owned());
            Ok(section)
        }
        
        else {
            create_pattern_error(&section.name)
        }
    }
    
    else {
        Err(Error::from_text(format!("Section declaration \"{}\" does not contain any pattern!", section.name)))
    }
}