/// <summary>
        ///     Advances to a particular state in your application. The string can be
        ///     any value of your choosing, and will show up in the dashboard.
        ///     A state is a section of your app that the user is currently in.
        /// </summary>
        public override void AdvanceTo(string state, string info,
                                       IDictionary <string, object> parameters)
        {
            if (Constants.isNoop)
            {
                return;
            }
            if (!calledStart)
            {
                CompatibilityLayer.LogError("You cannot call AdvanceTo before calling Start.");
                return;
            }

            IDictionary <string, string> requestParams = new Dictionary <string, string>();

            requestParams[Constants.Params.INFO] = info;
            if (state != null)
            {
                requestParams[Constants.Params.STATE] = state;
            }
            if (parameters != null)
            {
                requestParams[Constants.Params.PARAMS] = Json.Serialize(parameters);
            }
            LeanplumRequest.Post(Constants.Methods.ADVANCE, requestParams).Send();

            LeanplumActionManager.MaybePerformActions(ActionTrigger.State, state);
        }
        /// <summary>
        ///     Logs a particular event in your application. The string can be
        ///     any value of your choosing, and will show up in the dashboard.
        ///     To track purchases, use Leanplum.PURCHASE_EVENT_NAME as the event name.
        /// </summary>
        public void Track(string eventName, double value, string info,
                          IDictionary <string, object> parameters, IDictionary <string, string> arguments)
        {
            if (Constants.isNoop)
            {
                return;
            }
            if (!calledStart)
            {
                CompatibilityLayer.LogError("You cannot call Track before calling Start.");
                return;
            }
            IDictionary <string, string> requestParams = new Dictionary <string, string>();

            requestParams[Constants.Params.EVENT] = eventName;
            requestParams[Constants.Params.VALUE] = value.ToString();
            if (info != null)
            {
                requestParams[Constants.Params.INFO] = info;
            }
            if (parameters != null)
            {
                requestParams[Constants.Params.PARAMS] = Json.Serialize(parameters);
            }
            if (arguments != null)
            {
                foreach (string argName in arguments.Keys)
                {
                    requestParams[argName] = arguments[argName];
                }
            }
            LeanplumRequest.Post(Constants.Methods.TRACK, requestParams).Send();

            LeanplumActionManager.MaybePerformActions(ActionTrigger.Event, eventName);
        }
        public override bool ShowMessage(string id)
        {
            var messageConfig = Util.GetValueOrDefault(VarCache.Messages, id) as IDictionary <string, object>;

            if (messageConfig != null)
            {
                LeanplumActionManager.TriggerAction(id, messageConfig);
                return(true);
            }
            CompatibilityLayer.LogError($"Message not found. Message Id: {id}");
            return(false);
        }
        /// <summary>
        ///     Call this when your application starts.
        ///     This will initiate a call to Leanplum's servers to get the values
        ///     of the variables used in your app.
        /// </summary>
        public override void Start(string userId, IDictionary <string, object> attributes,
                                   Leanplum.StartHandler startResponseAction)
        {
            if (calledStart)
            {
                CompatibilityLayer.Log("Already called start");
                return;
            }
            if (String.IsNullOrEmpty(LeanplumRequest.AppId))
            {
                CompatibilityLayer.LogError("You cannot call Start without setting your app's " +
                                            "API keys.");
                return;
            }

            if (startResponseAction != null)
            {
                Started += startResponseAction;
            }
            if (Constants.isNoop)
            {
                _hasStarted     = true;
                calledStart     = true;
                startSuccessful = true;
                OnVariablesChanged();
                OnVariablesChangedAndNoDownloadsPending();
                OnStarted(true);
                VarCache.ApplyVariableDiffs(null, null);
                return;
            }

            ValidateAttributes(attributes);
            calledStart = true;

            // Load the variables that were stored on the device from the last session.
            VarCache.IsSilent = true;
            VarCache.LoadDiffs();
            VarCache.IsSilent = false;

            // Setup class members.
            VarCache.Update += delegate
            {
                OnVariablesChanged();
                if (LeanplumRequest.PendingDownloads == 0)
                {
                    OnVariablesChangedAndNoDownloadsPending();
                }
            };

            LeanplumRequest.NoPendingDownloads += delegate
            {
                OnVariablesChangedAndNoDownloadsPending();
            };

            string deviceId;

            if (customDeviceId != null)
            {
                deviceId = customDeviceId;
            }
            else
            {
                deviceId = CompatibilityLayer.GetDeviceId();
            }
            LeanplumRequest.DeviceId = deviceId;

            // load saved inbox messages
            if (Inbox is LeanplumInboxNative native)
            {
                native.Load();
            }

            // Don't overwrite UserID if it was set previously if Start()
            // was called without a new UserID.
            if (!String.IsNullOrEmpty(userId))
            {
                LeanplumRequest.UserId = userId;
            }
            if (String.IsNullOrEmpty(LeanplumRequest.UserId))
            {
                LeanplumRequest.UserId = deviceId;
            }

            // Setup parameters.
            var parameters = new Dictionary <string, string>();

            if (IsDeveloperModeEnabled())
            {
                parameters[Constants.Params.INCLUDE_DEFAULTS] = _includeDefaults.ToString();
            }
            else
            {
                parameters[Constants.Params.INCLUDE_DEFAULTS] = false.ToString();
            }
            parameters[Constants.Params.VERSION_NAME]          = CompatibilityLayer.VersionName ?? "";
            parameters[Constants.Params.DEVICE_NAME]           = CompatibilityLayer.GetDeviceName();
            parameters[Constants.Params.DEVICE_MODEL]          = CompatibilityLayer.GetDeviceModel();
            parameters[Constants.Params.DEVICE_SYSTEM_NAME]    = CompatibilityLayer.GetSystemName();
            parameters[Constants.Params.DEVICE_SYSTEM_VERSION] =
                CompatibilityLayer.GetSystemVersion();
            var timezone = TimeZoneInfo.Local;

            if (timezone.IsDaylightSavingTime(DateTime.UtcNow))
            {
                parameters[Constants.Keys.TIMEZONE] = timezone.DaylightName;
            }
            else
            {
                parameters[Constants.Keys.TIMEZONE] = timezone.StandardName;
            }
            parameters[Constants.Keys.TIMEZONE_OFFSET_SECONDS] =
                timezone.GetUtcOffset(DateTime.UtcNow).TotalSeconds.ToString();
            parameters[Constants.Keys.COUNTRY]  = Constants.Values.DETECT;
            parameters[Constants.Keys.REGION]   = Constants.Values.DETECT;
            parameters[Constants.Keys.CITY]     = Constants.Values.DETECT;
            parameters[Constants.Keys.LOCATION] = Constants.Values.DETECT;
            if (attributes != null)
            {
                parameters[Constants.Params.USER_ATTRIBUTES] = Json.Serialize(attributes);
            }

            parameters[Constants.Keys.INBOX_MESSAGES] = Json.Serialize(Inbox.MessageIds);


            // Issue start API call.
            LeanplumRequest req = LeanplumRequest.Post(Constants.Methods.START, parameters);

            req.Response += delegate(object responsesObject)
            {
                IDictionary <string, object> response       = Util.GetLastResponse(responsesObject) as IDictionary <string, object>;
                IDictionary <string, object> messages       = Util.GetValueOrDefault(response, "messages") as IDictionary <string, object> ?? new Dictionary <string, object>();
                IDictionary <string, object> values         = Util.GetValueOrDefault(response, Constants.Keys.VARS) as IDictionary <string, object> ?? new Dictionary <string, object>();
                IDictionary <string, object> fileAttributes = Util.GetValueOrDefault(response, Constants.Keys.FILE_ATTRIBUTES) as IDictionary <string, object> ?? new Dictionary <string, object>();
                List <object> variants = Util.GetValueOrDefault(response, Constants.Keys.VARIANTS) as List <object> ?? new List <object>();

                bool isRegistered = (bool)Util.GetValueOrDefault(response, Constants.Keys.IS_REGISTERED, false);
                bool syncInbox    = (bool)Util.GetValueOrDefault(response, Constants.Keys.SYNC_INBOX, false);

                LeanplumRequest.Token = Util.GetValueOrDefault(response, Constants.Keys.TOKEN) as
                                        string ?? "";

                // Download inbox messages
                if (syncInbox)
                {
                    if (Inbox is LeanplumInboxNative nativeInbox)
                    {
                        nativeInbox.DownloadMessages();
                    }
                }

                // Allow bidirectional realtime variable updates.
                if (Constants.isDevelopmentModeEnabled)
                {
                    VarCache.SetDevModeValuesFromServer(
                        Util.GetValueOrDefault(response, Constants.Keys.VARS_FROM_CODE) as
                        Dictionary <string, object>);

#if !UNITY_WEBGL
                    if (Constants.EnableRealtimeUpdatesInDevelopmentMode &&
                        SocketUtilsFactory.Utils.AreSocketsAvailable)
                    {
                        leanplumSocket = new LeanplumSocket(delegate()
                        {
                            // Callback when we receive an updateVars command through the
                            // development socket.
                            // Set a flag so that the next time VarCache.CheckVarsUpdate() is
                            // called the variables are updated.
                            VarCache.VarsNeedUpdate = true;
                        },
                                                            LeanplumActionManager.TriggerPreview);
                    }
#endif
                    // Register device.
                    if (isRegistered)
                    {
                        // Check for updates.
                        string latestVersion = Util.GetValueOrDefault(response,
                                                                      Constants.Keys.LATEST_VERSION) as string;
                        if (latestVersion != null)
                        {
                            CompatibilityLayer.Log("Leanplum Unity SDK " + latestVersion +
                                                   " available. Go to https://www.leanplum.com/dashboard to " +
                                                   "download it.");
                        }
                        OnHasStartedAndRegisteredAsDeveloper();
                    }
                }

                VarCache.ApplyVariableDiffs(values, messages, fileAttributes, variants);
                _hasStarted     = true;
                startSuccessful = true;
                OnStarted(true);
                CompatibilityLayer.Init();

                LeanplumActionManager.MaybePerformActions(ActionTrigger.StartOrResume);
            };
            req.Error += delegate
            {
                VarCache.ApplyVariableDiffs(null, null);
                _hasStarted     = true;
                startSuccessful = false;
                OnStarted(false);
                CompatibilityLayer.Init();

                LeanplumActionManager.MaybePerformActions(ActionTrigger.StartOrResume);
            };
            req.SendIfConnected();
        }