package org.develnext.jphp.json.gson;

import com.google.gson.*;
import php.runtime.Memory;
import php.runtime.env.Environment;
import php.runtime.lang.StdClass;
import php.runtime.memory.ArrayMemory;
import php.runtime.memory.DoubleMemory;
import php.runtime.memory.ObjectMemory;
import php.runtime.memory.StringMemory;

import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.util.Map;

public class MemoryDeserializer implements JsonDeserializer<Memory> {

    protected boolean assoc;
    protected int maxDepth = 512;
    protected WeakReference<Environment> env;

    public boolean isAssoc() {
        return assoc;
    }

    public void setEnv(Environment env) {
        this.env = new WeakReference<Environment>(env);
    }

    public void setAssoc(boolean assoc) {
        this.assoc = assoc;
    }

    public int getMaxDepth() {
        return maxDepth;
    }

    public void setMaxDepth(int maxDepth) {
        this.maxDepth = maxDepth;
    }

    protected Memory convert(JsonElement json, int depth) {
        if (depth > maxDepth)
            throw new MaxDepthException();

        if (json.isJsonNull())
            return Memory.NULL;
        if (json.isJsonPrimitive()) {
            JsonPrimitive jsonPrimitive = json.getAsJsonPrimitive();
            if (jsonPrimitive.isString())
                return StringMemory.valueOf(jsonPrimitive.getAsString());
            else if (jsonPrimitive.isBoolean())
                return jsonPrimitive.getAsBoolean() ? Memory.TRUE : Memory.FALSE;
            else if (jsonPrimitive.isNumber()) {
                Memory l = StringMemory.toLong(jsonPrimitive.getAsString());
                if (l != null)
                    return l;
                else
                    return new DoubleMemory(json.getAsDouble());
            }
            return Memory.NULL;
        } else if (json.isJsonArray()) {
            ArrayMemory array = new ArrayMemory();
            for(JsonElement el : json.getAsJsonArray())
                array.add(convert(el, depth + 1));
            return array.toConstant();
        } else if (json.isJsonObject()) {
            JsonObject jsonObject = json.getAsJsonObject();
            StdClass stdClass = assoc ? null : new StdClass(env.get());
            ArrayMemory array = assoc ? new ArrayMemory() : stdClass.getProperties();

            for(Map.Entry<String, JsonElement> el : jsonObject.entrySet()){
                String key = el.getKey();
                if (!key.startsWith("\0"))
                    array.put(key, convert(el.getValue(), depth + 1));
            }

            return assoc ? array.toConstant() : new ObjectMemory(stdClass);
        } else
            return Memory.NULL;
    }

    @Override
    public Memory deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        return convert(json, 0);
    }

    public static class MaxDepthException extends RuntimeException {
        @Override
        public synchronized Throwable fillInStackTrace() {
            return this;
        }
    }
}

@@ -2033,32 +2033,46 @@
 t(el, depth + 1)
+.toImmutable()
 );%0A            r
@@ -2587,16 +2587,30 @@
 pth + 1)
+.toImmutable()
 );%0A     

Fix bug in json extenison, deserialization arrays.