#[cfg(feature = "parser")]
use pax::internal::{PropertyManifestable};
#[cfg(feature = "parser")]
use pax_compiler_api::{ManifestContext, PaxManifest};
#[cfg(feature = "parser")]
use std::collections::{HashSet, HashMap};
#[cfg(feature = "parser")]
use serde_json;

{% if is_root %}
// For the root component only, a `main` is generated for the `parser` bin target.
// This method bootstraps the parsing process, parsing not only the root component
// but every component/primitive found in its extended render tree. This main method
// also returns the parsed, serialized PaxManifest via stdio (println)
#[cfg(feature = "parser")]
pub fn main() {

    let mut ctx = ManifestContext::default();

    let (mut ctx, _) = {{pascal_identifier}}::parse_to_manifest(ctx);

    let manifest = PaxManifest {
        components: ctx.component_definitions,
        root_component_id: ctx.root_component_id,
    };

    //Send data back to parent process by printing to stdout
    println!("{}", &serde_json::to_string_pretty(&manifest).unwrap());
    std::process::exit(0);
}

//For root only, and only when we're NOT parsing, include reexports
//Parsing is excluded because the `pub mod pax_reexports` snippet can only be generated
//after parsing.  Trying to include_pax_reexports! before reexports.partial.rs is generated
//will result in a macro error.
//Finally: note that this code generates a macro call in order to load pax_reexports — this is necessary to load the
//reexports.partial.rs file into a token stream, and this could not be evaluated by the normal
//`pax_root` macro context because the local `CARGO_MANIFEST_DIR` is unknowable in that context.  Evaluating
//another macro here, `include_pax_reexports`, and passing the locally knowable `env!("CARGO_MANIFEST_DIR")`, is a solution to that problem.
#[cfg(not(feature = "parser"))]
include_pax_reexports!(concat!(env!("CARGO_MANIFEST_DIR"), "/.pax/"));
{% endif %}

{{original_tokens}}

#[cfg(feature = "parser")]
impl {{pascal_identifier}} {
    // For all components, a parse_to_manifest is generated under the `parser` feature
    // so that the parser binary may traverse all dependencies.
    // This method is the recursive workhorse of parsing logic.
    // One way to look at this, in conjunction with the `parser` bin target: a solution to "coordinating between macros"
    pub fn parse_to_manifest(mut ctx: ManifestContext) -> (ManifestContext, String) {

        let source_id = "component::{{pascal_identifier}}";
        let mut property_definitions = vec![];

        // Populate `PropertyDefinition`s
        {% for ctpd in compile_time_property_definitions %}
            //Here we bridge from pure static analysis into some dynamic analysis via `parser`, in order
            //to fully qualify module paths for scoped atomic types.

            //For each compile-time property definition, populate a full (parse-time) PropertyDefinition:
            //1. name | from template args
            //2. (full original type: punt) | from template args (via macro)
            //3. unique, fully qualified dependencies (HashSet, perhaps) | AT PARSE-TIME: from imperative calls to `get_fully_qualified_path`
            //4. (default settings value?  punt) | from default-block parsing

            let mut fully_qualified_dependencies = vec![];
            let mut dep_to_fqd_map = std::collections::HashMap::new();
            {% for scoped_resolvable_type in ctpd.scoped_resolvable_types %}
                let fqd = {{scoped_resolvable_type}}::get_fully_qualified_path("{{scoped_resolvable_type}}");
                dep_to_fqd_map.insert("{{scoped_resolvable_type}}",fqd.clone());

                fully_qualified_dependencies.push(
                    fqd
                );
            {% endfor %}

            let mut fully_qualified_type = "{{ctpd.original_type}}".to_string();
            //break original_type into tokens; map each token through dep_to_fqd
            vec!["<", ">", "(", ")", ","].iter().for_each(|delim| {
                //split by delim; pass each part through dep_to_fqd map; re-splice with same delims
                fully_qualified_type = fully_qualified_type.split(delim).map(|part|{
                    if let Some(more_qualified) = dep_to_fqd_map.get(&part) {
                        more_qualified
                    } else {
                        //if we can't further-qualify this segment, return the original segment
                        part
                    }
                }).collect::<Vec<_>>().join(delim);
            });

            property_definitions.push(pax_compiler_api::PropertyDefinition {
                name: "{{ctpd.field_name}}".to_string(),
                original_type: "{{ctpd.original_type}}".to_string(),
                fully_qualified_dependencies,
                fully_qualified_type,
            });
        {% endfor %}
        ctx.all_property_definitions.insert(source_id.to_string(), property_definitions);

        // TODO: mitigate injection risk here -- notable risk if dealing with untrusted input.  A mitigation may be to sanitize/validate/parse `raw_pax` before templating.

        const raw_pax: &str = r#####"{{raw_pax}}
"#####;
        match ctx.visited_source_ids.get(&source_id as &str) {

            None => {
                //First time visiting this file/source — parse the relevant contents
                //then recurse through child nodes, unrolled here in the macro as
                //parsed from the template
                ctx.visited_source_ids.insert(source_id.clone().into());

                {% for dep in template_dependencies %}
                let (mut ctx, component_id) = {{dep}}::parse_to_manifest(ctx);
                ctx.template_map.insert("{{dep}}".into(), component_id);
                {% endfor %}

                let PASCAL_IDENTIFIER = "{{pascal_identifier}}";

                let template_map= ctx.template_map.clone();

                let (mut ctx, comp_def) =
                    pax_compiler_api::parse_full_component_definition_string(
                        ctx,
                        &raw_pax,
                        PASCAL_IDENTIFIER,
                        {{is_root}},
                        template_map,
                        &source_id,
                        module_path!(),
                    );

                ctx.component_definitions
                    .push(comp_def);

                (ctx, source_id.to_string())
            },
            _ => (ctx, source_id.to_string()), //early return; this file has already been parsed
        }
    }
}