namespace PhpEmbed {
    [CCode (cname = "valaphp_embed_echo_zval", cheader_filename = "call/echo_zval.h")]
    private extern static bool call_echo_zval (Php.zval value, out string? error_message);

    [CCode (cname = "valaphp_embed_echo_string", cheader_filename = "call/echo_zval.h")]
    private extern static bool call_echo_string (string value, out string? error_message);

    [CCode (cname = "valaphp_embed_exit_eval", cheader_filename = "call/exit_eval.h")]
    private extern static int call_exit_eval (string construct_name, Php.zval? value, bool has_value, out string? error_message);

    [CCode (cname = "valaphp_embed_exit_eval_no_args", cheader_filename = "call/exit_eval.h")]
    private extern static int call_exit_eval_no_args (string construct_name, out string? error_message);

    [CCode (cname = "valaphp_embed_exit_eval_int", cheader_filename = "call/exit_eval.h")]
    private extern static int call_exit_eval_int (string construct_name, int status, out string? error_message);

    [CCode (cname = "valaphp_embed_exit_eval_string", cheader_filename = "call/exit_eval.h")]
    private extern static int call_exit_eval_string (string construct_name, string message, out string? error_message);

    public void echo_static (string value, string php_file, int php_line) throws Error {
        string? error_message = null;
        if (!call_echo_string (value, out error_message)) {
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP echo failed", php_file, php_line));
        }
    }

    public Php.zval echo_zval (Php.zval value, string php_file, int php_line) throws Error {
        string? error_message = null;
        if (!call_echo_zval (value, out error_message)) {
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP echo failed", php_file, php_line));
        }
        return Php.zval.null_value ();
    }

    private int exit_or_die_static (string construct_name, Php.zval? value, bool has_value, string php_file, int php_line) throws Error {
        string? error_message = null;
        int status = call_exit_eval (construct_name, value, has_value, out error_message);
        if (status < 0) {
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP %s failed".printf (construct_name), php_file, php_line));
        }
        return status;
    }

    public int exit_no_args_static (string php_file, int php_line) throws Error {
        string? error_message = null;
        int status = call_exit_eval_no_args ("exit", out error_message);
        if (status < 0)
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP exit failed", php_file, php_line));
        return status;
    }

    public Php.zval exit_no_args_zval (string php_file, int php_line) throws Error {
        return Php.zval.integer (exit_no_args_static (php_file, php_line));
    }

    public int exit_int_static (int status, string php_file, int php_line) throws Error {
        string? error_message = null;
        int result = call_exit_eval_int ("exit", status, out error_message);
        if (result < 0)
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP exit failed", php_file, php_line));
        return result;
    }

    public Php.zval exit_int_zval (Php.zval status, string php_file, int php_line) throws Error {
        return Php.zval.integer (exit_int_static ((int) Php.zval_to_int (status), php_file, php_line));
    }

    public int exit_string_static (string message, string php_file, int php_line) throws Error {
        string? error_message = null;
        int status = call_exit_eval_string ("exit", message, out error_message);
        if (status < 0)
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP exit failed", php_file, php_line));
        return status;
    }

    public Php.zval exit_string_zval (Php.zval message, string php_file, int php_line) throws Error {
        return Php.zval.integer (exit_string_static (Php.zval_to_string (message), php_file, php_line));
    }

    public Php.zval exit_zval (Php.zval status_or_message, string php_file, int php_line) throws Error {
        if (status_or_message == null || status_or_message.is_null ()) {
            return Php.zval.integer (exit_no_args_static (php_file, php_line));
        }
        return Php.zval.integer (exit_or_die_static ("exit", status_or_message, true, php_file, php_line));
    }

    public int die_no_args_static (string php_file, int php_line) throws Error {
        string? error_message = null;
        int status = call_exit_eval_no_args ("die", out error_message);
        if (status < 0)
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP die failed", php_file, php_line));
        return status;
    }

    public Php.zval die_no_args_zval (string php_file, int php_line) throws Error {
        return Php.zval.integer (die_no_args_static (php_file, php_line));
    }

    public int die_int_static (int status, string php_file, int php_line) throws Error {
        string? error_message = null;
        int result = call_exit_eval_int ("die", status, out error_message);
        if (result < 0)
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP die failed", php_file, php_line));
        return result;
    }

    public Php.zval die_int_zval (Php.zval status, string php_file, int php_line) throws Error {
        return Php.zval.integer (die_int_static ((int) Php.zval_to_int (status), php_file, php_line));
    }

    public int die_string_static (string message, string php_file, int php_line) throws Error {
        string? error_message = null;
        int status = call_exit_eval_string ("die", message, out error_message);
        if (status < 0)
            StacktraceError.throw (Php.with_php_source (error_message ?? "PHP die failed", php_file, php_line));
        return status;
    }

    public Php.zval die_string_zval (Php.zval message, string php_file, int php_line) throws Error {
        return Php.zval.integer (die_string_static (Php.zval_to_string (message), php_file, php_line));
    }

    public Php.zval die_zval (Php.zval status_or_message, string php_file, int php_line) throws Error {
        if (status_or_message == null || status_or_message.is_null ()) {
            return Php.zval.integer (die_no_args_static (php_file, php_line));
        }
        return Php.zval.integer (exit_or_die_static ("die", status_or_message, true, php_file, php_line));
    }
}
