public IEnumerator SendCoroutine()
        {
            AddDefaultParameters();

            WWWForm requestForm = new WWWForm ();
            requestForm.AddField ("name", this.eventName);
            requestForm.AddField ("data", this.data.ToString ());
            if (!this.customData.IsNull) {
                requestForm.AddField ("customData", this.customData.Print(false));
            } else {
                requestForm.AddField ("customData", "{}");
            }

            requestForm.AddField ("ts", "1470057439857");
            requestForm.AddField ("queued", 0);
            requestForm.AddField ("debugMode", "true");

            WWW request = new WWW ("https://apptracker.spilgames.com/v1/native-events/event/android/" + Spil.BundleIdEditor + "/" + this.eventName, requestForm);
            yield return request;
            //			while (!request.isDone);
            if (request.error != null) {
                Debug.LogError ("Error getting data: " + request.error);
                Debug.LogError ("Error getting data: " + request.text);
            } else {
                JSONObject serverResponse = new JSONObject (request.text);
                Debug.Log ("Data returned: " + serverResponse.ToString());
                ResponseEvent.Build(serverResponse);
            }
        }
        public static void ProcessPackagesResponse(ResponseEvent response)
        {
            if(response.data != null){
                if(response.data.HasField("packages")){
                    JSONObject json = new JSONObject();
                    json.AddField("data", response.data.GetField("packages").Print(false));
                    GamePackagesData = JsonHelper.getObjectFromJson<List<PackageData>>(json.GetField("data").str);
                }

                if(response.data.HasField("promotions")){
                    JSONObject json = new JSONObject();
                    json.AddField("data", response.data.GetField("promotions").Print(false));
                    GamePromotionData = JsonHelper.getObjectFromJson<List<PromotionData>>(json.GetField("data").str);
                }

            }
        }
        private void AddDefaultParameters()
        {
            this.data.AddField("uid", this.uid);
            this.data.AddField("locale", "en");
            this.data.AddField("appVersion", "1.0");
            this.data.AddField("apiVersion", SpilUnityImplementationBase.PluginVersion);
            this.data.AddField("osVersion", "1.0");
            this.data.AddField("os", "android");
            this.data.AddField("deviceModel", "Editor");
            this.data.AddField("timezoneOffset", "0");
            this.data.AddField("tto", "200");
            this.data.AddField("packageName", Spil.BundleIdEditor);
            this.data.AddField ("sessionId", "deadbeef");
            this.data.AddField("pluginName", Response.pluginName);
            this.data.AddField("pluginVersion", Response.pluginVersion);

            if(Response.provider != null && Response.externalId != null){
                JSONObject externalUserIdJson = new JSONObject();
                externalUserIdJson.AddField("provider", Response.provider);
                externalUserIdJson.AddField("userId", Response.externalId);

                this.data.AddField("externalUserId", externalUserIdJson);
            }
        }
		/// <summary>
		/// Sends an event to the native Spil SDK which will send a request to the back-end.
		/// Custom events may be tracked as follows:
		/// To track an simple custom event, simply call Spil.Instance.SendCustomEvent(String eventName); from anywhere in your code.
		/// To pass more information with the event create a &lt;String, String&gt; Dictionary and pass that as the second parameter like so:
		/// Dictionary&lt;String, String&gt; eventParams = new Dictionary&lt;String,String&gt;();
		/// eventParams.Add(“level”,levelName);
		/// Spil.Instance.SendCustomEvent(“PlayerDeath”, eventParams);
		/// See https://github.com/spilgames/spil_event_unity_plugin for more information on events.
		/// This method was previously called "TrackEvent".
		/// </summary>
		/// <param name="eventName"></param>
		/// <param name="dict"></param>
		public override void SendCustomEvent(string eventName, Dictionary<string,string> dict)
		{
			if (dict != null)
			{
				// Create a new dict json string
				string jsonString = "{";

				// Add each passed kv to the json dict
				foreach (var item in dict) {
					string key = item.Key;
					object value = item.Value;
					jsonString += "\"" + key + "\":";

					// Detect the value type
					try {
						string jsonInputString = item.Value.Replace("\\\"", "\"").Trim(new char[]{'\"'});
						JSONObject inputJsonObject = new JSONObject(jsonInputString);
						if (inputJsonObject.IsArray || inputJsonObject.IsObject) {
							jsonString += jsonInputString;
						} else {
							jsonString += "\"" + value + "\"";
						}
					} catch (Exception e) {
						Debug.Log ("---JSON DETECTION FAILED" + e.Message);
						jsonString += "\"" + value + "\"";
					}

					jsonString += ",";
				}

				// Close the json dict
				if (jsonString.EndsWith(",")){
					jsonString = jsonString.Substring(0, jsonString.Length - 1);
				}
				jsonString += "}";

				Debug.Log ("---JSON BUILDED:" + jsonString);

				if (jsonString != "{}") {
					trackEventWithParamsNative (eventName, jsonString);
				} else {
					trackEventNative(eventName);
				}
			} else {
				trackEventNative(eventName);
			}
		}
		private void CheckForRemoteNotifications()
		{
	bool proccessedNotifications = false;
	#if UNITY_IPHONE
	#if UNITY_5
			if (UnityEngine.iOS.NotificationServices.remoteNotificationCount > 0)
			{			
				foreach(UnityEngine.iOS.RemoteNotification notification in 	UnityEngine.iOS.NotificationServices.remoteNotifications)
				{
	#else
	if (UnityEngine.NotificationServices.remoteNotificationCount > 0)
	{			
	foreach(UnityEngine.RemoteNotification notification in 	UnityEngine.NotificationServices.remoteNotifications)
	{
	#endif
					foreach(var key in notification.userInfo.Keys)
					{
						if(notification.userInfo[key].GetType() == typeof(Hashtable))
						{
							Hashtable userInfo = (Hashtable) notification.userInfo[key];
							JSONObject notificationPayload = new JSONObject();
							foreach(var pKey in userInfo.Keys)
							{
								if(userInfo[pKey].GetType() == typeof(string))
								{
									string keyStr = pKey.ToString();
									string value = userInfo[pKey].ToString();
									notificationPayload.AddField(keyStr,value);
								}
								if(userInfo[pKey].GetType() == typeof(Hashtable))
								{
									JSONObject innerJson = new JSONObject();
									Hashtable innerTable = (Hashtable)userInfo[pKey];
									foreach(var iKey in innerTable.Keys)
									{
										string iKeyStr = iKey.ToString();
										if(innerTable[iKey].GetType() == typeof(Hashtable))
										{
											Hashtable innerTableB = (Hashtable)innerTable[iKey];
											JSONObject innerJsonB = new JSONObject();
											foreach(var bKey in innerTableB.Keys)
											{
												innerJsonB.AddField(bKey.ToString(),innerTableB[bKey].ToString());
											}
											innerJson.AddField(iKeyStr,innerJsonB);
										}
										if(innerTable[iKey].GetType() == typeof(string))
										{
											string iValue = innerTable[iKey].ToString();
											innerJson.AddField(iKeyStr,iValue);
										}
									}
									string keyStr = pKey.ToString();
									notificationPayload.AddField(keyStr,innerJson);
								}
							}

							String notificationJsonForNative = notificationPayload.ToString().Replace("'","\"");
							if(!proccessedNotifications){									
								SendCustomEvent("notificationReceived", new Dictionary<string, string>() { { "notificationPayload", notificationJsonForNative}});
								proccessedNotifications = true;
							}
						}
					}
				}
	#if UNITY_5
				UnityEngine.iOS.NotificationServices.ClearRemoteNotifications();
	#else
	UnityEngine.NotificationServices.ClearRemoteNotifications();
	#endif
			} else {
				Debug.Log("NO REMOTE NOTIFICATIONS FOUND");
			}
	#endif
		}
		/// <summary>
		/// Sends an event to the native Spil SDK which will send a request to the back-end.
		/// Custom events may be tracked as follows:
		/// To track an simple custom event, simply call Spil.Instance.SendCustomEvent(String eventName); from anywhere in your code.
		/// To pass more information with the event create a &lt;String, String&gt; Dictionary and pass that as the second parameter like so:
		/// Dictionary&lt;String, String&gt; eventParams = new Dictionary&lt;String,String&gt;();
		/// eventParams.Add(“level”,levelName);
		/// Spil.Instance.SendCustomEvent(“PlayerDeath”, eventParams);
		/// See https://github.com/spilgames/spil_event_unity_plugin for more information on events.
		/// This method was previously called "TrackEvent".
		/// </summary>
		/// <param name="eventName"></param>
		/// <param name="dict"></param>
		public override void SendCustomEvent(string eventName, Dictionary<string, object> dict)
		{
			if (dict != null)
			{
				// Create a new dict json string
				string jsonString = "{";

				// Add each passed kv to the json dict
				foreach (var item in dict) {
					string key = item.Key;
					object value = item.Value;
					jsonString += "\"" + key + "\":";

					// Detect the value type
					if (value != null) {
						if (value is String) {
							// Determine if there is nested json included in the json, in that case reformat it
							try {
								string jsonInputString = ((string)item.Value).Replace ("\\\"", "\"").Trim (new char[]{ '\"' });
								JSONObject inputJsonObject = new JSONObject (jsonInputString);
								if (inputJsonObject.IsArray || inputJsonObject.IsObject) {
									jsonString += jsonInputString;
								} else {
									jsonString += "\"" + value + "\"";
								}
							} catch (Exception e) {
								Debug.Log ("---JSON DETECTION FAILED" + e.Message);
								jsonString += "\"" + value + "\"";
							}
						} else if (value is bool) {
							// Value is a bool, add it without quotes
							jsonString += (bool)value ? "true" : "false";
						} else if (value is int || value is float || value is double || value is long) {
							// Value is a number, add it without quotes
							jsonString += value.ToString ();
						} else if (value is JSONObject) {
							jsonString += ((JSONObject)value).Print();
						} else {
							try {
								jsonString += JsonHelper.getJSONFromObject(value);
							} catch (Exception) {
								// Value is unknown or not supported
								jsonString += "\"INVALID PARAMETER TYPE\"";
								Debug.LogError ("---INVALID JSON FOR KEY: " + key + ", expected type: string, bool, int, float, double, long");
							}
						}
					} else {
						// Value is empty
						jsonString += "\"\"";
					}

					jsonString += ",";
				}

				// Close the json dict
				if (jsonString.EndsWith(",")){
					jsonString = jsonString.Substring(0, jsonString.Length - 1);
				}
				jsonString += "}";

				Debug.Log ("---JSON BUILDED:" + jsonString);

				if (jsonString != "{}") {
					trackEventWithParamsNative (eventName, jsonString);
				} else {
					trackEventNative(eventName);
				}
			} else {
				trackEventNative(eventName);
			}
		}
		/// <summary>
		/// This method is marked as internal and should not be exposed to developers.
		/// The Spil Unity SDK is not packaged as a seperate assembly yet so this method is currently visible, this will be fixed in the future.
		/// Internal method names start with a lower case so you can easily recognise and avoid them.
		/// </summary>
		internal override void SpilInit()
		{
			JSONObject options = new JSONObject();
			options.AddField ("isUnity",true);
			initEventTrackerWithOptions(options.ToString());
			applicationDidBecomeActive();

            if (disableAutomaticRegisterForPushNotifications == false) 
			{
                RegisterForPushNotifications ();
				CheckForRemoteNotifications();
			}
		}
	    /*
	     * The Merge function is experimental. Use at your own risk.
	     */
	    public void Merge(JSONObject obj) {
		    MergeRecur(this, obj);
	    }
	    /// <summary>
	    /// Merge object right into left recursively
	    /// </summary>
	    /// <param name="left">The left (base) object</param>
	    /// <param name="right">The right (new) object</param>
	    static void MergeRecur(JSONObject left, JSONObject right) {
		    if(left.type == Type.NULL)
			    left.Absorb(right);
		    else if(left.type == Type.OBJECT && right.type == Type.OBJECT) {
			    for(int i = 0; i < right.list.Count; i++) {
				    string key = right.keys[i];
				    if(right[i].isContainer) {
					    if(left.HasField(key))
						    MergeRecur(left[key], right[i]);
					    else
						    left.AddField(key, right[i]);
				    } else {
					    if(left.HasField(key))
						    left.SetField(key, right[i]);
					    else
						    left.AddField(key, right[i]);
				    }
			    }
		    } else if(left.type == Type.ARRAY && right.type == Type.ARRAY) {
			    if(right.Count > left.Count) {
				    Debug.LogError("Cannot merge arrays when right object has more elements");
				    return;
			    }
			    for(int i = 0; i < right.list.Count; i++) {
				    if(left[i].type == right[i].type) {			//Only overwrite with the same type
					    if(left[i].isContainer)
						    MergeRecur(left[i], right[i]);
					    else {
						    left[i] = right[i];
					    }
				    }
			    }
		    }
	    }
	    public void SetField(string name, JSONObject obj) {
		    if(HasField(name)) {
			    list.Remove(this[name]);
			    keys.Remove(name);
		    }
		    AddField(name, obj);
	    }
	    public void AddField(string name, JSONObject obj) {
		    if(obj) {		//Don't do anything if the object is null
			    if(type != Type.OBJECT) {
				    if(keys == null)
					    keys = new List<string>();
				    if(type == Type.ARRAY) {
					    for(int i = 0; i < list.Count; i++)
						    keys.Add(i + "");
				    } else
					    if(list == null)
						    list = new List<JSONObject>();
				    type = Type.OBJECT;		//Congratulations, son, you're an OBJECT now
			    }
			    keys.Add(name);
			    list.Add(obj);
		    }
	    }
	    public void Add(JSONObject obj) {
		    if(obj) {		//Don't do anything if the object is null
			    if(type != Type.ARRAY) {
				    type = Type.ARRAY;		//Congratulations, son, you're an ARRAY now
				    if(list == null)
					    list = new List<JSONObject>();
			    }
			    list.Add(obj);
		    }
	    }
	    public void Absorb(JSONObject obj) {
		    list.AddRange(obj.list);
		    keys.AddRange(obj.keys);
		    str = obj.str;
		    n = obj.n;
		    b = obj.b;
		    type = obj.type;
	    }
	    public JSONObject(JSONObject[] objs) {
		    type = Type.ARRAY;
		    list = new List<JSONObject>(objs);
	    }