package org.bdware.sc.engine;

import com.google.gson.*;
import jdk.nashorn.internal.runtime.Context;
import wrp.jdk.nashorn.api.scripting.ScriptObjectMirror;
import wrp.jdk.nashorn.internal.objects.NativeArray;
import wrp.jdk.nashorn.internal.runtime.PropertyMap;
import wrp.jdk.nashorn.internal.runtime.ScriptObject;
import wrp.jdk.nashorn.internal.scripts.JO;

import java.util.*;

public class JSONTool {
    public static JsonElement convertMirrorToJson(Object ret2) {
        return convertMirrorToJsonInternal(ret2, new HashSet<>());
    }

    public static Object convertJsonElementToMirror(JsonElement jsonElement) {
        if (jsonElement.isJsonPrimitive()) {
            JsonPrimitive primitive = jsonElement.getAsJsonPrimitive();
            if (primitive.isString())
                return primitive.getAsString();
            else if (primitive.isBoolean())
                return primitive.getAsBoolean();
            return primitive.getAsNumber();
        } else if (jsonElement.isJsonObject()) {
            JO jo = new JO(PropertyMap.newMap());
            JsonObject jsonObject = jsonElement.getAsJsonObject();
            for (String key : jsonObject.keySet()) {
                jo.put(key, convertJsonElementToMirror(jsonObject.get(key)), false);
            }
            return jo;
        } else if (jsonElement.isJsonArray()) {
            NativeArray narray = new NativeArray();
            JsonArray jsonArray = jsonElement.getAsJsonArray();
            for (int i = 0; i < jsonArray.size(); i++)
                NativeArray.push(narray, convertJsonElementToMirror(jsonArray.get(i)));
            return narray;
        }
        return null;
    }

    private static JsonElement convertMirrorToJsonInternal(Object obj, Set<Object> recorded) {
        if (recorded.contains(obj)) return JsonNull.INSTANCE;
        if (obj == null) return JsonNull.INSTANCE;
        if (obj.getClass() == wrp.jdk.nashorn.internal.runtime.Undefined.class)
            return JsonNull.INSTANCE;
        if (obj instanceof JsonElement) return (JsonElement) obj;
        if (obj.getClass().isArray()) {
            Object[] arr = (Object[]) obj;
            recorded.add(obj);
            JsonArray jsonArray = new JsonArray();
            for (int i = 0; i < arr.length; i++) {
                jsonArray.add(convertMirrorToJsonInternal(arr[i], recorded));
            }
            return jsonArray;
        } else if (List.class.isAssignableFrom(obj.getClass())) {
            List arr = (List) obj;
            recorded.add(arr);
            JsonArray jsonArray = new JsonArray();
            for (int i = 0; i < arr.size(); i++) {
                jsonArray.add(convertMirrorToJsonInternal(arr.get(i), recorded));
            }
            return jsonArray;
        } else if (List.class.isAssignableFrom(obj.getClass())) {
            List arr = (List) obj;
            recorded.add(arr);
            JsonArray jsonArray = new JsonArray();
            for (int i = 0; i < arr.size(); i++) {
                jsonArray.add(convertMirrorToJsonInternal(arr.get(i), recorded));
            }
            return jsonArray;
        } else if (Set.class.isAssignableFrom(obj.getClass())) {
            Set arr = (Set) obj;
            recorded.add(arr);
            JsonArray jsonArray = new JsonArray();
            for (Object k : arr) {
                jsonArray.add(convertMirrorToJsonInternal(k, recorded));
            }
            return jsonArray;
        } else if (obj.getClass() == ScriptObjectMirror.class) {
            recorded.add(obj);
            ScriptObjectMirror som = (ScriptObjectMirror) obj;
            if (som.isFunction()) {
                return JsonNull.INSTANCE;
            }
            if (som.isArray()) {
                JsonArray jarray = new JsonArray();
                for (String str : som.getOwnKeys(true)) {
                    try {
                        if (Integer.parseInt(str) >= 0)
                            jarray.add(convertMirrorToJsonInternal(som.getMember(str), recorded));
                    } catch (Exception e) {
                        // System.out.println("[JSONTool] ignore key:"+str);
                    }
                }
                return jarray;
            } else {
                JsonObject jo = new JsonObject();
                for (String str : som.getOwnKeys(true)) {
                    jo.add(str, convertMirrorToJsonInternal(som.getMember(str), recorded));
                }
                return jo;
            }
        } else if (obj.getClass() == jdk.nashorn.api.scripting.ScriptObjectMirror.class) {
            recorded.add(obj);
            jdk.nashorn.api.scripting.ScriptObjectMirror som =
                    (jdk.nashorn.api.scripting.ScriptObjectMirror) obj;
            if (som.isFunction()) {
                return JsonNull.INSTANCE;
            }
            if (som.isArray()) {
                JsonArray jarray = new JsonArray();

                for (String str : som.getOwnKeys(true)) {
                    try {
                        if (Integer.parseInt(str) >= 0)
                            jarray.add(convertMirrorToJsonInternal(som.getMember(str), recorded));
                    } catch (Exception e) {
                        // System.out.println("[JSONTool] ignore key:"+str);
                    }
                }
                return jarray;
            } else {
                JsonObject jo = new JsonObject();
                for (String str : som.getOwnKeys(true)) {
                    jo.add(str, convertMirrorToJsonInternal(som.getMember(str), recorded));
                }
                return jo;
            }
        } else if (Map.class.isAssignableFrom(obj.getClass())) {
            Map arr = (Map) obj;
            recorded.add(arr);
            JsonObject jsonObject = new JsonObject();
            for (Object k : arr.keySet()) {
                jsonObject.add(k.toString(), convertMirrorToJsonInternal(arr.get(k), recorded));
            }
            return jsonObject;
        } else if (obj.getClass() == jdk.internal.dynalink.beans.StaticClass.class) {
            return JsonNull.INSTANCE;
        } else if (obj instanceof Number) {
            return new JsonPrimitive((Number) obj);

        } else if (obj instanceof String) {
            return new JsonPrimitive((String) obj);

        } else if (obj instanceof Character) {
            return new JsonPrimitive((Character) obj);
        }
        if (obj instanceof Boolean) {
            return new JsonPrimitive((Boolean) obj);
        }
        return JsonNull.INSTANCE;
    }
}
