using Gee;

public class PhpToValaZvalStatementGenerator : Object {
    private PhpToValaTranspiler transpiler;

    public PhpToValaZvalStatementGenerator (PhpToValaTranspiler transpiler) {
        this.transpiler = transpiler;
    }

    public string? transpile_line (owned string line, int php_line_number) throws Error {
        line = line.strip ();
        if (line.has_prefix ("//")) {
            return line;
        }
        if (line.has_prefix ("#")) {
            return "//" + line.substring (1);
        }
        if (line.has_prefix ("/*")) {
            return line.replace ("\n", "\n        ");
        }
        if (line.has_prefix ("@")) {
            return transpile_line (line.substring (1).strip (), php_line_number);
        }
        if (line.has_suffix (";flush();")) {
            string first = line.substring (0, line.length - "flush();".length).strip ();
            transpiler.mark_function_used ("flush");
            string flush_name = transpiler.map_php_runtime_function_name ("flush", "flush_zval", php_line_number);
            return transpile_line (first, php_line_number) + "\n" + flush_name + " (" + transpiler.source_location_args (php_line_number).substring (2) + ");";
        }
        if (line == "{" || line == "}") {
            return line;
        }
        if (line == "else{") {
            return "else {";
        }
        if (line.has_prefix ("} else")) {
            return "} else {";
        }
        if (line == "try" || line == "try {") {
            return "{";
        }
        if (line == "do" || line == "do {") {
            return "do {";
        }
        if (line == "finally" || line == "finally {") {
            return "{";
        }
        if (line.has_prefix ("} finally")) {
            return "}\n{";
        }
        if (line.has_prefix ("echo ")) {
            return transpiler.transpile_zval_echo (line.substring (5).strip (), php_line_number);
        }
        if (line.has_prefix ("const ")) {
            return transpiler.transpile_zval_const (line, php_line_number);
        }
        if (line.has_prefix ("if(") || line.has_prefix ("if (")) {
            return transpiler.transpile_zval_inline_condition (line, "if", php_line_number);
        }
        if (line.has_prefix ("elseif(") || line.has_prefix ("elseif (")) {
            return transpiler.transpile_zval_inline_condition (line, "else if", php_line_number);
        }
        if (line.has_prefix ("else ")) {
            string nested = transpile_line (line.substring (5).strip (), php_line_number);
            return @"else { $nested }";
        }
        if (line.has_prefix ("foreach(") || line.has_prefix ("foreach (")) {
            return transpiler.transpile_zval_foreach (line, php_line_number);
        }
        if (line.has_prefix ("switch(") || line.has_prefix ("switch (")) {
            return transpiler.transpile_zval_switch (line, php_line_number);
        }
        if (line.has_prefix ("case ")) {
            return transpiler.transpile_zval_case (line, php_line_number);
        }
        if (line == "default:") {
            return "default:";
        }
        if (line.has_prefix ("for(") || line.has_prefix ("for (")) {
            return transpiler.transpile_zval_for (line, php_line_number);
        }
        if (line.has_prefix ("while(") || line.has_prefix ("while (")) {
            return transpiler.transpile_zval_while (line, php_line_number);
        }
        if (line.has_prefix ("} while")) {
            return transpiler.transpile_zval_do_while_end (line, php_line_number);
        }
        if (line.has_prefix ("return")) {
            string expression = line.substring ("return".length).strip ();
            expression = PhpToValaTranspiler.trim_trailing_semicolon (expression);
            return transpiler.transpile_zval_return_expression (expression, php_line_number);
        }
        if (line.has_prefix ("throw ")) {
            return transpiler.transpile_zval_throw (line, php_line_number);
        }
        if (line == "exit;" || line == "die;") {
            return transpiler.transpile_zval_exit ("", php_line_number, line.has_prefix ("die") ? "die" : "exit");
        }
        if (line == "continue;" || line == "break;") {
            return line;
        }
        string? dynamic_call = transpiler.transpile_zval_dynamic_this_call (line, php_line_number);
        if (dynamic_call != null) {
            return dynamic_call;
        }
        dynamic_call = transpiler.transpile_zval_variable_function_call (line, php_line_number);
        if (dynamic_call != null) {
            return dynamic_call;
        }
        string? inline_stream_write = transpiler.transpile_zval_inline_fwrite_fopen_call (line, php_line_number);
        if (inline_stream_write != null) {
            return inline_stream_write;
        }
        string? exec_call = transpiler.transpile_zval_exec_call (line, php_line_number);
        if (exec_call != null) {
            return exec_call;
        }
        string? increment = transpiler.transpile_zval_increment (line, php_line_number);
        if (increment != null) {
            return increment;
        }
        string? list_assignment = transpiler.transpile_zval_list_assignment (line, php_line_number);
        if (list_assignment != null) {
            return list_assignment;
        }
        string? object_property_assignment = transpiler.transpile_zval_object_property_assignment (line, php_line_number);
        if (object_property_assignment != null) {
            return object_property_assignment;
        }
        string? closure_assignment = transpiler.transpile_zval_closure_assignment (line, php_line_number);
        if (closure_assignment != null) {
            return closure_assignment;
        }

        string? compound_assignment = transpiler.transpile_zval_compound_assignment (line, php_line_number);
        if (compound_assignment != null) {
            return compound_assignment;
        }
        string? array_assignment = transpiler.transpile_zval_array_assignment (line, php_line_number);
        if (array_assignment != null) {
            return array_assignment;
        }
        string? new_object_assignment = transpiler.transpile_zval_new_object_assignment (line, php_line_number);
        if (new_object_assignment != null) {
            return new_object_assignment;
        }
        string? object_method_call = transpiler.transpile_zval_object_method_call (line, php_line_number);
        if (object_method_call != null) {
            return object_method_call;
        }
        string? static_method_call = transpiler.transpile_zval_static_method_call (line, php_line_number);
        if (static_method_call != null) {
            return static_method_call;
        }

        string? assignment_name = null;
        string raw_assignment_expression = "";
        MatchInfo assignment_match;
        if (transpiler.assignment_regex.match (line, 0, out assignment_match)) {
            assignment_name = assignment_match.fetch (1);
            raw_assignment_expression = assignment_match.fetch (2).strip ();
        } else {
            assignment_name = PhpToValaTranspiler.parse_assignment_name (line);
            if (assignment_name != null) {
                raw_assignment_expression = PhpToValaTranspiler.parse_assignment_expression (line);
            } else {
                int dollar_index = line.index_of ("$");
                int equals_index = line.index_of ("=");
                if (dollar_index >= 0 && equals_index > dollar_index && line.has_suffix (";")) {
                    string candidate = line.substring (dollar_index + 1, equals_index - dollar_index - 1).strip ();
                    if (PhpToValaTranspiler.is_identifier (candidate)) {
                        assignment_name = candidate;
                        raw_assignment_expression = PhpToValaTranspiler.trim_trailing_semicolon (line.substring (equals_index + 1).strip ());
                    }
                }
            }
        }
        if (assignment_name != null) {
            transpiler.ensure_variable_alias_for_name (assignment_name);
            string raw_expression = raw_assignment_expression;
            string storage_name = transpiler.variable_aliases.has_key (assignment_name) ? transpiler.variable_aliases[assignment_name] : assignment_name;
            string? array_filter_assignment = transpiler.transpile_zval_array_filter_assignment (storage_name, raw_expression, php_line_number);
            if (array_filter_assignment != null) {
                return array_filter_assignment;
            }
            string? short_array_assignment = transpiler.transpile_zval_short_array_assignment (storage_name, raw_expression, php_line_number);
            if (short_array_assignment != null) {
                return short_array_assignment;
            }
            string? chained_assignment = transpiler.transpile_zval_chained_assignment (assignment_name, raw_expression, php_line_number);
            if (chained_assignment != null) {
                return chained_assignment;
            }
            string? stream_select_assignment = transpiler.transpile_zval_stream_select_assignment (storage_name, raw_expression, php_line_number);
            if (stream_select_assignment != null) {
                return stream_select_assignment;
            }
            string? exec_assignment = transpiler.transpile_zval_native_exec_assignment (storage_name, raw_expression, php_line_number);
            if (exec_assignment != null) {
                return exec_assignment;
            }
            string? typed_native_assignment = transpiler.typed_assignment_optimizer.transpile_zval_typed_native_assignment (storage_name, raw_expression, php_line_number);
            if (typed_native_assignment != null) {
                return typed_native_assignment;
            }
            string? typed_static_assignment = transpiler.typed_assignment_optimizer.transpile_zval_typed_static_assignment (storage_name, raw_expression, php_line_number);
            if (typed_static_assignment != null) {
                return typed_static_assignment;
            }
            string? zval_type_change_assignment = transpiler.typed_assignment_optimizer.transpile_zval_type_change_assignment (assignment_name, storage_name, raw_expression, php_line_number);
            if (zval_type_change_assignment != null) {
                return zval_type_change_assignment;
            }
            string? existing_native_assignment = transpiler.typed_assignment_optimizer.transpile_zval_existing_native_assignment (storage_name, raw_expression, php_line_number);
            if (existing_native_assignment != null) {
                return existing_native_assignment;
            }
            string expression = transpiler.convert_zval_expression (raw_expression, php_line_number);
            if (transpiler.local_functions.has_key (assignment_name) && !transpiler.variable_aliases.has_key (assignment_name)) {
                storage_name = assignment_name + "_result";
                transpiler.variable_aliases[assignment_name] = storage_name;
            }
            if (!transpiler.variable_types.has_key (storage_name)) {
                transpiler.variable_types[storage_name] = "Php.zval";
                return @"Php.zval $storage_name = $expression;";
            }
            return @"$storage_name = $expression;";
        }

        MatchInfo match;
        if (transpiler.function_call_regex.match (line, 0, out match)) {
            string php_function_name = match.fetch (1);
            if (php_function_name == "exit" || php_function_name == "die") {
                return transpiler.transpile_zval_exit (match.fetch (2).strip (), php_line_number, php_function_name);
            }
            LocalFunction? called_local_function = transpiler.local_functions[php_function_name];
            if (called_local_function != null && PhpToValaTranspiler.function_body_exits (called_local_function.body)) {
                string call = transpiler.convert_zval_expression (php_function_name + "(" + match.fetch (2).strip () + ")", php_line_number);
                return transpiler.transpiling_local_function ? call + ";\nreturn Php.zval.null_value ();" : call + ";\nreturn 0;";
            }
            if (transpiler.local_functions.has_key (php_function_name)) {
                LocalFunction? local_function = transpiler.local_functions[php_function_name];
                if (local_function != null && transpiler.should_emit_file_func_vars (local_function)) {
                    return transpiler.file_func_vars_optimizer.local_function_statement_as_native_call (local_function, match.fetch (2).strip (), php_line_number);
                }
            }
            if (transpiler.external_functions.has_key (php_function_name)) {
                LocalFunction? external_function = transpiler.external_functions[php_function_name];
                if (external_function != null) {
                    string raw_args = match.fetch (2).strip ();
                    transpiler.validate_php_stub_argument_count (external_function, raw_args, php_line_number, php_function_name);
                    if (transpiler.optimizes_cross_file_func_vars () && transpiler.file_func_vars_optimizer.can_call_external_static (external_function)) {
                        return "%s;".printf (transpiler.file_func_vars_optimizer.external_function_call_as_native_value (external_function, raw_args, php_line_number));
                    }
                    return "%s (%s);".printf (transpiler.external_zval_call_name (external_function), transpiler.convert_zval_argument_list (raw_args, php_line_number));
                }
            }
            string expression = transpiler.convert_zval_expression (php_function_name + "(" + match.fetch (2).strip () + ")", php_line_number);
            expression = PhpToValaTranspiler.discard_unused_zval_wrapper (expression);
            return "%s;".printf (expression);
        }

        int fallback_equals_index = line.index_of ("=");
        if (fallback_equals_index > 0) {
            string lhs = line.substring (0, fallback_equals_index).strip ();
            if (lhs.has_prefix ("$")) {
                string name = PhpToValaTranspiler.replace_php_variables (lhs);
                if (PhpToValaTranspiler.is_identifier (name)) {
                    string storage_name = transpiler.ensure_variable_alias_for_name (name);
                    string expression = transpiler.convert_zval_expression (PhpToValaTranspiler.trim_trailing_semicolon (line.substring (fallback_equals_index + 1).strip ()), php_line_number);
                    if (!transpiler.variable_types.has_key (storage_name)) {
                        transpiler.variable_types[storage_name] = "Php.zval";
                        return @"Php.zval $storage_name = $expression;";
                    }
                    return @"$storage_name = $expression;";
                }
            }
        }

        throw transpiler.unsupported_syntax (php_line_number, line);
    }

}
