public void DeleteMailAsync(UInt64 mailId, DeleteMailResultDelegate resultDelegate)
        {
            if (IsActive == false)
                return;

            JObject jObj = JObject.FromObject(new { mail_id = mailId });
            ApiRequest apiReq = new ApiRequest(_settings.AuthorizationTokenId, _settings.AuthorizationToken)
            {
                RequestBody = jObj.ToString()
            };
            Task.Run(async () =>
            {
                ApiRequest.ApiResponse apiResp = await apiReq.StartRequestAsync("mail/delete");
                
                bool result = apiResp.IsSuccess;
                string reason = "Unknown.";

                if (!result)
                {
                    reason = apiResp.StatusText;
                }

                if (resultDelegate != null)
                    resultDelegate(result, reason);
            });
        }
        private void GetMailResult(ApiRequest.ApiResponse response, GetMailResultDelegate resultDelegate)
        {
            bool result = response.IsSuccess;
            string reason = "";
            List<CharacterMailMessage> messages = null;

            if (result)
            {
                try
                {
                    messages = response.ResponseObject["messages"].ToObject<List<CharacterMailMessage>>();
                }
                catch (Exception e)
                {
                    messages = null;
                    reason = e.Message;
                }
            }
            else
            {
                reason = response.StatusText;
            }

            if (resultDelegate != null)
                resultDelegate(messages, reason);
        }
        public void GetMailAsync(GetMailResultDelegate resultDelegate)
        {
            if (IsActive == false)
                return;

            ApiRequest apiReq = new ApiRequest(_settings.AuthorizationTokenId, _settings.AuthorizationToken)
            {
                RequestBody = "{}"
            };
            Task.Run(async () =>
            {
                ApiRequest.ApiResponse apiResp = await apiReq.StartRequestAsync("mail/get");
                GetMailResult(apiResp, resultDelegate);
            });
        }
        private void GetFriendsResult(ApiRequest.ApiResponse response, GetFriendsResultDelegate resultDelegate)
        {
            bool result = response.IsSuccess;
            string reason = "";
            List<CharacterFriend> friends = null;

            if (result)
            {
                try
                {
                    friends = response.ResponseObject["friends"].ToObject<List<CharacterFriend>>();
                }
                catch (Exception e)
                {
                    friends = null;
                    reason = e.Message;
                }
            }
            else
            {
                reason = response.StatusText;
            }

            if (resultDelegate != null)
                resultDelegate(friends, reason);
        }
        private void UpdatePushTokenAsync()
        {
            if (IsActive == false || DevicePushToken == null)
                return;

            JObject jObj = JObject.FromObject(
                new { push_token = Base64.Encode(DevicePushToken), 
#if !DEBUG
                      platform_type = 2 /* iOS Sandbox */,
#else
                      platform_type = 1 /* iOS */, 
#endif
                });
            ApiRequest apiReq = new ApiRequest(_settings.AuthorizationTokenId, _settings.AuthorizationToken)
            {
                RequestBody = jObj.ToString()
            };
            Task.Run(async () =>
            {
                await apiReq.StartRequestAsync("update_push_token");
            });
        }
        private Tuple<LoginResult, string> ParseTokenResult(ApiRequest.ApiResponse apiResp)
        {
            if (!apiResp.IsSuccess)
            {
                return new Tuple<LoginResult, string>(LoginResult.Failed, apiResp.StatusText);
            }

            // Status already exists, this is checked by ApiRequest
            if (apiResp.ResponseObject["status"].ToObject<int>() == ApiRequest.StatusCustomBase + 1)
            {
                return new Tuple<LoginResult, string>(LoginResult.NeedPassword, apiResp.StatusText);
            }

            try
            {
                UInt64 tokenid = apiResp.ResponseObject["tokenid"].ToObject<UInt64>();
                string token = apiResp.ResponseObject["token"].ToObject<string>();
                _settings.AuthorizationTokenId = tokenid;
                _settings.AuthorizationToken = Base64.Decode(token);
            }
            catch (Exception ex)
            {
                return new Tuple<LoginResult, string>(LoginResult.Failed, ex.Message);
            }


            return new Tuple<LoginResult, string>(LoginResult.Ok, "");
        }
        private void GetTokenAsync(JObject paramsObject, LoginResultDelegate resultDelegate)
        {
            ApiRequest apiReq = new ApiRequest { RequestBody = paramsObject.ToString() };
            Task.Run(async () =>
            {
                ApiRequest.ApiResponse apiResp = await apiReq.StartRequestAsync("get_token");

                Tuple<LoginResult, string> loginResult = ParseTokenResult(apiResp);

                if (loginResult.Item1 == LoginResult.Ok)
                {
                    Tuple<UserData, string> userData = await GetUserData();
                    CurrentUserData = userData.Item1;
                    IsActive = (CurrentUserData != null);
                    if (IsActive)
                    {
                        SaveSettings();
                        UpdatePushTokenAsync();
                        resultDelegate(LoginResult.Ok, userData.Item2);
                    }
                    else
                    {
                        ClearTokens();
                        resultDelegate(LoginResult.Failed, userData.Item2);
                    }
                }
                else
                {
                    ClearTokens();
                    resultDelegate(loginResult.Item1, loginResult.Item2);
                }
            });
        }
        /// <summary>
        /// Tries to verify the saved token with the server.
        /// </summary>
        /// <param name="resultDelegate">Delegate that is called when operation is finished.</param>
        public void RestoreAsync(RestoreResultDelegate resultDelegate)
        {
            IsActive = false;

            if (_settings.AuthorizationTokenId > 0 && _settings.AuthorizationToken.Length > 0)
            {
                ApiRequest apiReq = new ApiRequest(_settings.AuthorizationTokenId, _settings.AuthorizationToken)
                {
                    RequestBody = "{}"
                };
                Task.Run(async () =>
                {
                    ApiRequest.ApiResponse apiResp = await apiReq.StartRequestAsync("verify_token");

                    if (apiResp.IsSuccess)
                    {
                        Tuple<UserData, string> userData = await GetUserData();
                        CurrentUserData = userData.Item1;
                        IsActive = (CurrentUserData != null);
                    }

                    if (IsActive)
                    {
                        UpdatePushTokenAsync();
                    }
                    else
                    {
                        ClearTokens();
                    }

                    if (resultDelegate != null)
                        resultDelegate(IsActive);
                });
            }
            else
            {
                if (resultDelegate != null)
                    Task.Run(() => resultDelegate(false));
            }
        }
        private async Task<Tuple<UserData, string>> GetUserData()
        {
            ApiRequest apiReq = new ApiRequest(_settings.AuthorizationTokenId, _settings.AuthorizationToken)
            {
                RequestBody = "{}"
            };
            ApiRequest.ApiResponse apiResp = await apiReq.StartRequestAsync("user_data");

            UserData user = null;
            string reason = apiResp.StatusText;

            if (apiResp.IsSuccess)
            {
                try
                {
                    user = apiResp.ResponseObject["user"].ToObject<UserData>();
                }
                catch (Exception ex)
                {
                    user = null;
                    reason = ex.Message;
                }
            }
            return new Tuple<UserData, string>(user, reason);
        }
        public void SendMailAsync(string recipient, string subject, string message, SendMailResultDelegate resultDelegate)
        {
            if (IsActive == false)
                return;

            JObject jObj = JObject.FromObject(new { recipient = recipient, subject = subject, message = message });
            ApiRequest apiReq = new ApiRequest(_settings.AuthorizationTokenId, _settings.AuthorizationToken)
            {
                RequestBody = jObj.ToString()
            };
            Task.Run(async () =>
            {
                ApiRequest.ApiResponse apiResp = await apiReq.StartRequestAsync("mail/send");

                if (resultDelegate == null)
                    return;

                bool result = apiResp.IsSuccess;
                string reason = "";

                if (result)
                {
                    if (apiResp.ResponseObject["status"].ToObject<int>() == ApiRequest.StatusCustomBase + 1)
                    {
                        string filteredSubject = null;
                        string filteredMessage = null;
                        try
                        {
                            filteredSubject = apiResp.ResponseObject["subject"].ToObject<string>();
                            filteredMessage = apiResp.ResponseObject["message"].ToObject<string>();
                        }
                        catch (Exception e)
                        {
                            result = false;
                            reason = e.Message;
                        }
                        resultDelegate(result, reason, filteredSubject, filteredMessage);
                        return;
                    }
                }
                else 
                { 
                    reason = apiResp.StatusText;
                }

                resultDelegate(result, reason, null, null);
            });
        }
        public void ValidateNameAsync(string characterName, ValidateNameResultDelegate resultDelegate)
        {
            if (IsActive == false)
                return;

            string[] names = characterName.Split((char [])null, StringSplitOptions.RemoveEmptyEntries);

            if (names.Length < 2)
            {
                if (resultDelegate != null)
                {
                    Task.Run(() => resultDelegate(false, "Invalid name."));
                }
                return;
            }

            JObject jObj = JObject.FromObject(new { first_name = names[0], last_name = names[1] });
            ApiRequest apiReq = new ApiRequest(_settings.AuthorizationTokenId, _settings.AuthorizationToken)
            {
                RequestBody = jObj.ToString()
            };
            Task.Run(async () =>
            {
                ApiRequest.ApiResponse apiResp = await apiReq.StartRequestAsync("character/validate_name");

                if (resultDelegate == null)
                    return;

                bool result = apiResp.IsSuccess;
                string reason = "Unknown.";

                if (result)
                {
                    try
                    {
                        bool nameValid = apiResp.ResponseObject["valid"].ToObject<bool>();
                        result = nameValid;
                    }
                    catch (Exception e)
                    {
                        result = false;
                        reason = e.Message;
                    }
                }
                else
                {
                    reason = apiResp.StatusText;
                }

                resultDelegate(result, reason);
            });
        }