/// <summary>
	    /// Display the main user groups.
	    /// </summary>
	    /// <param name="formData"></param>
	    /// <param name="userName">A form of user data passed up from the client.</param>
	    /// <param name="userGroup">The name of the current user group to display users for.</param>
	    /// <returns>A view to show a list of users in the current user group.</returns>
	    public ActionResult Index( FormCollection formData, string userName, string userGroup = "General" )
        {
            var group = _unitOfWork.UserGroupRepository.First(data => data.Name == userGroup);

            if (!string.IsNullOrWhiteSpace(userName))
            {
                var user = _unitOfWork.UserRepository.GetByUserName(userName);
                if (user != null && group != null)
                {
                    user.UserGroup = group;
                    _unitOfWork.UserRepository.Update(user);
                    _unitOfWork.Save();
                }
            }

	        var formhelper = new FormHelper(Request, formData, "");
	        var skip = (formhelper.Page -1) * formhelper.PageSize;
	        var take = formhelper.PageSize;

			var model = new UsersViewModel
			{
			    UserGroup = userGroup,
			    User = userName,
			    Users = _unitOfWork.UserRepository.ListAll().Where(data => data.UserGroupId == group.Id).OrderBy(data => data.UserName)
                    .Skip(skip)
                    .Take(take)
			        .Select(data => new UserViewModel() {Name = data.UserName, UserGroup = data.UserGroup.Name})
			        .ToList()
			};

		    var groupCounts =
		        _unitOfWork.UserRepository.ListAll()
		            .GroupBy(data => data.UserGroup)
		            .Select(data => new { Key = data.Key, Count = data.Count() }).ToDictionary( groupCount => groupCount.Key.Name, groupCount => groupCount.Count );
            
		    model.GroupSelectList = groupCounts.Select(listItem => new SelectListItem{Selected = listItem.Key == userGroup, Text = listItem.Key, Value = listItem.Key}).ToList();
            model.GroupCounts = groupCounts;
	        model.PagingInfo = new PagingInfo
	        {
	            CurrentPage = formhelper.Page,
	            PageSize = formhelper.PageSize,
	            TotalResults = _unitOfWork.UserRepository.ListAll().Count(data => data.UserGroupId == group.Id)
	        };

			return View( "Index", model );
		}
		/// <summary>
		/// Display a summary list of crashes based on the search criteria.
		/// </summary>
		/// <param name="crashesForm">A form of user data passed up from the client.</param>
		/// <returns>A view to display a list of crash reports.</returns>
		public ActionResult Index( FormCollection crashesForm )
		{
            using( var logTimer = new FAutoScopedLogTimer( this.GetType().ToString(), bCreateNewLog: true ) )
			{
				// Handle any edits made in the Set form fields
				foreach( var Entry in crashesForm )
				{
					int Id = 0;
					if( int.TryParse( Entry.ToString(), out Id ) )
					{
                        Crash currentCrash = _unitOfWork.CrashRepository.GetById(Id);
						if( currentCrash != null )
						{
							if( !string.IsNullOrEmpty( crashesForm["SetStatus"] ) )
							{
								currentCrash.Status = crashesForm["SetStatus"];
							}

							if( !string.IsNullOrEmpty( crashesForm["SetFixedIn"] ) )
							{
								currentCrash.FixedChangeList = crashesForm["SetFixedIn"];
							}

							if( !string.IsNullOrEmpty( crashesForm["SetTTP"] ) )
							{
								currentCrash.Jira = crashesForm["SetTTP"];
							}
						}
					}

				    _unitOfWork.Save();
				}

				// <STATUS>

				// Parse the contents of the query string, and populate the form
				var formData = new FormHelper( Request, crashesForm, "TimeOfCrash" );
				var result = GetResults( formData );
                result.BranchNames = _unitOfWork.CrashRepository.GetBranchesAsListItems();
                result.VersionNames = _unitOfWork.CrashRepository.GetVersionsAsListItems();
                result.PlatformNames = _unitOfWork.CrashRepository.GetPlatformsAsListItems();
                result.EngineModes = _unitOfWork.CrashRepository.GetEngineModesAsListItems();
                result.EngineVersions = _unitOfWork.CrashRepository.GetEngineVersionsAsListItems();

				// Add the FromCollection to the CrashesViewModel since we don't need it for the get results function but we do want to post it back to the page.
				result.FormCollection = crashesForm;
				result.GenerationTime = logTimer.GetElapsedSeconds().ToString( "F2" );
				return View( "Index", result );
			}
		}
        /// <summary>Constructs query for filtering.</summary>
        private IQueryable<Crash> ConstructQueryForFiltering(FormHelper formData)
        {
            //Instead of returning a queryable and filtering it here we should construct a filtering expression and pass that to the repository.
            //I don't like that data handling is taking place in the controller.
            var results = _unitOfWork.CrashRepository.ListAll();

            // Grab Results 
            var queryString = HttpUtility.HtmlDecode(formData.SearchQuery);
            if (!string.IsNullOrEmpty(queryString))
            {
                if (!string.IsNullOrEmpty(queryString))
                {
                    //We only use SearchQuery now for CallStack searching - if there's a SearchQuery value and a Username value, we need to get rid of the 
                    //Username so that we can create a broader search range
                    formData.UsernameQuery = "";
                }
                results = results.Where(data => data.RawCallStack.Contains(formData.SearchQuery));
            }

            // Filter by Crash Type
            if (formData.CrashType != "All")
            {
                switch (formData.CrashType)
                {
                    case "Crashes":
                        results = results.Where(crashInstance => crashInstance.CrashType == 1);
                        break;
                    case "Assert":
                        results = results.Where(crashInstance => crashInstance.CrashType == 2);
                        break;
                    case "Ensure":
                        results = results.Where(crashInstance => crashInstance.CrashType == 3);
                        break;
                    case "CrashesAsserts":
                        results = results.Where(crashInstance => crashInstance.CrashType == 1 || crashInstance.CrashType == 2);
                        break;
                }
            }

            // JRX Restore EpicID/UserName searching
            if (!string.IsNullOrEmpty(formData.UsernameQuery))
            {
                var decodedUsername = HttpUtility.HtmlDecode(formData.UsernameQuery).ToLower();
                var user = _unitOfWork.UserRepository.First(data => data.UserName.Contains(decodedUsername));
                if (user != null)
                {
                    var userId = user.Id; 
                    results = (
                        from crashDetail in results
                        where crashDetail.UserNameId == userId
                        select crashDetail
                        );
                }
            }

            // Start Filtering the results
            if (!string.IsNullOrEmpty(formData.EpicIdOrMachineQuery))
            {
                var decodedEpicOrMachineId = HttpUtility.HtmlDecode(formData.EpicIdOrMachineQuery).ToLower();
                results =
                        (
                            from crashDetail in results
                            where crashDetail.EpicAccountId.Equals(decodedEpicOrMachineId) || crashDetail.ComputerName.Equals(decodedEpicOrMachineId)
                            select crashDetail
                        );
            }

            if (!string.IsNullOrEmpty(formData.JiraQuery))
            {
                var decodedJira = HttpUtility.HtmlDecode(formData.JiraQuery).ToLower();
                results =
                    (
                        from crashDetail in results
                        where crashDetail.Jira.Contains(decodedJira)
                        select crashDetail
                    );
            }

            // Filter by BranchName
            if (!string.IsNullOrEmpty(formData.BranchName))
            {
                results =
                    (
                        from crashDetail in results
                        where crashDetail.Branch.Equals(formData.BranchName)
                        select crashDetail
                    );
            }

            // Filter by VersionName
            if (!string.IsNullOrEmpty(formData.VersionName))
            {
                results =
                    (
                        from crashDetail in results
                        where crashDetail.BuildVersion.Equals(formData.VersionName)
                        select crashDetail
                    );
            }

            //Filter by Engine Version
            if (!string.IsNullOrEmpty(formData.EngineVersion))
            {
                results =
                    (
                        from crashDetail in results
                        where crashDetail.EngineVersion.Equals(formData.EngineVersion)
                        select crashDetail
                    );
            }

            // Filter by VersionName
            if (!string.IsNullOrEmpty(formData.EngineMode))
            {
                results =
                    (
                        from crashDetail in results
                        where crashDetail.EngineMode.Equals(formData.EngineMode)
                        select crashDetail
                    );
            }

            // Filter by PlatformName
            if (!string.IsNullOrEmpty(formData.PlatformName))
            {
                results =
                    (
                        from crashDetail in results
                        where crashDetail.PlatformName.Contains(formData.PlatformName)
                        select crashDetail
                    );
            }

            // Filter by GameName
            if (!string.IsNullOrEmpty(formData.GameName))
            {
                var DecodedGameName = HttpUtility.HtmlDecode(formData.GameName).ToLower();

                if (DecodedGameName.StartsWith("-"))
                {
                    results =
                    (
                        from crashDetail in results
                        where !crashDetail.GameName.Contains(DecodedGameName.Substring(1))
                        select crashDetail
                    );
                }
                else
                {
                    results =
                    (
                        from crashDetail in results
                        where crashDetail.GameName.Contains(DecodedGameName)
                        select crashDetail
                    );
                }
            }

            // Filter by MessageQuery
            if (!string.IsNullOrEmpty(formData.MessageQuery))
            {
                results =
                (
                    from crashDetail in results
                    where crashDetail.Summary.Contains(formData.MessageQuery) || crashDetail.Description.Contains(formData.MessageQuery)
                    select crashDetail
                );
            }

            // Filter by BuiltFromCL
            if (!string.IsNullOrEmpty(formData.BuiltFromCL))
            {
                var builtFromCl = 0;
                var bValid = int.TryParse(formData.BuiltFromCL, out builtFromCl);

                if (bValid)
                {
                    results =
                        (
                            from crashDetail in results
                            where crashDetail.ChangeListVersion.Equals(formData.BuiltFromCL)
                            select crashDetail
                        );
                }
            }

            return results;
        }
        /// <summary>
        /// Gets a list of crashes filtered based on our form data.
        /// </summary>
        /// <param name="formData"></param>
        /// <returns></returns>
        public CrashesViewModel GetResults(FormHelper formData)
        {
            List<Crash> results = null;
            IQueryable<Crash> resultsQuery = null;

            var skip = (formData.Page - 1) * formData.PageSize;
            var take = formData.PageSize;

            resultsQuery = ConstructQueryForFiltering(formData);

            // Filter by data and get as enumerable.
            resultsQuery = FilterByDate(resultsQuery, formData.DateFrom, formData.DateTo);

            // Filter by BuggId
            if (!string.IsNullOrEmpty(formData.BuggId))
            {
                var buggId = 0;
                var bValid = int.TryParse(formData.BuggId, out buggId);

                if (bValid)
                {
                    var newBugg = _unitOfWork.BuggRepository.GetById(buggId);

                    if (newBugg != null)
                    {
                        resultsQuery = resultsQuery.Where(data => data.PatternId == newBugg.PatternId);
                    }
                }
            }

            var countsQuery = resultsQuery.GroupBy(data => data.User.UserGroup).Select(data => new { Key = data.Key.Name, Count = data.Count()});
            var groupCounts = countsQuery.OrderBy(data => data.Key).ToDictionary(data => data.Key, data => data.Count);

            // Filter by user group if present
            var userGroupId = !string.IsNullOrEmpty(formData.UserGroup) ? _unitOfWork.UserGroupRepository.First(data => data.Name == formData.UserGroup).Id : 1;
            resultsQuery = resultsQuery.Where(data => data.User.UserGroupId == userGroupId);

            var orderedQuery = GetSortedQuery(resultsQuery, formData.SortTerm ?? "TimeOfCrash", formData.SortOrder == "Descending");

            // Grab just the results we want to display on this page
            results = orderedQuery.Skip(skip).Take(take).ToList();

            // Get the Count for pagination
            var resultCount = 0;
            resultCount = orderedQuery.Count();

            // Process call stack for display
            foreach (var crashInstance in results)
            {
                // Put call stacks into an list so we can access them line by line in the view
                crashInstance.CallStackContainer = new CallStackContainer(crashInstance);
            }

            return new CrashesViewModel
            {
                Results = results,
                PagingInfo = new PagingInfo { CurrentPage = formData.Page, PageSize = formData.PageSize, TotalResults = resultCount },
                SortOrder = formData.SortOrder,
                SortTerm = formData.SortTerm,
                UserGroup = formData.UserGroup,
                CrashType = formData.CrashType,
                SearchQuery = formData.SearchQuery,
                UsernameQuery = formData.UsernameQuery,
                EpicIdOrMachineQuery = formData.EpicIdOrMachineQuery,
                MessageQuery = formData.MessageQuery,
                BuiltFromCL = formData.BuiltFromCL,
                BuggId = formData.BuggId,
                JiraQuery = formData.JiraQuery,
                DateFrom = (long)(formData.DateFrom - CrashesViewModel.Epoch).TotalMilliseconds,
                DateTo = (long)(formData.DateTo - CrashesViewModel.Epoch).TotalMilliseconds,
                BranchName = formData.BranchName,
                VersionName = formData.VersionName,
                PlatformName = formData.PlatformName,
                GameName = formData.GameName,
                GroupCounts = groupCounts
            };
        }