/// <summary>
        /// Creates a blob associated with a session.
        /// </summary>
        /// <param name="session">A Smash session object.</param>
        /// <param name="fileExtension">File extension for the blob.</param>
        /// <param name="state">State to be passed as userState in the completion event args.</param>
        public void CreateBlobAsync(SmashSession session, string fileExtension, object state)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }

            IAsyncResult asyncResult = SmashClientREST.CreateBlobAsync(
                session.ClientIdentity,
                session.MeetingToken,
                session.SessionID,
                session.ClientID,
                fileExtension,
                new ServiceAgent<Contracts.CreateBlobResponse>.OnCompleteDelegate(
                    (response) =>
                    {
                        CreateBlobCompletedArgs e = new CreateBlobCompletedArgs(response.Exception, response.Aborted, response.StateObject);
                        if (response.Exception == null && !response.Aborted)
                        {
                            SmashBlobUploader blobUploader = new SmashBlobUploader(session.ClientIdentity, session.MeetingToken, session.SessionID, session.ClientID, response.BlobAddress, response.BlobSharedSignature);

                            e.BlobUploader = blobUploader;
                        }
                        OnCreateBlobCompleted(e);
                    }),
                state);

            SmashClientREST.HandleCompletion(asyncResult, state);
        }
        /// <summary>
        /// Joins a Smash session. User+email+device must be unique across all joinees of a session.
        /// </summary>
        /// <param name="identity">ClientIdentity object used for authentication.</param>
        /// <param name="dispatcher">The Dispatcher object. a dispatcher must be specified in order to use data binding with Smash.</param>
        /// <param name="meetingToken">The meeting token of the session to join.</param>
        /// <param name="user">User name joining the session.</param>
        /// <param name="email">email address of the user joining the session.</param>
        /// <param name="device">Device name of the user joining the session.</param>
        /// <param name="forceRejoin">A value of 'true' overrides user+email+device uniqueness requirement for joining a session.</param>
        /// <param name="tables">Array of ISmashTable for all SmashTable objects to be associated with the joined session.</param>
        /// <param name="state">State to be passed as userState in the completion event args.</param>
        public void JoinSessionAsync(ClientIdentity identity, Dispatcher dispatcher, Guid meetingToken, string user, string email, string device, bool forceRejoin, ISmashTable[] tables, object state)
        {
            if (tables == null)
            {
                throw new ArgumentNullException("tables");
            }

            if (tables.Length == 0)
            {
                throw new ArgumentException("tables.Length==0");
            }

            Dictionary<int, ISmashTable> uniquenessCheck = new Dictionary<int, ISmashTable>();
            foreach (var table in tables)
            {
                int hash = table.TypeHash;

                if (uniquenessCheck.ContainsKey(hash))
                {
                    throw new ArgumentException("tables: Two tables with identical types and names provided");
                }

                uniquenessCheck.Add(hash,table);
            }

            IAsyncResult asyncResult = SmashClientREST.JoinSessionAsync(
                identity,
                meetingToken,
                user,
                email,
                device,
                forceRejoin,
                new ServiceAgent<Contracts.JoinSessionResponse>.OnCompleteDelegate(
                    (response) =>
                    {
                        JoinSessionCompletedArgs e = new JoinSessionCompletedArgs(response.Exception, response.Aborted, response.StateObject);
                        if (response.Exception == null && !response.Aborted)
                        {
                            SmashSession session = new SmashSession(identity, dispatcher, meetingToken, response.SessionID, response.ClientID, tables);

                            e.Session = session;
                        }
                        OnJoinSessionCompleted(e);
                    }),
                state);

            SmashClientREST.HandleCompletion(asyncResult, state);
        }
        /// <summary>
        /// Modifies a Smash session's attendee list.
        /// </summary>
        /// <param name="session">Smash session object.</param>
        /// <param name="managementID">The owner's management secret required to enumerate, modify, wipe sessions.</param>
        /// <param name="attendeesAdd">List of user names to add.</param>
        /// <param name="attendeesRemove">List of user names to delete.</param>
        /// <param name="state">State to be passed as userState in the completion event args.</param>
        public void ModifySessionAsync(SmashSession session, Guid managementID, IEnumerable<string> attendeesAdd, IEnumerable<string> attendeesRemove, object state)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }

            IAsyncResult asyncResult = SmashClientREST.ModifySessionAsync(
                session.ClientIdentity,
                session.MeetingToken,
                session.SessionID,
                managementID,
                attendeesAdd,
                attendeesRemove,
                new ServiceAgent<Contracts.ModifySessionResponse>.OnCompleteDelegate(
                    (response) =>
                    {
                        ModifySessionCompletedArgs e = new ModifySessionCompletedArgs(response.Exception, response.Aborted, response.StateObject);
                        OnModifySessionCompleted(e);
                    }),
                state);

            SmashClientREST.HandleCompletion(asyncResult, state);
        }