namespace Php {
    public string zval_to_string (Php.zval? value) {
        if (value == null || value.is_null ()) {
            return "";
        }
        switch (value.get_value_type ()) {
        case Php.ValueType.BOOL:
            return value.as_bool () ? "1" : "";
        case Php.ValueType.INT:
            return value.as_int ().to_string ();
        case Php.ValueType.DOUBLE:
            return "%g".printf (value.as_double ());
        case Php.ValueType.STRING:
            return value.as_string ();
        case Php.ValueType.ARRAY:
        case Php.ValueType.OBJECT:
            return "Array";
        default:
            return "";
        }
    }

    public double zval_to_double (Php.zval? value) {
        if (value == null || value.is_null ()) {
            return 0.0;
        }
        switch (value.get_value_type ()) {
        case Php.ValueType.BOOL:
            return value.as_bool () ? 1.0 : 0.0;
        case Php.ValueType.INT:
            return (double) value.as_int ();
        case Php.ValueType.DOUBLE:
            return value.as_double ();
        case Php.ValueType.STRING:
            return double.parse (value.as_string ());
        default:
            return 0.0;
        }
    }

    public int zval_to_int (Php.zval? value) {
        return (int) zval_to_double (value);
    }

    public bool truthy (Php.zval? value) {
        if (value == null || value.is_null ()) {
            return false;
        }
        switch (value.get_value_type ()) {
        case Php.ValueType.BOOL:
            return value.as_bool ();
        case Php.ValueType.INT:
            return value.as_int () != 0;
        case Php.ValueType.DOUBLE:
            return value.as_double () != 0.0;
        case Php.ValueType.STRING:
            return value.as_string () != "" && value.as_string () != "0";
        case Php.ValueType.ARRAY:
        case Php.ValueType.OBJECT:
            return value.count () > 0;
        default:
            return false;
        }
    }

    public Php.zval array_new () {
        return Php.zval.array ();
    }

    public Php.zval array_from_values_zval (Php.zval[] values) {
        Php.zval result = array_new ();
        foreach (Php.zval value in values) {
            array_append (result, value);
        }
        return result;
    }

    public string array_key (Php.zval key) {
        if (key.get_value_type () == Php.ValueType.INT) {
            return key.as_int ().to_string ();
        }
        if (key.get_value_type () == Php.ValueType.DOUBLE) {
            return ((int) key.as_double ()).to_string ();
        }
        return zval_to_string (key);
    }

    public Php.zval array_get (Php.zval? array_value, Php.zval key) {
        if (array_value == null) {
            return Php.zval.null_value ();
        }
        Php.zval? value = array_value.get (array_key (key));
        return value ?? Php.zval.null_value ();
    }

    public Php.zval array_ensure (Php.zval array_value, Php.zval key) {
        string string_key = array_key (key);
        Php.zval? value = array_value.get (string_key);
        if (value == null || value.is_null ()) {
            value = array_new ();
            array_value.set (string_key, value);
        }
        return value;
    }

    public void array_set (Php.zval array_value, Php.zval key, Php.zval value) {
        array_value.set (array_key (key), value);
    }

    public void array_append (Php.zval array_value, Php.zval value) {
        array_value.set (array_value.get_key_count ().to_string (), value);
    }

    public Gee.ArrayList<string> array_keys (Php.zval array_value) {
        var keys = new Gee.ArrayList<string> ();
        if (array_value.get_value_type () == Php.ValueType.ARRAY) {
            for (int i = 0; i < array_value.get_key_count (); i++) {
                string? key = array_value.get_key_at (i);
                if (key != null)
                    keys.add (key);
            }
            return keys;
        }
        return array_value.get_object_keys ();
    }

    public Gee.ArrayList<Php.zval> array_values (Php.zval array_value) {
        if (array_value.get_value_type () == Php.ValueType.ARRAY) {
            return array_value.get_array_values ();
        }
        var values = new Gee.ArrayList<Php.zval> ();
        foreach (string key in array_value.get_object_keys ()) {
            values.add (array_value.get (key) ?? Php.zval.null_value ());
        }
        return values;
    }

}
