<commit_before>use std::collections::HashMap;
use std::fmt::Write;

use syntex_syntax::{ast, parse};
use syntex_syntax::ptr::P;
use syntex_syntax::symbol::Symbol;
use syntex_syntax::print::pprust;
use syntex_syntax::parse::ParseSess;

use ForeignerClassInfo;
use ForeignTypesMap;
use MethodVariant;
use types_map::{MethodSignatureWithForeignTypes, unpack_unique_typename};
use java_jni::java_code;
use utils::fmt_write_err_map;

pub(crate) fn generate_rust_code(parse_sess: &ParseSess,
                                 types_map: &mut ForeignTypesMap,
                                 package_name: &str,
                                 class: &ForeignerClassInfo,
                                 methods_sign: &[MethodSignatureWithForeignTypes])
                                 -> Result<Vec<P<ast::Item>>, String> {
    let mut gen_fnames = HashMap::<String, usize>::new(); //handle overload names
    for method in &class.methods {
        let val_ref = gen_fnames.entry(java_code::method_name(&*method));
        *val_ref.or_insert(0) += 1;
    }

    let get_constructor_ret_type = |cls: &ForeignerClassInfo| -> Result<ast::Ty, String> {
        let ret: ast::Ty = cls.constructor_ret_type
            .as_ref()
            .ok_or_else(|| {
                            format!("package_name {}, class_name {}, have methods, \
                                    but no constructor",
                                    package_name,
                                    class.name)
                        })?
            .clone();
        Ok(ret)
    };

    let get_real_constructor_ret_type = |cls: &ForeignerClassInfo| -> Result<ast::Ty, String> {
        let ret: ast::Ty = cls.this_type_for_method
            .as_ref()
            .ok_or_else(|| {
                            format!("package_name {}, class_name {}, have methods, \
                                    but no constructor this type for method",
                                    package_name,
                                    class.name)
                        })?
            .clone();
        Ok(ret)
    };

    let mut gen_code = Vec::<P<ast::Item>>::new();

    let mut have_constructor = false;

    for (method_idx, method) in class.methods.iter().enumerate() {
        let java_method_name = java_code::method_name(&*method);
        let method_overloading = gen_fnames[&java_method_name] > 1;
        let jni_func_name = generate_jni_func_name(package_name,
                                                   &class.name,
                                                   &java_method_name,
                                                   &methods_sign[method_idx],
                                                   method_overloading);
        let n_args = methods_sign[method_idx].rust_input.len();
        let (mut deps_code_in, convert_input_code) =
            types_map
                .convert_foreign_input(parse_sess,
                                       &*method,
                                       &methods_sign[method_idx],
                                       (0..n_args).map(|v| format!("a_{}", v)))?;
        let args_names = methods_sign[method_idx]
            .rust_input
            .iter()
            .enumerate()
            .map(|a| format!("a_{}, ", a.0))
            .fold(String::new(), |acc, x| acc + &x);
        let decl_func_args = generate_jni_args_with_types(&methods_sign[method_idx])?;

        let mut code = String::new();
        match method.variant {
            MethodVariant::StaticMethod => {
                let (mut deps_code_out, convert_output_code) =
                    types_map
                        .convert_output_to_foreign(parse_sess,
                                                   &*method,
                                                   &methods_sign[method_idx],
                                                   "ret")?;
                write!(&mut code, r#"
#[allow(non_snake_case, unused_variables, unused_mut)]
#[no_mangle]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, {decl_func_args}) -> {jni_ret_type} {{
{convert_input_code}
    let mut ret = {rust_func_name}({args_names});
{convert_output_code}
    ret
}}
"#,
                       func_name = jni_func_name,
                       decl_func_args = decl_func_args,
                       jni_ret_type = unpack_unique_typename(methods_sign[method_idx].rust_output),
                       convert_input_code = convert_input_code,
                       rust_func_name = method.rust_id,
                       args_names = args_names,
                       convert_output_code = convert_output_code,
                ).map_err(fmt_write_err_map)?;



                for item in deps_code_in.drain(..).chain(deps_code_out.drain(..)) {
                    gen_code.push(P(item));
                }
                let mut jni_func_items = code_to_item(&code);
                gen_code.extend(jni_func_items.drain(..));
            }
            MethodVariant::Method => {
                let constructor_ret_type = get_real_constructor_ret_type(class)?;

                let (mut deps_code_out, convert_output_code) =
                    types_map
                        .convert_output_to_foreign(parse_sess,
                                                   &*method,
                                                   &methods_sign[method_idx],
                                                   "ret")?;
                write!(&mut code, r#"
#[allow(non_snake_case, unused_variables, unused_mut)]
#[no_mangle]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, this: jlong, {decl_func_args}) -> {jni_ret_type} {{
{convert_input_code}
    let this: &mut {this_type} = unsafe {{
        jlong_to_pointer::<{this_type}>(this).as_mut().unwrap()
    }};
{convert_this}
    let mut ret = {rust_func_name}(&mut *this, {args_names});
{convert_output_code}
    ret
}}
"#,
                       func_name = jni_func_name,
                       decl_func_args = decl_func_args,
                       convert_input_code = convert_input_code,
                       jni_ret_type = unpack_unique_typename(methods_sign[method_idx].rust_output),
                       this_type = pprust::ty_to_string(&constructor_ret_type),
                       convert_this = generate_rust_to_convert_this(class, &constructor_ret_type)?,
                       rust_func_name = method.rust_id,
                       args_names = args_names,
                       convert_output_code = convert_output_code,
                ).map_err(fmt_write_err_map)?;
                for item in deps_code_in.drain(..).chain(deps_code_out.drain(..)) {
                    gen_code.push(P(item));
                }
                let mut jni_func_items = code_to_item(&code);
                gen_code.extend(jni_func_items.drain(..));
            }
            MethodVariant::Constructor => {
                have_constructor = true;
                let constructor_ret_type = get_constructor_ret_type(class)?;
                write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]<commit_msg>cleanups: use Iterator::zip<commit_after>use std::collections::HashMap;
use std::fmt::Write;

use syntex_syntax::{ast, parse};
use syntex_syntax::ptr::P;
use syntex_syntax::symbol::Symbol;
use syntex_syntax::print::pprust;
use syntex_syntax::parse::ParseSess;

use ForeignerClassInfo;
use ForeignTypesMap;
use MethodVariant;
use types_map::{MethodSignatureWithForeignTypes, unpack_unique_typename};
use java_jni::java_code;
use utils::fmt_write_err_map;

pub(crate) fn generate_rust_code(parse_sess: &ParseSess,
                                 types_map: &mut ForeignTypesMap,
                                 package_name: &str,
                                 class: &ForeignerClassInfo,
                                 methods_sign: &[MethodSignatureWithForeignTypes])
                                 -> Result<Vec<P<ast::Item>>, String> {
    let mut gen_fnames = HashMap::<String, usize>::new(); //handle overload names
    for method in &class.methods {
        let val_ref = gen_fnames.entry(java_code::method_name(&*method));
        *val_ref.or_insert(0) += 1;
    }

    let get_constructor_ret_type = |cls: &ForeignerClassInfo| -> Result<ast::Ty, String> {
        let ret: ast::Ty = cls.constructor_ret_type
            .as_ref()
            .ok_or_else(|| {
                            format!("package_name {}, class_name {}, have methods, \
                                    but no constructor",
                                    package_name,
                                    class.name)
                        })?
            .clone();
        Ok(ret)
    };

    let get_real_constructor_ret_type = |cls: &ForeignerClassInfo| -> Result<ast::Ty, String> {
        let ret: ast::Ty = cls.this_type_for_method
            .as_ref()
            .ok_or_else(|| {
                            format!("package_name {}, class_name {}, have methods, \
                                    but no constructor this type for method",
                                    package_name,
                                    class.name)
                        })?
            .clone();
        Ok(ret)
    };

    let mut gen_code = Vec::<P<ast::Item>>::new();

    let mut have_constructor = false;

    for (method, method_sign) in class.methods.iter().zip(methods_sign.iter()) {
        let java_method_name = java_code::method_name(&*method);
        let method_overloading = gen_fnames[&java_method_name] > 1;
        let jni_func_name = generate_jni_func_name(package_name,
                                                   &class.name,
                                                   &java_method_name,
                                                   &method_sign,
                                                   method_overloading);
        let n_args = method_sign.rust_input.len();
        let (mut deps_code_in, convert_input_code) =
            types_map
                .convert_foreign_input(parse_sess,
                                       &*method,
                                       &method_sign,
                                       (0..n_args).map(|v| format!("a_{}", v)))?;
        let args_names = method_sign
            .rust_input
            .iter()
            .enumerate()
            .map(|a| format!("a_{}, ", a.0))
            .fold(String::new(), |acc, x| acc + &x);
        let decl_func_args = generate_jni_args_with_types(&method_sign)?;

        let mut code = String::new();
        match method.variant {
            MethodVariant::StaticMethod => {
                let (mut deps_code_out, convert_output_code) =
                    types_map
                        .convert_output_to_foreign(parse_sess,
                                                   &*method,
                                                   &method_sign,
                                                   "ret")?;
                write!(&mut code, r#"
#[allow(non_snake_case, unused_variables, unused_mut)]
#[no_mangle]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, {decl_func_args}) -> {jni_ret_type} {{
{convert_input_code}
    let mut ret = {rust_func_name}({args_names});
{convert_output_code}
    ret
}}
"#,
                       func_name = jni_func_name,
                       decl_func_args = decl_func_args,
                       jni_ret_type = unpack_unique_typename(method_sign.rust_output),
                       convert_input_code = convert_input_code,
                       rust_func_name = method.rust_id,
                       args_names = args_names,
                       convert_output_code = convert_output_code,
                ).map_err(fmt_write_err_map)?;



                for item in deps_code_in.drain(..).chain(deps_code_out.drain(..)) {
                    gen_code.push(P(item));
                }
                let mut jni_func_items = code_to_item(&code);
                gen_code.extend(jni_func_items.drain(..));
            }
            MethodVariant::Method => {
                let constructor_ret_type = get_real_constructor_ret_type(class)?;

                let (mut deps_code_out, convert_output_code) =
                    types_map
                        .convert_output_to_foreign(parse_sess,
                                                   &*method,
                                                   &method_sign,
                                                   "ret")?;
                write!(&mut code, r#"
#[allow(non_snake_case, unused_variables, unused_mut)]
#[no_mangle]
pub fn {func_name}(env: *mut JNIEnv, _: jclass, this: jlong, {decl_func_args}) -> {jni_ret_type} {{
{convert_input_code}
    let this: &mut {this_type} = unsafe {{
        jlong_to_pointer::<{this_type}>(this).as_mut().unwrap()
    }};
{convert_this}
    let mut ret = {rust_func_name}(&mut *this, {args_names});
{convert_output_code}
    ret
}}
"#,
                       func_name = jni_func_name,
                       decl_func_args = decl_func_args,
                       convert_input_code = convert_input_code,
                       jni_ret_type = unpack_unique_typename(method_sign.rust_output),
                       this_type = pprust::ty_to_string(&constructor_ret_type),
                       convert_this = generate_rust_to_convert_this(class, &constructor_ret_type)?,
                       rust_func_name = method.rust_id,
                       args_names = args_names,
                       convert_output_code = convert_output_code,
                ).map_err(fmt_write_err_map)?;
                for item in deps_code_in.drain(..).chain(deps_code_out.drain(..)) {
                    gen_code.push(P(item));
                }
                let mut jni_func_items = code_to_item(&code);
                gen_code.extend(jni_func_items.drain(..));
            }
            MethodVariant::Constructor => {
                have_constructor = true;
                let constructor_ret_type = get_constructor_ret_type(class)?;
                write!(&mut code, r#"
#[allow(non_snake_case)]
#[no_mangle]
{'fraction_non_alphanumeric': 0.10218978102189781, 'fraction_numerical': 0.0007086669973779321, 'mean_word_length': 1.5155080213903744, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 68, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3293197', 'n_tokens_mistral': 3923, 'n_tokens_neox': 3827, 'n_words': 637}