/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use anyhow::anyhow;
use std::collections::HashMap;

use super::*;
use heck::ToUpperCamelCase;

pub fn pass(root: &mut Root) -> Result<()> {
    // Generate `ScaffoldingCall` items
    root.cpp_scaffolding.scaffolding_calls =
        CombinedItems::try_new(root, |namespace, ids, items| {
            let mut ffi_func_map = HashMap::new();
            namespace.visit(|ffi_func: &FfiFunction| {
                if ffi_func.kind == FfiFunctionKind::Scaffolding {
                    ffi_func_map.insert(ffi_func.name.0.clone(), ffi_func.clone());
                }
            });
            namespace.try_visit_mut(|callable: &mut Callable| {
                // Callback interface methods don't have scaffolding functions associated with them
                if matches!(callable.kind, CallableKind::VTableMethod { .. }) {
                    return Ok(());
                }
                let ffi_func = ffi_func_map.remove(&callable.ffi_func.0).ok_or_else(|| {
                    anyhow!("FfiFunction lookup failed: {:?}", callable.ffi_func.0)
                })?;
                callable.id = ids.new_id();
                let mut arguments = match &callable.kind {
                    CallableKind::Method { self_type, .. } => vec![FfiValueArgument {
                        name: "uniffi_ptr".to_string(),
                        ty: self_type.ffi_type.clone(),
                        receiver: true,
                        ..FfiValueArgument::default()
                    }],
                    _ => vec![],
                };
                arguments.extend(callable.arguments.iter().map(|a| FfiValueArgument {
                    name: a.name.clone(),
                    ty: a.ty.ffi_type.clone(),
                    receiver: false,
                    ..FfiValueArgument::default()
                }));

                items.push(ScaffoldingCall {
                    id: callable.id,
                    arguments,
                    handler_class_name: format!(
                        "ScaffoldingCallHandler{}",
                        ffi_func.name.0.to_upper_camel_case(),
                    ),
                    return_ty: callable
                        .return_type
                        .ty
                        .as_ref()
                        .map(|ty| FfiValueReturnType {
                            ty: ty.ffi_type.clone(),
                            ..FfiValueReturnType::default()
                        }),
                    ffi_func,
                });
                Ok(())
            })
        })?;
    root.cpp_scaffolding
        .scaffolding_calls
        .sort_by_key(|call| call.id);

    Ok(())
}
