/// <summary>
		/// Processes the specified log level.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <returns></returns>
		public void Execute(RuleContext context)
		{
			string inputUrl = context.CurrentUrl.GetComponents(UriComponents.PathAndQuery, UriFormat.UriEscaped);
			string sqlCommand = Pattern.Replace(inputUrl, Substitution, context);
			string substituedUrl;

			using (MySqlConnection connection = new MySqlConnection(Properties.Settings.Default.DatabaseConnection))
			using (MySqlCommand command = connection.CreateCommand())
			{
				command.CommandText = sqlCommand;
				command.CommandType = CommandType.Text;

				try
				{
					connection.Open();
					substituedUrl = command.ExecuteScalar() as string;
				}
				finally
				{
					connection.Close();
				}
			}

			context.SubstitutedUrl = new Uri(context.CurrentUrl, substituedUrl);
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="ConditionContext"/> class.
		/// </summary>
		/// <param name="index">The index.</param>
		/// <param name="ruleContext">The rule context.</param>
		/// <param name="condition">The condition.</param>
		public ConditionContext(int index, RuleContext ruleContext, ICondition condition)
			: base(ruleContext)
		{
			if (condition == null)
				throw new ArgumentNullException("condition");

			ConditionIndex = index;
			LogCategory = "Condition " + index;
			CurrentCondition = condition;
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="RuleContext"/> class.
		/// </summary>
		/// <param name="copy">The copy.</param>
		internal RuleContext(RuleContext copy)
			: base(copy)
		{
			RuleIndex = copy.RuleIndex;
			LogCategory = copy.LogCategory;
			_currentContent = copy._currentContent;
			_substitutedContent = copy.SubstitutedContent;
			CurrentUrl = copy.CurrentUrl;
			_substitutedUrl = copy.SubstitutedUrl;
			CurrentRule = copy.CurrentRule;
		}
		public string GetValue(string input, RuleContext context)
		{
			int startIndex = 1;

			for (int i = 0; i < context.Conditions.Count; i++)
			{
				var cond = context.Conditions[i];
				int groupCount = cond.Pattern.GetGroupCount(input);
				ConditionContext condContext = new ConditionContext(i, context, cond);

				if ((startIndex + groupCount) >= Index)
				{
					int varIndex = Math.Abs(startIndex - _index);
					return cond.Pattern.GetValue(cond.Test.GetValue(condContext), varIndex, condContext);
				}

				startIndex += groupCount;
			}

			return null;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		/// <returns></returns>
		public virtual bool IsMatch(RuleContext context)
		{
			string testUrl = context.CurrentUrl.AbsolutePath;

			return Pattern.IsMatch(testUrl);
		}
		/// <summary>
		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </returns>
		public string ToString(RuleContext context)
		{
			return Replace(ToString(false), context);
		}
		/// <summary>
		/// Replaces the specified input for a rule variable.
		/// </summary>
		/// <param name="input">The input.</param>
		/// <param name="replacement">The replacement.</param>
		/// <param name="context">The context.</param>
		/// <returns></returns>
		public string Replace(string input, string replacement, RuleContext context)
		{
			if (context != null)
				replacement = Variables.Replace(replacement, match => {
					if (match.Groups["condvar"].Success)
					{
						var condVar = GetConditionVariable(match);
						return condVar.GetValue(input, context);
					}
					else if (match.Groups["servervar"].Success)
					{
						var serverVar = GetServerVariable(match);
						return serverVar.GetValue(input, context);
					}
					return match.Value;
				});


			return _pattern.Replace(input, replacement);
		}
		/// <summary>
		/// Gets the value.
		/// </summary>
		/// <param name="input">The input.</param>
		/// <param name="index">The index.</param>
		/// <param name="context">The context.</param>
		/// <returns>Returns the value of the pattern for the <paramref name="index"/></returns>
		public string GetValue(string input, int index, RuleContext context)
		{
			return GetValue(input, index);
		}
		/// <summary>
		/// Determines whether the specified input is match.
		/// </summary>
		/// <param name="input">The input.</param>
		/// <param name="context">The context.</param>
		/// <returns>
		/// 	<see langword="true"/> if the specified input is match; otherwise, <see langword="false"/>.
		/// </returns>
		public bool IsMatch(string input, RuleContext context)
		{
			// if the input contains a rule or a condition placeholders that need to be 
			// replaced from the input URL it needs to be processed by the rule pattern 
			// before we can check if it is a match
			if (input.IndexOfAny(new[] { '%', '$' }) >= 0)
				input = Replace(input, context);

			return IsMatch(input);
		}
		/// <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);
			byte[] currentContent = content;

			if (!EngineEnabled)
				Manager.LogIf(!EngineEnabled && LogLevel >= 9, "Rewrite Engine Is DISABLED", "OutRewrite");

			if (_outputRules.Count > 0 && EngineEnabled)
			{
				Manager.LogIf(LogLevel >= 1, "**********************************************************************************");
				//Manager.LogIf(LogLevel >= 9, "Input: " + currentContent, "OutRewrite");

				var temporyFlags = (IRuleFlagProcessor)null;
				bool skipNextChain = false;
				byte[] initialContent = currentContent;

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

					bool containsChain = RuleFlagsProcessor.HasChain(_outputRules[i].Flags);
					bool 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;
						bool 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;
					}
				}

				//Manager.LogIf(LogLevel >= 9, "Output: " + currentContent, "OutRewrite");
				Manager.LogIf(LogLevel >= 1, "**********************************************************************************");
			}

			return currentContent;
		}
		/// <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)
				Manager.LogIf(!EngineEnabled && LogLevel >= 9, "Rewrite Engine Is DISABLED", "Rewrite");

			if (_rules.Count > 0 && EngineEnabled)
			{
				Manager.LogIf(LogLevel >= 1, "**********************************************************************************");
				Manager.LogIf(LogLevel >= 1, "Input: " + currentUrl, "Rewrite");

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

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

				if (!String.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
					currentUrl = RemoveBase(VirtualBase, currentUrl);

				// process rules according to their settings
				for (int 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;

					bool containsChain = RuleFlagsProcessor.HasChain(_rules[i].Flags);
					bool 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;
						bool 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 the scheme, host, and ports do not match on the request vs the rewrite a redirect needs to be performed instead of a rewrite
				if (Uri.Compare(currentUrl, context.RequestedUrl, UriComponents.SchemeAndServer, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) != 0)
				{
					Manager.LogIf(LogLevel >= 1, "Output: 302 Redirect to " + currentUrl, "Rewrite");
					Manager.Redirect(httpContext, "found", currentUrl);
				}

				if (!String.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
					currentUrl = AddBase(VirtualBase, currentUrl);

				Manager.LogIf(LogLevel >= 1, "Output: " + currentUrl, "Rewrite");
				Manager.LogIf(LogLevel >= 1, "**********************************************************************************");

				Manager.TryToAddXRewriteUrlHeader(httpContext);
				Manager.TryToAddVanityHeader(httpContext);
			}

			// 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;
		}