using Gee;

public class PhpToValaRuntimeMetadata : Object {
    public static string build (
        string runtime_name,
        bool optimize_local_vars,
        HashMap<string, FunctionBinding> function_bindings,
        ArrayList<string> used_function_names
    ) {
        var metadata = new StringBuilder ();
        metadata.append_printf ("php2vala runtime: %s\n\n", runtime_name);
        metadata.append ("php2vala runtime dependencies:\n");

        var dependency_names = collect_used_dependency_names (function_bindings, used_function_names);
        if (dependency_names.size == 0) {
            metadata.append ("- none\n");
        } else {
            foreach (string dependency_name in dependency_names) {
                metadata.append_printf ("- %s\n", dependency_name);
            }
        }

        metadata.append ("\n");
        metadata.append ("php2vala function mapping:\n");
        if (used_function_names.size == 0) {
            metadata.append ("- none\n");
        } else {
            foreach (string function_name in used_function_names) {
                FunctionBinding? binding = function_bindings.get (function_name);
                if (binding == null) {
                    continue;
                }

                metadata.append_printf (
                    "- %s -> %s (%s, %s)\n",
                    function_name,
                    metadata_call_name (optimize_local_vars, function_bindings, function_name, binding),
                    runtime_module_name (binding.package_name),
                    binding.package_name
                );
            }
        }

        return metadata.str;
    }

    public static void append_comment (StringBuilder output, string metadata) {
        output.append ("/*\n");
        foreach (string line in metadata.split ("\n")) {
            if (line == "") {
                output.append (" *\n");
            } else {
                output.append_printf (" * %s\n", line);
            }
        }
        output.append (" */\n");
    }

    public static void append_debug_constant (StringBuilder output, string metadata) {
        output.append_printf (
            "private const string PHP2VALA_DEBUG_INFO = \"%s\";\n\n",
            escape_vala_string (metadata)
        );
    }

    private static ArrayList<string> collect_used_dependency_names (
        HashMap<string, FunctionBinding> function_bindings,
        ArrayList<string> used_function_names
    ) {
        var dependency_names = new ArrayList<string> ();
        bool needs_core = false;
        foreach (string function_name in used_function_names) {
            FunctionBinding? binding = function_bindings.get (function_name);
            if (binding == null) {
                continue;
            }

            if (PhpToValaRuntimeBindings.is_runtime_package (binding.package_name, "valaphp-runtime-core")
                || binding.return_type.has_prefix ("Php.")
                || binding.source_location_args) {
                needs_core = true;
            }

            if (!dependency_names.contains (binding.package_name)) {
                dependency_names.add (binding.package_name);
            }
        }

        if (needs_core) {
            dependency_names.remove ("valaphp-runtime-core");
            dependency_names.remove ("valaphp-runtime-core-0.01");
            dependency_names.insert (0, "valaphp-runtime-core");
        }

        return dependency_names;
    }

    private static string metadata_call_name (
        bool optimize_local_vars,
        HashMap<string, FunctionBinding> function_bindings,
        string function_name,
        FunctionBinding binding
    ) {
        if (optimize_local_vars
            && binding.namespace_name == "PhpEmbed"
            && PhpToValaRuntimeBindings.is_runtime_package (binding.package_name, "valaphp-runtime-embed")
            && function_name == "echo") {
            return "PhpEmbed.echo_static";
        }
        if (optimize_local_vars
            && binding.namespace_name == "PhpEmbed"
            && PhpToValaRuntimeBindings.is_runtime_package (binding.package_name, "valaphp-runtime-embed")
            && (function_name == "die" || function_name == "exit")) {
            return "PhpEmbed." + function_name + "_*_static";
        }

        if (!optimize_local_vars
            && binding.namespace_name == "PhpEmbed"
            && PhpToValaRuntimeBindings.is_runtime_package (binding.package_name, "valaphp-runtime-embed")) {
            string mapped = binding.mapped_function_name ?? function_name;
            if (mapped.has_suffix ("_static")) {
                mapped = mapped.substring (0, mapped.length - "_static".length);
            }
            if (!mapped.has_suffix ("_zval")) {
                mapped += "_zval";
            }
            return binding.namespace_name + "." + mapped;
        }

        if (!optimize_local_vars) {
            string mapped = binding.mapped_function_name ?? function_name;
            if (mapped.has_suffix ("_static")) {
                string zval_name = mapped.substring (0, mapped.length - "_static".length) + "_zval";
                FunctionBinding? zval_binding = function_bindings.get (zval_name);
                if (zval_binding != null) {
                    return zval_binding.call_name (zval_name);
                }
            }
        }

        return binding.call_name (function_name);
    }

    private static string runtime_module_name (string package_name) {
        string module_name = package_name;
        if (module_name.has_prefix ("valaphp-runtime-")) {
            module_name = module_name.substring ("valaphp-runtime-".length);
        }
        if (module_name.has_suffix ("-0.01")) {
            module_name = module_name.substring (0, module_name.length - "-0.01".length);
        }
        return module_name;
    }

    private static string escape_vala_string (string value) {
        return value
            .replace ("\\", "\\\\")
            .replace ("\"", "\\\"")
            .replace ("\n", "\\n");
    }
}
