Inheritance: JSONNode, IEnumerable
        static WebApiServer()
        {
            Process currentProcess = Process.GetCurrentProcess();
            int unityConsuloPluginPort = 56000 + currentProcess.Id % 1000 + 2000; // 56000 + 2000

            HTTPServer httpServer = new HTTPServer(unityConsuloPluginPort);

            Action action = () => HTTPServer.SendSetDefinesToConsulo(null);

            UnityUtil.RunInMainThread(() =>
            {
                AppDomain.CurrentDomain.DomainUnload += (sender, e) =>
                {
                    EditorUserBuildSettings.activeBuildTargetChanged -= action;

                    httpServer.Stop();
                };
            });

            EditorUserBuildSettings.activeBuildTargetChanged += action;

            Application.RegisterLogCallback((condition, stackTrace, type) =>
            {
                string testUUID = ourCurrentTestUUID;
                if(testUUID != null)
                {
                    JSONClass jsonClass = new JSONClass();
                    jsonClass.Add("name", ourCurrentTestName);
                    jsonClass.Add("uuid", testUUID);
                    jsonClass.Add("type", "TestOutput");
                    jsonClass.Add("message", condition);
                    jsonClass.Add("stackTrace", stackTrace);
                    jsonClass.Add("messageType", Enum.GetName(typeof(LogType), type));

                    ConsuloIntegration.SendToConsulo("unityTestState", jsonClass);
                }
                else
                {
                    JSONClass jsonClass = new JSONClass();

                    jsonClass.Add("condition", condition);
                    jsonClass.Add("stackTrace", stackTrace);
                    jsonClass.Add("projectPath", Path.GetDirectoryName(Application.dataPath));
                    jsonClass.Add("type", Enum.GetName(typeof(LogType), type));

                    ConsuloIntegration.SendToConsulo("unityLog", jsonClass);
                }
            });

            EditorApplication.playmodeStateChanged += delegate
            {
                JSONClass jsonClass = new JSONClass();

                jsonClass.Add("isPlaying", new JSONData(EditorApplication.isPlaying));
                jsonClass.Add("projectPath", Path.GetDirectoryName(Application.dataPath));

                ConsuloIntegration.SendToConsulo("unityPlayState", jsonClass);
            };
        }
        public static void SendSetDefinesToConsulo(string uuid)
        {
            UnityUtil.RunInMainThread(() =>
            {
                if(!ConsuloIntegration.UseConsulo())
                {
                    return;
                }

                string projectPath = Path.GetDirectoryName(Application.dataPath);
                projectPath = projectPath.Replace('\\', '/');

                JSONClass result = new JSONClass();
                result.Add("projectPath", projectPath);
                if(uuid != null)
                {
                    result.Add("uuid", uuid);
                }
                JSONArray array = new JSONArray();
                foreach (string define in EditorUserBuildSettings.activeScriptCompilationDefines)
                {
                    array.Add(define);
                }

                result.Add("defines", array);

                ConsuloIntegration.SendToConsulo("unitySetDefines", result);
            });
        }
        public void SuiteFinished(NUnit.Core.TestResult result)
        {
            if(result.FullName.Equals(myRootName))
            {
                return;
            }

            JSONClass jsonClass = new JSONClass();
            jsonClass.Add("name", result.Name);
            jsonClass.Add("uuid", myUUID);
            jsonClass.Add("type", "SuiteFinished");

            ConsuloIntegration.SendToConsulo("unityTestState", jsonClass);
        }
        public void SuiteStarted(NUnit.Core.TestName testName)
        {
            if(myRootName == null)
            {
                myRootName = testName.FullName;
                return;
            }

            JSONClass jsonClass = new JSONClass();
            jsonClass.Add("uuid", myUUID);
            jsonClass.Add("name", testName.Name);
            jsonClass.Add("type", "SuiteStarted");

            ConsuloIntegration.SendToConsulo("unityTestState", jsonClass);
        }
        public static void SendToConsulo(string url, JSONClass jsonClass)
        {
            if(!UseConsulo())
            {
                return;
            }

            try
            {
                WebRequest request = WebRequest.Create("http://localhost:" + PluginConstants.ourPort + "/api/" + url);
                request.Timeout = 10000;
                request.Method = "POST";
                request.ContentType = "application/json; charset=utf-8";

                WebRequestState state = new WebRequestState
                {
                    Request = request,
                    Json = jsonClass
                };

                request.BeginGetRequestStream(new AsyncCallback(WriteCallback), state);
            }
            catch(Exception e)
            {
                EditorUtility.DisplayDialog(PluginConstants.ourDialogTitle, "Consulo is not accessible at http://localhost:" + PluginConstants.ourPort + "/" + url + ", message: " + e.Message, "OK");
            }
        }
        static bool OnOpenedAssetCallback(int instanceID, int line)
        {
            if(!UseConsulo())
            {
                return false;
            }

            UnityEngine.Object selected = EditorUtility.InstanceIDToObject(instanceID);
            string contentType = selected.GetType().ToString();
            if(!ourSupportedContentTypes.Contains(contentType))
            {
                return false;
            }

            string projectPath = Path.GetDirectoryName(Application.dataPath);
            string filePath = projectPath + "/" + AssetDatabase.GetAssetPath(selected);
            projectPath = projectPath.Replace('\\', '/');
            filePath = filePath.Replace('\\', '/');

            JSONClass jsonClass = new JSONClass();
            jsonClass.Add("projectPath", new JSONData(projectPath));
            jsonClass.Add("filePath", new JSONData(filePath));
            jsonClass.Add("editorPath", new JSONData(EditorApplication.applicationPath));
            jsonClass.Add("contentType", new JSONData(selected.GetType().ToString()));
            jsonClass.Add("line", new JSONData(line));

            SendToConsulo("unityOpenFile", jsonClass);
            return true;
        }
        private void Process(HttpListenerContext context)
        {
            var requestHttpMethod = context.Request.HttpMethod;
            if("POST".Equals(requestHttpMethod))
            {
                string pathAndQuery = context.Request.Url.PathAndQuery;

                context.Response.ContentType = "application/json";

                bool resultValue = false;
                HttpStatusCode code = HttpStatusCode.InternalServerError;

                JSONClass jsonClass = null;
                string uuid = null;
                switch(pathAndQuery)
                {
                    case "/unityRefresh":
                        jsonClass = ReadJSONClass(context);
                        uuid = jsonClass == null ? null : jsonClass["uuid"].Value;
                        resultValue = uuid != null;
                        if(uuid != null)
                        {
                            UnityUtil.RunInMainThread(() =>
                            {
                                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);

                                JSONClass result = new JSONClass();
                                result.Add("uuid", uuid);
                                ConsuloIntegration.SendToConsulo("unityRefreshResponse", result);
                            });
                        }
                        code = HttpStatusCode.OK;
                        break;
                    case "/unityRequestDefines":
                    {
                        jsonClass = ReadJSONClass(context);
                        if(jsonClass != null)
                        {
                            resultValue = true;
                            uuid = jsonClass["uuid"].Value;
                            SendSetDefinesToConsulo(uuid);
                        }

                        code = HttpStatusCode.OK;
                        break;
                    }
                    case "/unityOpenScene":
                        jsonClass = ReadJSONClass(context);
                        string fileValue = jsonClass == null ? null : jsonClass["file"].Value;
                        if(fileValue != null)
                        {
                            resultValue = true;
                            UnityUtil.RunInMainThread(() =>
                            {
                                EditorApplication.OpenScene(fileValue);
                                EditorUtility.FocusProjectWindow();
                            });
                        }
                        code = HttpStatusCode.OK;
                        break;
                    #if NUNIT
                    case "/unityRunTest":
                        jsonClass = ReadJSONClass(context);
                        if(jsonClass != null)
                        {
                            resultValue = true;
                            string type = jsonClass["type"].Value;
                            uuid = jsonClass["uuid"].Value;
                            UnityUtil.RunInMainThread(() =>
                            {
                                int undo = Undo.GetCurrentGroup();
                                RunNUnitTests(type, uuid);
                                Undo.RevertAllDownToGroup(undo);

                                JSONClass result = new JSONClass();
                                result.Add("uuid", uuid);
                                result.Add("type", "RunFinished");
                                ConsuloIntegration.SendToConsulo("unityTestState", result);
                            });
                        }
                        code = HttpStatusCode.OK;
                        break;
                    #endif
                    default:
                        UnityUtil.RunInMainThread(() =>
                        {
                            EditorUtility.DisplayDialog(PluginConstants.ourDialogTitle, $"Unknown how handle API url {pathAndQuery}, please update UnityEditor plugin for Consulo", "OK");
                        });
                        code = HttpStatusCode.InternalServerError;
                        break;
                }

                string text = "{ \"success\":" + resultValue + " }";

                context.Response.ContentLength64 = text.Length;
                context.Response.StatusCode = (int) code;
                byte[] encodingUTFGetBytes = Encoding.UTF8.GetBytes(text);
                context.Response.OutputStream.Write(encodingUTFGetBytes, 0, encodingUTFGetBytes.Length);
                context.Response.OutputStream.Flush();
            }
            else
            {
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            }

            context.Response.OutputStream.Close();
        }
        public void TestFinished(NUnit.Core.TestResult result)
        {
            JSONClass jsonClass = new JSONClass();
            jsonClass.Add("name", result.Name);
            jsonClass.Add("uuid", myUUID);
            string typeText;
            switch(result.ResultState)
            {
                case ResultState.Ignored:
                    typeText = "TestIgnored";
                    break;
                case ResultState.Success:
                    typeText = "TestFinished";
                    break;
                default:
                    typeText = "TestFailed";
                    break;
            }

            jsonClass.Add("type", typeText);
            string message = result.Message;
            if(message != null)
            {
                jsonClass.Add("message", message);
            }
            string trace = result.StackTrace;
            if(trace != null)
            {
                jsonClass.Add("stackTrace", trace);
            }
            jsonClass.Add("time", result.Time.ToString());

            ConsuloIntegration.SendToConsulo("unityTestState", jsonClass);
        }
        public void TestStarted(NUnit.Core.TestName testName)
        {
            WebApiServer.ourCurrentTestName = testName.Name;

            JSONClass jsonClass = new JSONClass();
            jsonClass.Add("name", testName.Name);
            jsonClass.Add("uuid", myUUID);
            jsonClass.Add("type", "TestStarted");

            ConsuloIntegration.SendToConsulo("unityTestState", jsonClass);
        }
        public void TestOutput(NUnit.Core.TestOutput testOutput)
        {
            JSONClass jsonClass = new JSONClass();
            jsonClass.Add("name", Enum.GetName(typeof(TestOutputType), testOutput.Type));
            jsonClass.Add("uuid", myUUID);
            jsonClass.Add("type", "TestOutput");
            jsonClass.Add("message", testOutput.ToString());

            ConsuloIntegration.SendToConsulo("unityTestState", jsonClass);
        }
        public static JSONNode Deserialize(System.IO.BinaryReader aReader)
        {
            JSONBinaryTag type = (JSONBinaryTag)aReader.ReadByte();
            switch(type)
            {
                case JSONBinaryTag.Array:
                {
                    int count = aReader.ReadInt32();
                    JSONArray tmp = new JSONArray();
                    for(int i = 0; i < count; i++)
                        tmp.Add(Deserialize(aReader));
                    return tmp;
                }
                case JSONBinaryTag.Class:
                {
                    int count = aReader.ReadInt32();
                    JSONClass tmp = new JSONClass();
                    for(int i = 0; i < count; i++)
                    {
                        string key = aReader.ReadString();
                        var val = Deserialize(aReader);
                        tmp.Add(key, val);
                    }
                    return tmp;
                }
                case JSONBinaryTag.Value:
                {
                    return new JSONData(aReader.ReadString());
                }
                case JSONBinaryTag.IntValue:
                {
                    return new JSONData(aReader.ReadInt32());
                }
                case JSONBinaryTag.DoubleValue:
                {
                    return new JSONData(aReader.ReadDouble());
                }
                case JSONBinaryTag.BoolValue:
                {
                    return new JSONData(aReader.ReadBoolean());
                }
                case JSONBinaryTag.FloatValue:
                {
                    return new JSONData(aReader.ReadSingle());
                }

                default:
                {
                    throw new Exception("Error deserializing JSON. Unknown tag: " + type);
                }
            }
        }
 public override void Add(string aKey, JSONNode aItem)
 {
     var tmp = new JSONClass();
     tmp.Add(aKey, aItem);
     Set(tmp);
 }
 public override JSONNode this[string aKey]
 {
     get
     {
         return new JSONLazyCreator(this, aKey);
     }
     set
     {
         var tmp = new JSONClass();
         tmp.Add(aKey, value);
         Set(tmp);
     }
 }