/// <summary>
            /// ReleaseClientCredentials releases all resources associated with the
            /// MLTokenAgentClientCredentials structure that was returned by the library.
            /// </summary>
            /// <param name="clientCredentials">Reference to the clientCredentials object.</param>
            public static void CleanupClientCredentialsMemory(MLTokenAgent.ClientCredentials clientCredentials)
            {
                if (clientCredentials.CurrentRequest != null)
                {
                    MLPluginLog.Warning("This client has an ongoing request and cannot have it's memory released.");
                }

                IntPtr clientCredentialsPtr = clientCredentialsPtrMap.ContainsKey(clientCredentials) ? clientCredentialsPtrMap[clientCredentials] : IntPtr.Zero;

                if (clientCredentialsPtr != IntPtr.Zero)
                {
                    try
                    {
                        MLResult.Code resultCode = MLTokenAgent.NativeBindings.MLTokenAgentReleaseClientCredentials(clientCredentialsPtr);

                        if (resultCode != MLResult.Code.Ok)
                        {
                            MLPluginLog.ErrorFormat("MLTokenAgent.CleanupClientCredentialsMemory failed. Reason: {0}", resultCode);
                        }
                    }
                    catch (EntryPointNotFoundException)
                    {
                        MLPluginLog.Error("MLTokenAgent.CleanupClientCredentialsMemory failed. Reason: API symbols not found.");
                    }
                }
            }
            /// <summary>
            /// Invokes the MLTokenAgentGetClientCredentials() function asynchronously (in a different thread).
            /// </summary>
            /// <param name="clientCredentials">Reference to the clientCredentials object.</param>
            /// <returns>
            /// MLResult.Result will be <c>MLResult.Code.Ok</c> if the operation completed successfully.
            /// MLResult.Result will be <c>MLResult.Code.UnspecifiedFailure</c> if the operation failed with an unspecified error.
            /// MLResult.Result will be <c>MLResult.Code.InvalidParam</c> if the profile or out_future were 0 (null).
            /// MLResult.Result will be <c>MLResult.Code.AllocFailed</c> if the operation failed to allocate memory.
            /// MLResult.Result will be <c>MLResult.Code.PrivilegeDenied</c> if the caller does not have the ClientCredentialsRead privilege.
            /// MLResult.Result will be <c>MLResult.Code.TokenAgent*</c> if a token specific failure occurred during the operation.
            /// </returns>
            public static MLResult.Code RequestClientCredentialsAsync(MLTokenAgent.ClientCredentials clientCredentials)
            {
                IntPtr clientCredentialsFuturePtr = clientCredentialsFuturePtrMap.ContainsKey(clientCredentials) ? clientCredentialsFuturePtrMap[clientCredentials] : IntPtr.Zero;

                try
                {
                    MLResult.Code resultCode = MLTokenAgent.NativeBindings.MLTokenAgentGetClientCredentialsAsync(ref clientCredentialsFuturePtr);

                    if (MLResult.IsOK(resultCode))
                    {
                        clientCredentialsFuturePtrMap.Remove(clientCredentials);
                        clientCredentialsFuturePtrMap.Add(clientCredentials, clientCredentialsFuturePtr);
                    }
                    else if (resultCode == MLResult.Code.PrivilegeDenied)
                    {
                        MLPluginLog.Warning("MLTokenAgent.NativeBindings.RequestClientCredentialsAsync failed. Reason: Caller does not have IdentityRead Privilege.");
                    }
                    else
                    {
                        MLPluginLog.ErrorFormat("MLTokenAgent.NativeBindings.RequestClientCredentialsAsync failed. Reason: {0}", resultCode);
                    }

                    return(resultCode);
                }
                catch (EntryPointNotFoundException)
                {
                    MLPluginLog.Error("MLTokenAgent.NativeBindings.RequestClientCredentialsAsync failed. Reason: API symbols not found.");
                    return(MLResult.Code.UnspecifiedFailure);
                }
            }
            /// <summary>
            /// GetClientCredentials is a blocking function that accesses the cloud and
            /// returns a MLTokenAgentClientCredentials structure containing the users credentials and
            /// tokens for a particular service (Audience).
            /// The library deduces the Audience being requested from the name of the calling service.
            /// </summary>
            /// <param name="clientCredentials">Reference to the clientCredentials object.</param>
            /// <param name="credentials">Reference to the credentials struct of the clientCredentials object.</param>
            /// <param name="tokens">Reference to the tokens struct of the clientCredentials object.</param>
            /// <returns>
            /// MLResult.Result will be <c>MLResult.Code.Ok</c> if successful.
            /// MLResult.Result will be <c>MLResult.Code.InvalidParam</c> if failed due to internal invalid input parameter.
            /// MLResult.Result will be <c>MLResult.Code.UnspecifiedFailure</c> if failed due to internal error.
            /// MLResult.Result will be <c>MLResult.Code.AllocFailed</c> if failed to allocate memory.
            /// MLResult.Result will be <c>MLResult.Code.PrivilegeDenied</c> if necessary privilege is missing.
            /// MLResult.Result will be <c>MLResult.Code.TokenAgent*</c> if a token specific failure occurred during the operation.
            /// </returns>
            public static MLResult.Code GetClientCredentials(MLTokenAgent.ClientCredentials clientCredentials, ref MLTokenAgent.Credentials credentials, ref MLTokenAgent.Tokens tokens)
            {
                try
                {
                    IntPtr        clientCredentialsPtr = clientCredentialsPtrMap.ContainsKey(clientCredentials) ? clientCredentialsPtrMap[clientCredentials] : IntPtr.Zero;
                    MLResult.Code resultCode           = MLTokenAgent.NativeBindings.MLTokenAgentGetClientCredentials(ref clientCredentialsPtr);
                    if (MLResult.IsOK(resultCode))
                    {
                        clientCredentialsPtrMap.Remove(clientCredentials);
                        clientCredentialsPtrMap.Add(clientCredentials, clientCredentialsPtr);

                        MLTokenAgent.NativeBindings.ClientCredentialsNative clientCredentialsNative = (MLTokenAgent.NativeBindings.ClientCredentialsNative)Marshal.PtrToStructure(clientCredentialsPtr, typeof(MLTokenAgent.NativeBindings.ClientCredentialsNative));
                        credentials = clientCredentialsNative.Credentials;
                        tokens      = clientCredentials.Tokens;
                    }
                    else
                    {
                        credentials = new Credentials();
                        tokens      = new Tokens();
                    }

                    return(resultCode);
                }
                catch (EntryPointNotFoundException)
                {
                    MLPluginLog.Error("MLTokenAgent.NativeBindings.GetClientCredentials failed. Reason: API symbols not found.");
                    return(MLResult.Code.UnspecifiedFailure);
                }
            }
            /// <summary>
            /// Having made a call to MLTokenAgentGetClientCredentialsAsync(), the user can call MLTokenAgentGetClientCredentialsWait()
            /// to detect whether the asynchronous call completed and if so, to retrieve the credentials in out_credentials.
            /// </summary>
            /// <param name="clientCredentials">Reference to the clientCredentials object.</param>
            /// <param name="credentials">Reference to the credentials struct of the clientCredentials object.</param>
            /// <param name="tokens">Reference to the tokens struct of the clientCredentials object.</param>
            /// <returns>
            /// MLResult.Result will be <c>MLResult.Code.Ok</c> if the operation completed successfully before the timeout elapsed.
            /// MLResult.Result will be <c>MLResult.Code.Pending</c> if the timeout elapsed before the asynchronous call completed.
            /// MLResult.Result will be <c>MLResult.Code.UnspecifiedFailure</c> if the operation failed with an unspecified error.
            /// MLResult.Result will be <c>MLResult.Code.InvalidParam</c> if the future or out_credentials were 0 (null).
            /// MLResult.Result will be <c>MLResult.Code.AllocFailed</c> if the operation failed to allocate memory.
            /// MLResult.Result will be <c>MLResult.Code.PrivilegeDenied</c> if the caller does not have the ClientCredentialsRead privilege.
            /// MLResult.Result will be <c>MLResult.Code.TokenAgent*</c> if a token specific failure occurred during the operation.
            /// </returns>
            public static MLResult.Code ListenClientCredentialsResponse(MLTokenAgent.ClientCredentials clientCredentials, ref MLTokenAgent.Credentials credentials, ref MLTokenAgent.Tokens tokens)
            {
                try
                {
                    IntPtr clientCredentialsPtr       = clientCredentialsPtrMap.ContainsKey(clientCredentials) ? clientCredentialsPtrMap[clientCredentials] : IntPtr.Zero;
                    IntPtr clientCredentialsFuturePtr = clientCredentialsFuturePtrMap.ContainsKey(clientCredentials) ? clientCredentialsFuturePtrMap[clientCredentials] : IntPtr.Zero;

                    if (clientCredentialsFuturePtr == IntPtr.Zero)
                    {
                        MLPluginLog.Warning("MLTokenAgent.NativeBindings.ListenClientCredentialsResponse failed because a valid future pointer could not be found with the passed request.");
                        return(MLResult.Code.UnspecifiedFailure);
                    }

                    //// Attempt to get data if available, 0 is passed as a timeout to immediately return and never wait for results.
                    MLResult.Code resultCode = MLTokenAgent.NativeBindings.MLTokenAgentGetClientCredentialsWait(clientCredentialsFuturePtr, 0, ref clientCredentialsPtr);

                    // If it succeeded, copy any modifications made to the profile in unmanaged memory by the Identity API to managed memory.
                    if (MLResult.IsOK(resultCode))
                    {
                        clientCredentialsFuturePtrMap.Remove(clientCredentials);

                        clientCredentialsPtrMap.Remove(clientCredentials);
                        clientCredentialsPtrMap.Add(clientCredentials, clientCredentialsPtr);

                        MLTokenAgent.NativeBindings.ClientCredentialsNative clientCredentialsNative = (MLTokenAgent.NativeBindings.ClientCredentialsNative)Marshal.PtrToStructure(clientCredentialsPtr, typeof(MLTokenAgent.NativeBindings.ClientCredentialsNative));
                        credentials = clientCredentialsNative.Credentials;
                        tokens      = clientCredentials.Tokens;
                    }
                    else
                    {
                        credentials = new Credentials();
                        tokens      = new Tokens();
                    }

                    return(resultCode);
                }
                catch (EntryPointNotFoundException)
                {
                    MLPluginLog.Error("MLTokenAgent.NativeBindings.ListenClientCredentialsResponse failed. Reason: API symbols not found.");
                    return(MLResult.Code.UnspecifiedFailure);
                }
            }