use super::{imports::Imports, trampolines};
use crate::{
    analysis::trampolines::Trampoline,
    config::{self, gobjects::GObject},
    env::Env,
    library, nameutil,
    traits::*,
    version::Version,
};

#[derive(Debug)]
pub struct Info {
    pub connect_name: String,
    pub signal_name: String,
    pub action_emit_name: Option<String>,
    pub trampoline: Result<Trampoline, Vec<String>>,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub doc_hidden: bool,
    pub is_detailed: bool,
    pub generate_doc: bool,
}

pub fn analyze(
    env: &Env,
    signals: &[library::Signal],
    type_tid: library::TypeId,
    in_trait: bool,
    is_fundamental: bool,
    obj: &GObject,
    imports: &mut Imports,
) -> Vec<Info> {
    let mut sns = Vec::new();

    for signal in signals {
        let configured_signals = obj.signals.matched(&signal.name);
        if !configured_signals.iter().all(|f| f.status.need_generate()) {
            continue;
        }
        if env.is_totally_deprecated(Some(type_tid.ns_id), signal.deprecated_version) {
            continue;
        }

        let info = analyze_signal(
            env,
            signal,
            type_tid,
            in_trait,
            is_fundamental,
            &configured_signals,
            obj,
            imports,
        );
        sns.push(info);
    }

    sns
}

fn analyze_signal(
    env: &Env,
    signal: &library::Signal,
    type_tid: library::TypeId,
    in_trait: bool,
    is_fundamental: bool,
    configured_signals: &[&config::signals::Signal],
    obj: &GObject,
    imports: &mut Imports,
) -> Info {
    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let version = configured_signals
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(signal.version);
    let deprecated_version = signal.deprecated_version;
    let doc_hidden = configured_signals.iter().any(|f| f.doc_hidden);

    let imports = &mut imports.with_defaults(version, &None);
    imports.add("glib::translate::*");

    let connect_name = format!("connect_{}", nameutil::signal_to_snake(&signal.name));
    let trampoline = trampolines::analyze(
        env,
        signal,
        type_tid,
        in_trait,
        is_fundamental,
        configured_signals,
        obj,
        &mut used_types,
        version,
    );

    let action_emit_name = if signal.is_action {
        imports.add("glib::prelude::*");
        Some(format!("emit_{}", nameutil::signal_to_snake(&signal.name)))
    } else {
        None
    };

    if trampoline.is_ok() {
        imports.add_used_types(&used_types);
        imports.add("glib::prelude::*");
        imports.add("glib::signal::{connect_raw, SignalHandlerId}");
        imports.add("std::boxed::Box as Box_");
    }
    let generate_doc = configured_signals.iter().all(|f| f.generate_doc);

    Info {
        connect_name,
        signal_name: signal.name.clone(),
        trampoline,
        action_emit_name,
        version,
        deprecated_version,
        doc_hidden,
        is_detailed: signal.is_detailed,
        generate_doc,
    }
}
