internal UserDetails(UserDetails source)
		{
			UserId = source.UserId;
			IsAnonymous = source.IsAnonymous;
			DisplayName = source.DisplayName;
			FirstName = source.FirstName;
			LastName = source.LastName;
			EmailAddress = source.EmailAddress;
			MobileNumber = source.MobileNumber;
			CountryCode = source.CountryCode;
			AvatarAssetId = source.AvatarAssetId;
			SelectedTags = source.SelectedTags == null
				? new string[0]
				: source.SelectedTags.ToArray();
			AdditionalProperties = source.AdditionalProperties?.ToDictionary(
				s => s.Key,
				s => s.Value
				);
		}
        public async Task EnsureRegisteredAsync(UserDetails user, DeviceDetails device, string appVersion,
            bool isReplacement)
        {
            // store app version for ReplaceRegistration calls
            _appVersion = appVersion;

	        try
			{
				await _synchroniseLock.WaitAsync();
				await EnsureRegisteredWithoutLockAsync(user, device, appVersion, isReplacement);
			}
			finally
	        {
		        _synchroniseLock.Release();
	        }
        }
 private static User CreateServiceUser(UserDetails user)
 {
     return new User
     {
         AdditionalProperties = user.AdditionalProperties,
         AvatarAssetId = user.AvatarAssetId,
         Id = user.UserId,
         CountryCode = user.CountryCode,
         DisplayName = user.DisplayName,
         EmailAddress = user.EmailAddress,
         FirstName = user.FirstName,
         LastName = user.LastName,
         MobileNumber = user.MobileNumber
     };
 }
 private async Task UpdateUserInContext(UserDetails user, string userId, bool isAnonymous)
 {
     var userContext = user ?? new UserDetails();
     userContext.UserId = userId;
     userContext.DisplayName = userContext.DisplayName ?? userId;
     userContext.IsAnonymous = isAnonymous;
     await _registrationContext.SetUser(userContext);
 }
		private async Task RegisterAsync(UserDetails user, DeviceDetails device, string appVersion, bool alreadyLocked = false)
        {
	        try
	        {
		        if (!alreadyLocked)
		        {
			        await _synchroniseLock.WaitAsync();
		        }

		        var isAnonymous = user == null || string.IsNullOrEmpty(user.UserId);

		        _logger.LogInformation("Starting registration for user: {0}",
			        isAnonymous ? "(anonymous)" : user.UserId);

		        var request = new RegistrationDetail
		        {
			        Client = CreateClientDetail(appVersion),
			        Device = await CreateServiceDevice(device),
			        User = user == null
				        ? null
				        : CreateServiceUser(user)
		        };

		        var response = await _publicRegistrationService.RegisterAsync(request);
		        await UpdateUserInContext(user, response.UserId, isAnonymous);

		        await UpdateDeviceInContext(device);
		        _registrationContext.NetworkId = response.NetworkId;

		        _serviceContext.UpdateFromAccessDetail(response.AccessDetails);
		        await _configurationManager.UpdateConfigurationAsync(
			        response.AccessDetails.Configuration.ConfigurationItems,
			        response.AccessDetails.Configuration.ConfigurationSets);

		        _logger.LogInformation("Registered successfully.  NetworkId: {0}",
			        response.NetworkId);

				PublishRegistrationChanged();

		        _logger.LogDebug("New token expiry time: {0}", response.AccessDetails.ExpiresOn);

	        }
	        finally
	        {
		        if (!alreadyLocked)
		        {
					_synchroniseLock.Release();
				}
			}
		}
		private async Task EnsureRegisteredWithoutLockAsync(UserDetails user, DeviceDetails device, string appVersion, bool isReplacement)
		{
			var existingUser = await _registrationContext.GetUser();
			if (existingUser == null || isReplacement)
			{
				// New registration or ReplaceRegistration
				await RegisterAsync(user, device, appVersion, true);
			}

			if (isReplacement)
			{
				// Refresh token
				await _tokenRefresher.RefreshTokenAsync();
				_registrationContext.DeviceSecret = Guid.NewGuid().ToString();
			}

			PublishRegistrationChanged();
		}
        public async Task<ApiResult> UpdateRegistrationDetailsAsync(UserDetails user = null, DeviceDetails device = null)
        {
            return await ApiResult.ForOperationAsync(async () =>
            {
	            try
	            {
					await _synchroniseLock.WaitAsync();

					if (user == null && device == null)
					{
						throw new ArgumentException("user or device must be specified");
					}

					var existingUser = await _registrationContext.GetUser();
					var wasAnonymous = existingUser.IsAnonymous;
					var oldUserId = existingUser.UserId;

					if (user != null && device != null)
					{
						// Composite update
						var isAnonymous = wasAnonymous && oldUserId == user.UserId;

						try
						{
							await _secureRegistrationService.UpdateRegistrationAsync(new RegistrationDetail
							{
								Client = CreateClientDetail(),
								Device = await CreateServiceDevice(device),
								User = CreateServiceUser(user)
							});
						}
						catch (ValidationException ex)
						{
							if (ex.ValidationFailures.Count == 1 && ex.ValidationFailures.Single().FailureKey == "UserIdAlreadyTaken")
							{
								Logger.Instance.LogInformation("UserId {0} is already taken.  Starting ReplaceRegistration flow.", user.UserId);
								await EnsureRegisteredWithoutLockAsync(user, device, _appVersion, true);
								return;
							}
							else
							{
								throw;
							}
						}
						await UpdateUserInContext(user, user.UserId, isAnonymous);
						await UpdateDeviceInContext(device);
					}
					else if (user != null)
					{
						var isAnonymous = wasAnonymous && oldUserId == user.UserId;

						try
						{
							// User only update
							await _secureRegistrationService.UpdateUserAsync(CreateServiceUser(user));
						}
						catch (ValidationException ex)
						{
							if (ex.ValidationFailures.Count == 1 && ex.ValidationFailures.Single().FailureKey == "UserIdAlreadyTaken")
							{
								Logger.Instance.LogInformation("UserId {0} is already taken.  Starting ReplaceRegistration flow.", user.UserId);
								await EnsureRegisteredWithoutLockAsync(user, null, _appVersion, true);
								return;
							}
							else
							{
								throw;
							}
						}

						await UpdateUserInContext(user, user.UserId, isAnonymous);
					}
					else
					{
						// Device only update
						await _secureRegistrationService.UpdateDeviceAsync(await CreateServiceDevice(device));

						await UpdateDeviceInContext(device);
					}

					PublishRegistrationChanged();
				}
	            finally
	            {
	                _synchroniseLock.Release();
	            }
            }, "UpdateRegistrationDetailsAsync");
        }
 public async Task ReplaceRegistrationAsync(UserDetails user, DeviceDetails device)
 {
     await EnsureRegisteredAsync(user, device, _appVersion, true);
 }
		public async Task SetUser(UserDetails user)
		{
			_user = user;
			await SetValueAsync(user, "User");
		}
		public async Task<UserDetails> GetUser()
		{
			return _user ?? (_user = await GetValueAsync<UserDetails>("User"));
		}