using Gee;

private static string default_output_path (string input_path) {
    if (input_path.has_suffix (".php")) {
        return input_path.substring (0, input_path.length - 4) + ".vala";
    }
    return input_path + ".vala";
}

private static ArrayList<string> discover_default_php_stub_paths () {
    var paths = new ArrayList<string> ();
    string[] roots = {
        Path.build_filename (Environment.get_user_data_dir (), "valaphp"),
        Path.build_filename (Environment.get_user_data_dir (), "pwphpbytecode-stubs"),
        "/usr/local/share/valaphp",
        "/usr/local/share/pwphpbytecode-stubs",
        "/usr/share/valaphp",
        "/usr/share/pwphpbytecode-stubs"
    };

    foreach (string root in roots) {
        if (!FileUtils.test (root, FileTest.IS_DIR)) {
            continue;
        }
        try {
            Dir dir = Dir.open (root);
            string? child_name;
            while ((child_name = dir.read_name ()) != null) {
                string child_path = Path.build_filename (root, (string) child_name);
                if (!FileUtils.test (child_path, FileTest.IS_DIR)) {
                    continue;
                }
                try {
                    Dir child_dir = Dir.open (child_path);
                    string? stub_name;
                    while ((stub_name = child_dir.read_name ()) != null) {
                        if (!((string) stub_name).has_suffix (".stub.php")) {
                            continue;
                        }
                        paths.add (Path.build_filename (child_path, (string) stub_name));
                    }
                } catch (Error e) {
                }
            }
        } catch (Error e) {
        }
    }
    return paths;
}

private static bool syntax_check_php_source (string input_path, string php_source, bool use_temporary_source, out string? syntax_error) throws Error {
    syntax_error = null;
    if (use_temporary_source) {
    }

    try {
        PhpToValaTokenizer.tokenize_php_source (input_path, php_source, true);
        return true;
    } catch (Error e) {
        syntax_error = e.message;
        return false;
    }
}

private static void print_usage (string program_name, FileStream stream) {
    stream.printf ("Usage: %s [--composer-library composer.json|--executable|--library] [--php-stub path.stub.php] [--php-stub-out path.stub.php] [--embed-runtime|--native-runtime|--both-runtimes] [--optimize-local-vars|--optimize-file-func-vars|--optimize-cross-file-func-vars] [--no-syntax-check] input.php [output.vala]\n", program_name);
    stream.printf ("\n");
    stream.printf ("Output options:\n");
    stream.printf ("  --executable       Generate executable output with public main() (default); requires top-level main commands\n");
    stream.printf ("  --library          Generate library output without public main(); rejects top-level main commands\n");
    stream.printf ("  --php-stub FILE    Load declarative PHP stub for external classes/functions\n");
    stream.printf ("  --php-stub-out FILE  Write declarative PHP stub beside generated Vala output\n");
    stream.printf ("  --composer-library FILE  Generate a Composer PSR-4 library skeleton from composer.json\n");
    stream.printf ("\n");
    stream.printf ("Runtime options:\n");
    stream.printf ("  --embed-runtime     Use only valaphp-runtime-embed for PHP helpers\n");
    stream.printf ("  --native-runtime    Use only native runtime modules; missing native helpers are errors\n");
    stream.printf ("  --both-runtimes     Prefer native runtime modules, then use embed fallback for missing helpers (default)\n");
    stream.printf ("\n");
    stream.printf ("Optimizer options:\n");
    stream.printf ("  --no-optimize-local-vars       Disable local/file function variable typing\n");
    stream.printf ("  --optimize-local-vars          Enable per-function local variable typing only\n");
    stream.printf ("  --optimize-file-func-vars      Enable whole-file local function typing; also enables local vars\n");
    stream.printf ("  --optimize-cross-file-func-vars  Enable typed cross-file function calls via generated PHP stubs (default)\n");
}

private static void load_php_stub_files (PhpToValaTranspiler transpiler, ArrayList<string> php_stub_paths) throws Error {
    var loaded_php_stub_paths = new HashMap<string, bool> ();
    foreach (string php_stub_path in discover_default_php_stub_paths ()) {
        transpiler.load_php_stub_file (php_stub_path);
        loaded_php_stub_paths[php_stub_path] = true;
    }
    foreach (string php_stub_path in php_stub_paths) {
        if (loaded_php_stub_paths.has_key (php_stub_path)) {
            continue;
        }
        transpiler.load_php_stub_file (php_stub_path);
    }
}

public int main (string[] args) {
    if (args.length < 2) {
        print_usage (args[0], stderr);
        return 1;
    }

    string runtime_name = "both-runtimes";
    string output_mode = "executable";
    var php_stub_paths = new ArrayList<string> ();
    string? php_stub_out_path = null;
    string? composer_library_path = null;
    bool optimize_local_vars = false;
    bool optimize_file_func_vars = true;
    bool optimize_cross_file_func_vars = true;
    bool syntax_check = true;
    int arg_index = 1;
    while (arg_index < args.length && args[arg_index].has_prefix ("-")) {
        if (args[arg_index] == "--help" || args[arg_index] == "-h") {
            print_usage (args[0], stdout);
            return 0;
        } else if (args[arg_index] == "--version") {
            stdout.printf ("php2vala %s\n", Php2Vala.VERSION);
            return 0;
        } else if (args[arg_index] == "--embed-runtime") {
            runtime_name = "embed-runtime";
        } else if (args[arg_index] == "--native-runtime") {
            runtime_name = "native-runtime";
        } else if (args[arg_index] == "--both-runtimes") {
            runtime_name = "both-runtimes";
        } else if (args[arg_index] == "--executable") {
            output_mode = "executable";
        } else if (args[arg_index] == "--library") {
            output_mode = "library";
        } else if (args[arg_index] == "--php-stub") {
            if (arg_index + 1 >= args.length) {
                stderr.printf ("Missing argument for --php-stub\n");
                return 1;
            }
            php_stub_paths.add (args[++arg_index]);
        } else if (args[arg_index] == "--php-stub-out") {
            if (arg_index + 1 >= args.length) {
                stderr.printf ("Missing argument for --php-stub-out\n");
                return 1;
            }
            php_stub_out_path = args[++arg_index];
        } else if (args[arg_index] == "--composer-library") {
            if (arg_index + 1 >= args.length) {
                stderr.printf ("Missing argument for --composer-library\n");
                return 1;
            }
            composer_library_path = args[++arg_index];
        } else if (args[arg_index] == "--optimize-local-vars") {
            optimize_local_vars = true;
            optimize_file_func_vars = false;
            optimize_cross_file_func_vars = false;
        } else if (args[arg_index] == "--no-optimize-local-vars") {
            optimize_local_vars = false;
            optimize_file_func_vars = false;
            optimize_cross_file_func_vars = false;
        } else if (args[arg_index] == "--optimize-file-func-vars") {
            optimize_file_func_vars = true;
            optimize_cross_file_func_vars = false;
        } else if (args[arg_index] == "--optimize-cross-file-func-vars") {
            optimize_cross_file_func_vars = true;
            optimize_file_func_vars = true;
        } else if (args[arg_index] == "--no-optimize-file-func-vars") {
            optimize_file_func_vars = false;
            optimize_cross_file_func_vars = false;
        } else if (args[arg_index] == "--no-syntax-check") {
            syntax_check = false;
        } else {
            stderr.printf ("Unknown option: %s\n", args[arg_index]);
            print_usage (args[0], stderr);
            return 1;
        }
        arg_index++;
    }

    if (composer_library_path != null) {
        if (args.length - arg_index > 1) {
            print_usage (args[0], stderr);
            return 1;
        }
        string output_path = args.length - arg_index == 1 ? args[arg_index] : "composer-library.vala";
        try {
            var composer_library = PhpToValaComposerLibrary.generate ((string) composer_library_path);
            FileUtils.set_contents (output_path, composer_library.vala_source);
            if (php_stub_out_path != null) {
                FileUtils.set_contents ((string) php_stub_out_path, composer_library.stub_source);
            }
        } catch (Error e) {
            stderr.printf ("php2vala: %s\n", e.message);
            return 1;
        }
        return 0;
    }

    if (arg_index >= args.length || args.length - arg_index > 2) {
        print_usage (args[0], stderr);
        return 1;
    }

    string input_path = args[arg_index];
    string output_path = args.length - arg_index == 2 ? args[arg_index + 1] : default_output_path (input_path);

    try {
        string php_source;
        FileUtils.get_contents (input_path, out php_source);
        bool had_interpreter_line = PhpToValaTokenizer.has_leading_interpreter_line (php_source);
        php_source = PhpToValaTokenizer.remove_leading_interpreter_line_preserving_lines (php_source);

        if (syntax_check) {
            string? syntax_error = null;
            if (!syntax_check_php_source (input_path, php_source, had_interpreter_line, out syntax_error)) {
                throw new PhpToValaError.UNSUPPORTED (
                    "PHP syntax check failed for %s: %s",
                    input_path,
                    syntax_error ?? "unknown PHP syntax error"
                );
            }
        }

        var transpiler = new PhpToValaTranspiler (input_path, optimize_local_vars, optimize_file_func_vars, optimize_cross_file_func_vars, runtime_name, output_mode);
        load_php_stub_files (transpiler, php_stub_paths);
        string vala_source = transpiler.transpile (php_source);

        FileUtils.set_contents (output_path, vala_source);
        transpiler.write_optimizer_step_vala_files (output_path);
        transpiler.write_debug_artifacts (output_path);
        if (php_stub_out_path != null) {
            FileUtils.set_contents ((string) php_stub_out_path, transpiler.build_php_stub ());
        }
    } catch (Error e) {
        stderr.printf ("php2vala: %s\n", e.message);
        return 1;
    }

    return 0;
}
