/// <summary>
        /// A method used to get the HTTP method value according to the WOPI operation name.
        /// </summary>
        /// <param name="operationName">A parameter represents the WOPI operation name.</param>
        /// <returns>A return value represents the HTTP method value for the WOPI operation.</returns>
        private string GetHttpMethodForWOPIOperation(WOPIOperationName operationName)
        {
            string httpMethod = string.Empty;

            switch (operationName)
            {
            case WOPIOperationName.CheckFileInfo:
            case WOPIOperationName.CheckFolderInfo:
            case WOPIOperationName.GetFile:
            case WOPIOperationName.EnumerateChildren:
            {
                httpMethod = WebRequestMethods.Http.Get;
                break;
            }

            default:
            {
                httpMethod = WebRequestMethods.Http.Post;
                break;
            }
            }

            return(httpMethod);
        }
        /// <summary>
        /// A method is used to get the X-WOPI-Override header value according to the operation name.
        /// </summary>
        /// <param name="operationName">A parameter represents the operation name.</param>
        /// <returns>A return value represents the X-WOPI-Override header value which is used by specified operation.</returns>
        private string GetTheXWOPIOverrideHeaderValue(WOPIOperationName operationName)
        {
            string valueTemp = string.Empty;

            switch (operationName)
            {
            case WOPIOperationName.Lock:
            case WOPIOperationName.UnlockAndRelock:
            {
                valueTemp = "LOCK";
                break;
            }

            case WOPIOperationName.PutFile:
            {
                valueTemp = "PUT";
                break;
            }

            case WOPIOperationName.UnLock:
            {
                valueTemp = "UNLOCK";
                break;
            }

            case WOPIOperationName.PutRelativeFile:
            {
                valueTemp = "PUT_RELATIVE";
                break;
            }

            case WOPIOperationName.RefreshLock:
            {
                valueTemp = "REFRESH_LOCK";
                break;
            }

            case WOPIOperationName.ExecuteCellStorageRequest:
            case WOPIOperationName.ExecuteCellStorageRelativeRequest:
            {
                valueTemp = "COBALT";
                break;
            }

            case WOPIOperationName.DeleteFile:
            {
                valueTemp = "DELETE";
                break;
            }

            case WOPIOperationName.GetRestrictedLink:
            {
                valueTemp = "GET_RESTRICTED_LINK";
                break;
            }

            case WOPIOperationName.RevokeRestrictedLink:
            {
                valueTemp = "REVOKE_RESTRICTED_LINK";
                break;
            }

            case WOPIOperationName.ReadSecureStore:
            {
                valueTemp = "READ_SECURE_STORE";
                break;
            }

            default:
            {
                string errorMsg = string.Format(@"There is no valid [X-WOPI-Override] header value for operation[{0}]", operationName);
                throw new InvalidOperationException(errorMsg);
            }
            }

            return(valueTemp);
        }
        /// <summary>
        /// A method used to log the HTTP transport information.
        /// </summary>
        /// <param name="wopiRequest">A parameter represents the request instance of a WOPI operation. It must not be null.</param>
        /// <param name="requestBody">A parameter represents the request body of a WOPI operation.</param>
        /// <param name="wopiResponse">A parameter represents the response of a WOPI operation. It must not be null if the "isResponse" parameter is true.</param>
        /// <param name="operationName">A parameter represents the WOPI operation name.</param>
        /// <param name="isResponse">A parameter represents the HTTP transport information recorded by this method whether belong to the response of a WOPI operation.</param>
        private void LogHttpTransportInfo(HttpWebRequest wopiRequest, byte[] requestBody, WOPIHttpResponse wopiResponse, WOPIOperationName operationName, bool isResponse = true)
        {
            #region validate parameters
            if (isResponse)
            {
                if (null == wopiResponse)
                {
                    throw new ArgumentNullException("wopiResponse");
                }

                // For log response, requires the request URL.
                if (null == wopiRequest)
                {
                    throw new ArgumentNullException("wopiRequest");
                }
            }
            else
            {
                if (null == wopiRequest)
                {
                    throw new ArgumentNullException("wopiRequest");
                }
            }
            #endregion

            #region headers

            // Build headers information
            StringBuilder       headerInfoBuilder = new StringBuilder();
            WebHeaderCollection headers           = isResponse ? wopiResponse.Headers : wopiRequest.Headers;
            if (null != headers && 0 != headers.Count)
            {
                foreach (string oheaderNameItem in headers.AllKeys)
                {
                    string headerValueString = string.Format(
                        "[{0}]:({1})",
                        oheaderNameItem,
                        headers[oheaderNameItem]);

                    headerInfoBuilder.AppendLine(headerValueString);
                }
            }

            #endregion

            #region body information

            // Build body information
            byte[] httpBodyOfResponse = null;
            if (isResponse && 0 != wopiResponse.ContentLength)
            {
                httpBodyOfResponse = WOPIResponseHelper.GetContentFromResponse(wopiResponse);
            }

            byte[]        httpBody        = isResponse ? httpBodyOfResponse : requestBody;
            StringBuilder bodyInfoBuilder = new StringBuilder();
            if (null != httpBody && 0 != httpBody.Length)
            {
                switch (operationName)
                {
                case WOPIOperationName.CheckFileInfo:
                case WOPIOperationName.ReadSecureStore:
                case WOPIOperationName.CheckFolderInfo:
                case WOPIOperationName.EnumerateChildren:
                {
                    if (isResponse)
                    {
                        // log response body by JSON format
                        bodyInfoBuilder.AppendLine(Encoding.UTF8.GetString(httpBody));
                    }

                    break;
                }

                case WOPIOperationName.PutRelativeFile:
                {
                    if (isResponse)
                    {
                        // log the body by JSON format
                        bodyInfoBuilder.AppendLine(Encoding.UTF8.GetString(httpBody));
                    }
                    else
                    {
                        // log the body as bytes string value
                        bodyInfoBuilder.AppendLine(WOPIResponseHelper.GetBytesStringValue(httpBody));
                    }

                    break;
                }

                case WOPIOperationName.GetFile:
                {
                    if (isResponse)
                    {
                        // log the body as bytes string value
                        bodyInfoBuilder.AppendLine(WOPIResponseHelper.GetBytesStringValue(httpBody));
                    }

                    break;
                }

                case WOPIOperationName.PutFile:
                {
                    if (!isResponse)
                    {
                        // log the body as bytes string value
                        bodyInfoBuilder.AppendLine(WOPIResponseHelper.GetBytesStringValue(httpBody));
                    }

                    break;
                }
                }
            }

            #endregion

            string credentialInfo = string.Format(
                "User:[{0}] Domain:[{1}]",
                this.defaultUserName,
                this.defaultDomain);

            string logTitle = string.Format(
                "{0} HTTP {1}{2} for [{3}] operation:",
                isResponse ? "Receive" : "Sending",
                isResponse ? "Response" : "Request",
                isResponse ? string.Empty : " with " + credentialInfo,
                operationName);

            StringBuilder logBuilder = new StringBuilder();
            logBuilder.AppendLine(logTitle);

            string urlInfor = string.Format("Request URL:[{0}]", wopiRequest.RequestUri.AbsoluteUri);
            logBuilder.AppendLine(urlInfor);

            string httpMethodValue = string.Format("HTTP method:[{0}]", this.GetHttpMethodForWOPIOperation(operationName));
            logBuilder.AppendLine(httpMethodValue);

            if (isResponse)
            {
                string httpStatusCodeValue = string.Format("HTTP status code:[{0}]", wopiResponse.StatusCode);
                logBuilder.AppendLine(httpStatusCodeValue);
            }

            string headerInfo = string.Format("Headers:\r\n{0}", 0 == headerInfoBuilder.Length ? "None" : headerInfoBuilder.ToString());
            logBuilder.AppendLine(headerInfo);

            string bodyInfo = string.Format("Body:\r\n{0}", 0 == bodyInfoBuilder.Length ? "None" : bodyInfoBuilder.ToString());
            logBuilder.AppendLine(bodyInfo);

            this.Site.Log.Add(LogEntryKind.Debug, logBuilder.ToString());
        }
        /// <summary>
        /// A method is used to record information about web exception which is thrown by calling protocol operations.
        /// </summary>
        /// <param name="targetResourceUrl">A parameter represents the request URL which cause a web exception.</param>
        /// <param name="wopiOperationName">A parameter represents the protocol operation name which sends a http request and then get a web exception.</param>
        /// <param name="webException">A parameter represents web exception instance.</param>
        protected virtual void RecordWebExceptionInformation(string targetResourceUrl, WOPIOperationName wopiOperationName, WebException webException)
        {
            string errorHeaderValue = this.GetWebExceptionHeadersValue(webException);

            if (!string.IsNullOrEmpty(errorHeaderValue))
            {
                this.Site.Log.Add(
                    LogEntryKind.Debug,
                    "Perform the [{0}] operation fail. Request URL:[{1}]\r\nError headers:\r\n{2}",
                    wopiOperationName,
                    targetResourceUrl,
                    errorHeaderValue);
            }
        }
        /// <summary>
        /// A method is used to send http request for MS-WOPI operation.
        /// </summary>
        /// <param name="targetResourceUrl">A parameter represents the target resource Uri.</param>
        /// <param name="headers">A parameter represents the headers which is included in the http request.</param>
        /// <param name="body">A parameter represents the body contents which is sent in the http request.</param>
        /// <param name="operationName">A parameter represents the WOPI operation which the http request belongs to.</param>
        /// <returns>A return value represents the http response of the http request which is sent with specified header, URI, body and http method. </returns>
        protected virtual WOPIHttpResponse SendWOPIRequest(string targetResourceUrl, WebHeaderCollection headers, byte[] body, WOPIOperationName operationName)
        {
            HttpWebResponse responseTemp = null;
            HttpWebRequest  request      = null;

            request        = (HttpWebRequest)HttpWebRequest.Create(targetResourceUrl);
            request.Method = this.GetHttpMethodForWOPIOperation(operationName);

            // Setting the required common headers
            if (null == headers)
            {
                headers = new WebHeaderCollection();
            }

            request.Headers = headers;

            if (null == body || 0 == body.Length)
            {
                request.ContentLength = 0;
            }
            else
            {
                request.ContentLength = body.Length;
                request.ContentType   = "application/binary";
                Stream stream = request.GetRequestStream();
                stream.Write(body, 0, body.Length);
            }

            // Get the response by default user credential
            request.Credentials = new NetworkCredential(this.defaultUserName, this.defaultPassword, this.defaultDomain);

            // Log the HTTP request
            this.LogHttpTransportInfo(request, body, null, operationName, false);

            try
            {
                responseTemp = request.GetResponse() as HttpWebResponse;
            }
            catch (WebException webEx)
            {
                this.Site.Log.Add(
                    LogEntryKind.Debug,
                    @"There is a WebException generated when sending WOPI request. Exception message[{0}],\r\nStackTrace:[{1}]",
                    webEx.Message,
                    webEx.StackTrace);

                this.RecordWebExceptionInformation(targetResourceUrl, operationName, webEx);
                throw;
            }

            WOPIHttpResponse wopiHttpResponse = new WOPIHttpResponse(responseTemp);

            // Log the HTTP response
            this.LogHttpTransportInfo(request, null, wopiHttpResponse, operationName);

            this.ValidateCommonMessageCapture();

            return(wopiHttpResponse);
        }
        /// <summary>
        /// A method used to get the HTTP method value according to the WOPI operation name.
        /// </summary>
        /// <param name="operationName">A parameter represents the WOPI operation name.</param>
        /// <returns>A return value represents the HTTP method value for the WOPI operation.</returns>
        private string GetHttpMethodForWOPIOperation(WOPIOperationName operationName)
        {
            string httpMethod = string.Empty;
            switch (operationName)
            {
                case WOPIOperationName.CheckFileInfo:
                case WOPIOperationName.CheckFolderInfo:
                case WOPIOperationName.GetFile:
                case WOPIOperationName.EnumerateChildren:
                    {
                        httpMethod = WebRequestMethods.Http.Get;
                        break;
                    }

                default:
                    {
                        httpMethod = WebRequestMethods.Http.Post;
                        break;
                    }
            }

            return httpMethod;
        }
        /// <summary>
        /// A method used to log the HTTP transport information.
        /// </summary>
        /// <param name="wopiRequest">A parameter represents the request instance of a WOPI operation. It must not be null.</param>
        /// <param name="requestBody">A parameter represents the request body of a WOPI operation.</param>
        /// <param name="wopiResponse">A parameter represents the response of a WOPI operation. It must not be null if the "isResponse" parameter is true.</param>
        /// <param name="operationName">A parameter represents the WOPI operation name.</param>
        /// <param name="isResponse">A parameter represents the HTTP transport information recorded by this method whether belong to the response of a WOPI operation.</param>
        private void LogHttpTransportInfo(HttpWebRequest wopiRequest, byte[] requestBody, WOPIHttpResponse wopiResponse, WOPIOperationName operationName, bool isResponse = true)
        {
            #region validate parameters
            if (isResponse)
            {
                if (null == wopiResponse)
                {
                    throw new ArgumentNullException("wopiResponse");
                }

                // For log response, requires the request URL.
                if (null == wopiRequest)
                {
                    throw new ArgumentNullException("wopiRequest");
                }
            }
            else
            {
                if (null == wopiRequest)
                {
                    throw new ArgumentNullException("wopiRequest");
                }
            }
            #endregion 

            #region headers

            // Build headers information
            StringBuilder headerInfoBuilder = new StringBuilder();
            WebHeaderCollection headers = isResponse ? wopiResponse.Headers : wopiRequest.Headers;
            if (null != headers && 0 != headers.Count)
            {
                foreach (string oheaderNameItem in headers.AllKeys)
                {
                    string headerValueString = string.Format(
                                                         "[{0}]:({1})",
                                                         oheaderNameItem,
                                                         headers[oheaderNameItem]);

                    headerInfoBuilder.AppendLine(headerValueString);
                }
            }

            #endregion 

            #region body information

            // Build body information
            byte[] httpBodyOfResponse = null;
            if (isResponse && 0 != wopiResponse.ContentLength)
            {
                httpBodyOfResponse = WOPIResponseHelper.GetContentFromResponse(wopiResponse);
            }

            byte[] httpBody = isResponse ? httpBodyOfResponse : requestBody;
            StringBuilder bodyInfoBuilder = new StringBuilder();
            if (null != httpBody && 0 != httpBody.Length)
            {
                switch (operationName)
                {
                    case WOPIOperationName.CheckFileInfo:
                    case WOPIOperationName.ReadSecureStore:
                    case WOPIOperationName.CheckFolderInfo:
                    case WOPIOperationName.EnumerateChildren:
                        {
                            if (isResponse)
                            {
                                // log response body by JSON format
                                bodyInfoBuilder.AppendLine(Encoding.UTF8.GetString(httpBody));
                            }

                            break;
                        }

                    case WOPIOperationName.PutRelativeFile:
                        {
                            if (isResponse)
                            {
                                // log the body by JSON format
                                bodyInfoBuilder.AppendLine(Encoding.UTF8.GetString(httpBody));
                            }
                            else
                            {
                                // log the body as bytes string value
                                bodyInfoBuilder.AppendLine(WOPIResponseHelper.GetBytesStringValue(httpBody));
                            }

                            break;
                        }

                    case WOPIOperationName.GetFile:
                        {
                            if (isResponse)
                            {
                                // log the body as bytes string value
                                bodyInfoBuilder.AppendLine(WOPIResponseHelper.GetBytesStringValue(httpBody));
                            }

                            break;
                        }

                    case WOPIOperationName.PutFile:
                        {
                            if (!isResponse)
                            {
                                // log the body as bytes string value
                                bodyInfoBuilder.AppendLine(WOPIResponseHelper.GetBytesStringValue(httpBody));
                            }

                            break;
                        }
                }
            }

            #endregion 

            string credentialInfo = string.Format(
                        "User:[{0}] Domain:[{1}]",
                        this.defaultUserName,
                        this.defaultDomain);

            string logTitle = string.Format(
                                    "{0} HTTP {1}{2} for [{3}] operation:",
                                    isResponse ? "Receive" : "Sending",
                                    isResponse ? "Response" : "Request",
                                    isResponse ? string.Empty : " with " + credentialInfo,
                                    operationName);

            StringBuilder logBuilder = new StringBuilder();
            logBuilder.AppendLine(logTitle);

            string urlInfor = string.Format("Request URL:[{0}]", wopiRequest.RequestUri.AbsoluteUri);
            logBuilder.AppendLine(urlInfor);

            string httpMethodValue = string.Format("HTTP method:[{0}]", this.GetHttpMethodForWOPIOperation(operationName));
            logBuilder.AppendLine(httpMethodValue);

            if (isResponse)
            {
                string httpStatusCodeValue = string.Format("HTTP status code:[{0}]", wopiResponse.StatusCode);
                logBuilder.AppendLine(httpStatusCodeValue);
            } 

            string headerInfo = string.Format("Headers:\r\n{0}", 0 == headerInfoBuilder.Length ? "None" : headerInfoBuilder.ToString());
            logBuilder.AppendLine(headerInfo);

            string bodyInfo = string.Format("Body:\r\n{0}", 0 == bodyInfoBuilder.Length ? "None" : bodyInfoBuilder.ToString());
            logBuilder.AppendLine(bodyInfo);

            this.Site.Log.Add(LogEntryKind.Debug, logBuilder.ToString());
        }
        /// <summary>
        /// A method is used to get the X-WOPI-Override header value according to the operation name.
        /// </summary>
        /// <param name="operationName">A parameter represents the operation name.</param>
        /// <returns>A return value represents the X-WOPI-Override header value which is used by specified operation.</returns>
        private string GetTheXWOPIOverrideHeaderValue(WOPIOperationName operationName)
        {
            string valueTemp = string.Empty;
            switch (operationName)
            {
                case WOPIOperationName.Lock:
                case WOPIOperationName.UnlockAndRelock:
                    {
                        valueTemp = "LOCK";
                        break;
                    }

                case WOPIOperationName.PutFile:
                    {
                        valueTemp = "PUT";
                        break;
                    }

                case WOPIOperationName.UnLock:
                    {
                        valueTemp = "UNLOCK";
                        break;
                    }

                case WOPIOperationName.PutRelativeFile:
                    {
                        valueTemp = "PUT_RELATIVE";
                        break;
                    }

                case WOPIOperationName.RefreshLock:
                    {
                        valueTemp = "REFRESH_LOCK";
                        break;
                    }

                case WOPIOperationName.ExecuteCellStorageRequest:
                case WOPIOperationName.ExecuteCellStorageRelativeRequest:
                    {
                        valueTemp = "COBALT";
                        break;
                    }

                case WOPIOperationName.DeleteFile:
                    {
                        valueTemp = "DELETE";
                        break;
                    }

                case WOPIOperationName.GetRestrictedLink:
                    {
                        valueTemp = "GET_RESTRICTED_LINK";
                        break;
                    }

                case WOPIOperationName.RevokeRestrictedLink:
                    {
                        valueTemp = "REVOKE_RESTRICTED_LINK";
                        break;
                    }

                case WOPIOperationName.ReadSecureStore:
                    {
                        valueTemp = "READ_SECURE_STORE";
                        break;
                    }

                default:
                    {
                        string errorMsg = string.Format(@"There is no valid [X-WOPI-Override] header value for operation[{0}]", operationName);
                        throw new InvalidOperationException(errorMsg);
                    }
            }

            return valueTemp;
        }
        /// <summary>
        /// A method is used to send http request for MS-WOPI operation.
        /// </summary>
        /// <param name="targetResourceUrl">A parameter represents the target resource Uri.</param>
        /// <param name="headers">A parameter represents the headers which is included in the http request.</param>
        /// <param name="body">A parameter represents the body contents which is sent in the http request.</param>
        /// <param name="operationName">A parameter represents the WOPI operation which the http request belongs to.</param>
        /// <returns>A return value represents the http response of the http request which is sent with specified header, URI, body and http method. </returns>
        protected virtual WOPIHttpResponse SendWOPIRequest(string targetResourceUrl, WebHeaderCollection headers, byte[] body, WOPIOperationName operationName)
        {
            HttpWebResponse responseTemp = null;
            HttpWebRequest request = null;

            request = (HttpWebRequest)HttpWebRequest.Create(targetResourceUrl);
            request.Method = this.GetHttpMethodForWOPIOperation(operationName);
            
            // Setting the required common headers
            if (null == headers)
            {
                headers = new WebHeaderCollection();
            }

            request.Headers = headers;

            if (null == body || 0 == body.Length)
            {
                request.ContentLength = 0;
            }
            else
            {
                request.ContentLength = body.Length;
                request.ContentType = "application/binary";
                Stream stream = request.GetRequestStream();
                stream.Write(body, 0, body.Length);
            }
 
            // Get the response by default user credential
            request.Credentials = new NetworkCredential(this.defaultUserName, this.defaultPassword, this.defaultDomain);
            
            // Log the HTTP request
            this.LogHttpTransportInfo(request, body, null, operationName, false);

            try
            {
                responseTemp = request.GetResponse() as HttpWebResponse;
            }
            catch (WebException webEx)
            {
                this.Site.Log.Add(
                                LogEntryKind.Debug,
                                @"There is a WebException generated when sending WOPI request. Exception message[{0}],\r\nStackTrace:[{1}]",
                                webEx.Message,
                                webEx.StackTrace);

                this.RecordWebExceptionInformation(targetResourceUrl, operationName, webEx);
                throw;
            }

            WOPIHttpResponse wopiHttpResponse = new WOPIHttpResponse(responseTemp);

            // Log the HTTP response
            this.LogHttpTransportInfo(request, null, wopiHttpResponse, operationName);

            this.ValidateCommonMessageCapture();
           
            return wopiHttpResponse;
        }
        /// <summary>
        /// A method is used to record information about web exception which is thrown by calling protocol operations.
        /// </summary>
        /// <param name="targetResourceUrl">A parameter represents the request URL which cause a web exception.</param>
        /// <param name="wopiOperationName">A parameter represents the protocol operation name which sends a http request and then get a web exception.</param>
        /// <param name="webException">A parameter represents web exception instance.</param>
        protected virtual void RecordWebExceptionInformation(string targetResourceUrl, WOPIOperationName wopiOperationName, WebException webException)
        {   
            string errorHeaderValue = this.GetWebExceptionHeadersValue(webException);

            if (!string.IsNullOrEmpty(errorHeaderValue))
            {
                  this.Site.Log.Add(
                                LogEntryKind.Debug,
                                "Perform the [{0}] operation fail. Request URL:[{1}]\r\nError headers:\r\n{2}",
                                wopiOperationName,
                                targetResourceUrl,
                                errorHeaderValue);
            }
        }