using Php;

namespace PhpNative {
    private void print_r_append_indent (StringBuilder builder, int indent) {
        for (int i = 0; i < indent; i++)
            builder.append_c (' ');
    }

    private string print_r_scalar_to_string (zval value) {
        switch (value.get_value_type ()) {
        case ValueType.NULL:
            return "";
        case ValueType.BOOL:
            return value.as_bool () ? "1" : "";
        case ValueType.INT:
            return ("%"+ int64.FORMAT).printf (value.as_int ());
        case ValueType.DOUBLE:
            return "%g".printf (value.as_double ());
        case ValueType.STRING:
            return value.as_string ();
        default:
            return "";
        }
    }

    private bool print_r_is_container (zval value) {
        return value.get_value_type () == ValueType.ARRAY
            || value.get_value_type () == ValueType.OBJECT;
    }

    private void append_print_r_zval (StringBuilder builder, zval value, int indent, bool nested = false) {
        switch (value.get_value_type ()) {
        case ValueType.OBJECT:
            builder.append ("stdClass Object\n");
            int paren_indent = nested ? indent + 4 : indent;
            print_r_append_indent (builder, paren_indent);
            builder.append ("(\n");

            foreach (string key in value.get_object_keys ()) {
                zval child = value.get_object_values ()[key];
                print_r_append_indent (builder, paren_indent + 4);
                builder.append ("[%s] => ".printf (key));
                append_print_r_zval (builder, child, paren_indent + 4, true);
                if (print_r_is_container (child))
                    builder.append_c ('\n');
            }

            print_r_append_indent (builder, paren_indent);
            builder.append (")\n");
            break;

        case ValueType.ARRAY:
            builder.append ("Array\n");
            int paren_indent = nested ? indent + 4 : indent;
            print_r_append_indent (builder, paren_indent);
            builder.append ("(\n");

            int index = 0;
            foreach (zval child in value.get_array_values ()) {
                print_r_append_indent (builder, paren_indent + 4);
                builder.append ("[%d] => ".printf (index));
                append_print_r_zval (builder, child, paren_indent + 4, true);
                if (print_r_is_container (child))
                    builder.append_c ('\n');
                index++;
            }

            print_r_append_indent (builder, paren_indent);
            builder.append (")\n");
            break;

        default:
            builder.append ("%s\n".printf (print_r_scalar_to_string (value)));
            break;
        }
    }

    public string print_r (zval value, bool return_output = false) {
        var builder = new StringBuilder ();
        append_print_r_zval (builder, value, 0);

        string output = builder.str;
        if (!return_output)
            stdout.printf ("%s", output);

        return output;
    }
}
