/// <summary>
        /// Initializes a new instance of the <see cref="RuleContext"/> class.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="ruleSetContext">The rule set context.</param>
        /// <param name="currentContent"></param>
        /// <param name="rule">The rule.</param>
        public RuleContext(int index, RuleSetContext ruleSetContext, byte[] currentContent, IRule rule)
            : base(ruleSetContext)
        {
            if (currentContent == null)
            {
                throw new ArgumentNullException("currentContent");
            }

            if (rule == null)
            {
                throw new ArgumentNullException("rule");
            }

            RuleIndex = index;
            LogCategory = "Rule " + index;
            _currentContent = currentContent;
            _substitutedContent = currentContent;
            CurrentUrl = ruleSetContext.RequestedUrl;
            _substitutedUrl = ruleSetContext.RequestedUrl;
            CurrentRule = rule;
        }
        /// <summary>
        /// Gets the value.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public string GetValue(string input, RuleSetContext context)
        {
            // if the value has already been cached then return that value instead of reprocessing
            if (Value != null)
            {
                return Value;
            }

            var value = String.Empty;

            if ((_type & ServerVariableType.ServerVariables) != 0)
            {
                value = context.HttpContext.Request.ServerVariables[_name];

                // check to see if the value was found if not try the switch statement
                if (string.IsNullOrEmpty(value))
                {
                    switch (_name)
                    {
                        case "TIME_YEAR":
                            value = DateTime.Today.Year.ToString();
                            break;
                        case "TIME_MON":
                            value = DateTime.Today.Month.ToString();
                            break;
                        case "TIME_DAY":
                            value = DateTime.Today.Day.ToString();
                            break;
                        case "TIME_HOUR":
                            value = DateTime.Now.Hour.ToString();
                            break;
                        case "TIME_MIN":
                            value = DateTime.Now.Minute.ToString();
                            break;
                        case "TIME_SEC":
                            value = DateTime.Now.Second.ToString();
                            break;
                        case "TIME_WDAY":
                            value = DateTime.Today.DayOfWeek.ToString();
                            break;
                        case "TIME":
                            value = DateTime.Now.ToString("f");
                            break;
                        case "API_VERSION":
                            value = String.Empty;
                            break;// TODO: figure out how to attack this
                        case "THE_REQUEST":
                            value = context.HttpContext.Request.ServerVariables["REQUEST_METHOD"] + " " + context.HttpContext.Request.ServerVariables["PATH_INFO"] + " " + context.HttpContext.Request.ServerVariables["SERVER_PROTOCOL"];
                            break;
                        case "REQUEST_URI":
                            value = context.HttpContext.Request.ServerVariables["URL"];
                            break;
                        case "REQUEST_FILENAME":
                            value = context.HttpContext.Request.ServerVariables["PATH_TRANSLATED"];
                            break;
                        case "IS_SUBREQ":
                            value = String.IsNullOrEmpty(context.HttpContext.Request.Headers["X-Rewriter-Transfer"]) ? Boolean.FalseString : Boolean.TrueString;
                            break;
                    }
                }
            }
            else if ((_type & ServerVariableType.Headers) != 0)
            {
                value = context.HttpContext.Request.Headers[_name];
            }
            else if ((_type & ServerVariableType.QueryString) != 0)
            {
                value = context.HttpContext.Request.QueryString[_name];
            }
            else if ((_type & ServerVariableType.Form) != 0)
            {
                value = context.HttpContext.Request.Form[_name];
            }
            else if ((_type & ServerVariableType.Cookies) != 0)
            {
                var cookie = context.HttpContext.Request.Cookies[_name];

                // if cookie was found set the value to the value of the cookie
                if (cookie != null)
                {
                    value = cookie.Value;
                }
            }

            Logger.InfoFormat("Input: " + value);

            Value = value;
            return value;
        }
        /// <summary>
        /// Runs the rules.
        /// </summary>
        /// <param name="httpContext">The HTTP context.</param>
        /// <param name="url">The URL.</param>
        /// <returns>
        /// Returns a rewritten <see cref="System.Uri"/>, or a value of <see langword="null"/> if no rewriting was done to <paramref name="url"/>.
        /// </returns>
        public Uri RunRules(HttpContextBase httpContext, Uri url)
        {
            var context = new RuleSetContext(this, url, httpContext);
            var currentUrl = url;

            if (!EngineEnabled)
            {
                Logger.Info("Rewrite Engine Is DISABLED");
            }

            if (_rules.Count > 0 && EngineEnabled)
            {
                Logger.InfoFormat("Input: " + currentUrl);

                // check if max number of internal transfers have been exceeded
                if (InternalTransferCount(httpContext) > MaxInternalTransfers)
                {
                    string message = "Exceeded the max number of internal transfers.";
                    throw new HttpException(500, message);
                }

                IRuleFlagProcessor temporyFlags = null;
                var skipNextChain = false;
                var initialUrl = currentUrl;

                if (!string.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
                {
                    currentUrl = RemoveBase(VirtualBase, currentUrl);
                }

                // process rules according to their settings
                for (var i = 0; i < _rules.Count; i++)
                {
                    var ruleContext = new RuleContext(i, context, currentUrl, _rules[i]);
                    temporyFlags = _rules[i].Flags;

                    // continue if this rule shouldn't be processed because it doesn't allow internal transfer requests
                    if (RuleFlagsProcessor.HasNotForInternalSubRequests(temporyFlags) && IsInternalTransfer(httpContext))
                    {
                        continue;
                    }

                    var containsChain = RuleFlagsProcessor.HasChain(_rules[i].Flags);
                    var previousContainsChain = RuleFlagsProcessor.HasChain(_rules[Math.Max(0, i - 1)].Flags);

                    // if the previous rule doesn't contain a chain flag then set the initial URL
                    // this will be used to reset a chain if one of the chain rules fail
                    if (!previousContainsChain)
                    {
                        initialUrl = currentUrl;
                    }

                    // skip if the current rule or the last rule has a chain flag
                    // and if the skip next chain is set
                    if (skipNextChain && (previousContainsChain || containsChain))
                    {
                        continue;
                    }
                    else
                    {
                        skipNextChain = false;
                    }

                    if (_rules[i].TryExecute(ruleContext))
                    {
                        var flagResponse = temporyFlags.Apply(ruleContext);
                        currentUrl = ruleContext.SubstitutedUrl;
                        i = ruleContext.RuleIndex ?? -1;
                        var breakLoop = false;

                        // apply the flags to the rules, and only do special processing
                        // for the flag responses listed in the switch statement below
                        switch (flagResponse)
                        {
                            case RuleFlagProcessorResponse.ExitRuleSet:
                                return null;
                            case RuleFlagProcessorResponse.LastRule:
                                breakLoop = true;
                                break;
                        }

                        // break the loop because we have reached the last rule as indicated by a flag
                        if (breakLoop)
                        {
                            break;
                        }
                    }
                    else if (containsChain)
                    {
                        skipNextChain = true;

                        // reset the current URL back to the initial URL from the start of the chain
                        currentUrl = initialUrl;
                    }
                    else if (previousContainsChain)
                    {
                        // reset the current URL back to the initial URL from the start of the chain
                        currentUrl = initialUrl;
                    }
                }

                if (!string.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
                {
                    currentUrl = AddBase(VirtualBase, currentUrl);
                }

                Logger.InfoFormat("Output: {0}", currentUrl);
            }

            // if the http request url matches for both the request and the rewrite no work was done so the url should be null
            if (Uri.Compare(currentUrl, url, UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0)
            {
                currentUrl = null;
            }

            return currentUrl;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="RuleSetContext"/> class.
        /// </summary>
        /// <param name="copy">The copy.</param>
        internal RuleSetContext(RuleSetContext copy)
        {
            if (copy == null)
            {
                throw new ArgumentNullException("copy");
            }

            RuleSet = copy.RuleSet;
            RequestedUrl = copy.RequestedUrl;
            _responseContent = copy._responseContent;
            HttpContext = copy.HttpContext;
            IsOutputRuleSet = copy.IsOutputRuleSet;
            LogCategory = String.Empty;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public byte[] RunOutputRules(HttpContextBase httpContext, byte[] content)
        {
            var context = new RuleSetContext(this, content, httpContext);
            var currentContent = content;

            if (!EngineEnabled)
            {
                Logger.Info("Rewrite Engine Is DISABLED");
            }

            if (_outputRules.Count > 0 && EngineEnabled)
            {
                IRuleFlagProcessor temporyFlags = null;
                var skipNextChain = false;
                var initialContent = currentContent;

                // process rules according to their settings
                for (var i = 0; i < _outputRules.Count; i++)
                {
                    var ruleContext = new RuleContext(i, context, currentContent, _outputRules[i]);
                    temporyFlags = _outputRules[i].Flags;

                    var containsChain = RuleFlagsProcessor.HasChain(_outputRules[i].Flags);
                    var previousContainsChain = RuleFlagsProcessor.HasChain(_outputRules[Math.Max(0, i - 1)].Flags);

                    // if the previous rule doesn't contain a chain flag then set the initial URL
                    // this will be used to reset a chain if one of the chain rules fail
                    if (!previousContainsChain)
                    {
                        initialContent = currentContent;
                    }

                    // skip if the current rule or the last rule has a chain flag
                    // and if the skip next chain is set
                    if (skipNextChain && (previousContainsChain || containsChain))
                    {
                        continue;
                    }
                    else
                    {
                        skipNextChain = false;
                    }

                    if (_outputRules[i].TryExecute(ruleContext))
                    {
                        var flagResponse = temporyFlags.Apply(ruleContext);
                        currentContent = ruleContext.SubstitutedContent;
                        i = ruleContext.RuleIndex ?? -1;
                        var breakLoop = false;

                        // apply the flags to the rules, and only do special processing
                        // for the flag responses listed in the switch statement below
                        switch (flagResponse)
                        {
                            case RuleFlagProcessorResponse.ExitRuleSet:
                                return null;
                            case RuleFlagProcessorResponse.LastRule:
                                breakLoop = true;
                                break;
                        }

                        // break the loop because we have reached the last rule as indicated by a flag
                        if (breakLoop)
                        {
                            break;
                        }
                    }
                    else if (containsChain)
                    {
                        skipNextChain = true;

                        // reset the current URL back to the initial URL from the start of the chain
                        currentContent = initialContent;
                    }
                    else if (previousContainsChain)
                    {
                        // reset the current URL back to the initial URL from the start of the chain
                        currentContent = initialContent;
                    }
                }

            }

            return currentContent;
        }