package gui

// matchesGui.go implements pages to view a user's mate matches

import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/canvas"
import "fyne.io/fyne/v2/container"
import "fyne.io/fyne/v2/data/binding"
import "fyne.io/fyne/v2/dialog"
import "fyne.io/fyne/v2/layout"
import "fyne.io/fyne/v2/theme"
import "fyne.io/fyne/v2/widget"

import "seekia/internal/appMemory"
import "seekia/internal/badgerDatabase"
import "seekia/internal/desires/mateDesires"
import "seekia/internal/desires/myLocalDesires"
import "seekia/internal/desires/myMateDesires"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/imagery"
import "seekia/internal/myBlockedUsers"
import "seekia/internal/myIdentity"
import "seekia/internal/myIgnoredUsers"
import "seekia/internal/myLikedUsers"
import "seekia/internal/myMatches"
import "seekia/internal/myMatchScore"
import "seekia/internal/mySettings"
import "seekia/internal/network/appNetworkType/getAppNetworkType"
import "seekia/internal/profiles/attributeDisplay"
import "seekia/internal/profiles/viewableProfiles"

import "strings"
import "time"
import "errors"
import "slices"
import "sync"


func setMatchesPage(window fyne.Window){

	appMemory.SetMemoryEntry("CurrentViewedPage", "Matches")

	checkIfPageHasChangedFunction := func()bool{
		exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
		if (exists == false || currentViewedPage != "Matches"){
			return true
		}
		return false
	}

	currentPage := func(){setMatchesPage(window)}

	title := getPageTitleCentered("My Matches")

	desiresIcon, err := getFyneImageIcon("Desires")
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}

	statsIcon, err := getFyneImageIcon("Stats")
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}

	matchScoreIcon, err := getFyneImageIcon("MatchScore")
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}

	customizeIcon, err := getFyneImageIcon("Info")
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}

	desiresButton := widget.NewButton(translate("Desires"), func(){
			setViewAllMyDesiresPage(window, currentPage)
		})
	desiresButtonWithIcon := container.NewGridWithColumns(1, desiresIcon, desiresButton)

	customizeButton := widget.NewButton("Customize", func(){
			setCustomizeMatchDisplayPage(window, currentPage)
		})
	customizeButtonWithIcon := container.NewGridWithColumns(1, customizeIcon, customizeButton)

	matchScoreButton := widget.NewButton(translate("Score"), func(){
			setConfigureMatchScorePage(window, currentPage)
		})
	matchScoreButtonWithIcon := container.NewGridWithColumns(1, matchScoreIcon, matchScoreButton)

	statsButton := widget.NewButton(translate("Stats"), func(){
			setMyMatchStatisticsPage(window, currentPage)
		})
	statsButtonWithIcon := container.NewGridWithColumns(1, statsIcon, statsButton)

	buttonsRow := getContainerCentered(container.NewGridWithRows(1, matchScoreButtonWithIcon, desiresButtonWithIcon, statsButtonWithIcon, customizeButtonWithIcon))

	currentSortByAttribute, err := myMatches.GetMatchesSortByAttribute()
	if (err != nil) {
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return	
	}

	sortingByLabel := getBoldLabel(translate("Sorting By:"))

	sortByAttributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(currentSortByAttribute)
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}

	sortByButton := widget.NewButton(translate(sortByAttributeTitle), func(){
			setSelectMatchesSortByAttributePage(window, currentPage)
		})

	getSortDirectionButtonWithIcon := func()(fyne.Widget, error){

		currentSortDirection, err := myMatches.GetMatchesSortDirection()
		if (err != nil) { return nil, err }

		if (currentSortDirection == "Ascending"){
			ascendingButton := widget.NewButtonWithIcon(translate("Ascending"), theme.MoveUpIcon(), func(){
					appMemory.SetMemoryEntry("StopBuildMatchesYesNo", "Yes")
					_ = mySettings.SetSetting("MatchesSortDirection", "Descending")
					_ = mySettings.SetSetting("MatchesSortedStatus", "No")
					_ = mySettings.SetSetting("MatchesViewIndex", "0")
					currentPage()
				})

			return ascendingButton, nil
		}

		descendingButton := widget.NewButtonWithIcon(translate("Descending"), theme.MoveDownIcon(), func(){
				appMemory.SetMemoryEntry("StopBuildMatchesYesNo", "Yes")
				_ = mySettings.SetSetting("MatchesSortDirection", "Ascending")
				_ = mySettings.SetSetting("MatchesSortedStatus", "No")
				_ = mySettings.SetSetting("MatchesViewIndex", "0")

				currentPage()
			})

		return descendingButton, nil
	}

	sortDirectionButton, err := getSortDirectionButtonWithIcon()
	if (err != nil) {
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return	
	}

	sortByRow := container.NewHBox(layout.NewSpacer(), sortingByLabel, sortByButton, sortDirectionButton, layout.NewSpacer())

	appNetworkType, err := getAppNetworkType.GetAppNetworkType()
	if (err != nil) {
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}

	matchesReady, err := myMatches.GetMatchesReadyStatus(appNetworkType)
	if (err != nil) {
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return	
	}

	if (matchesReady == false){

		progressPercentageBinding := binding.NewFloat()
		sortingProgressBinding := binding.NewString()

		startUpdateMatchesAndProgressBarFunction := func(){

			err := myMatches.StartUpdatingMyMatches(appNetworkType)
			if (err != nil) {
				fyne.Do(func(){setErrorEncounteredPage(window, err, currentPage)})
				return
			}

			sortingProgressBindingSet := false

			var encounteredError error

			for {

				pageHasChanged := checkIfPageHasChangedFunction()
				if (pageHasChanged == true){
					appMemory.SetMemoryEntry("StopBuildMatchesYesNo", "Yes")
					return
				}

				buildEncounteredError, errorEncounteredString, buildIsStopped, matchesAreReady, currentPercentageProgress, err := myMatches.GetMyMatchesBuildStatus(appNetworkType)
				if (err != nil){
					encounteredError = err
					break
				}
				if (buildEncounteredError == true){
					encounteredError = errors.New(errorEncounteredString)
					break
				}

				if (buildIsStopped == true){
					return
				}

				if (matchesAreReady == true){
					progressPercentageBinding.Set(1)

					// We wait so that the loading bar will appear complete.
					time.Sleep(100 * time.Millisecond)

					fyne.Do(currentPage)
					return
				}

				progressPercentageBinding.Set(currentPercentageProgress)

				if (currentPercentageProgress >= .50 && sortingProgressBindingSet == false){

					numberOfMatches, err := myMatches.GetNumberOfGeneratedMatches()
					if (err != nil) {
						encounteredError = err
						break
					}
					if (numberOfMatches != 0){
						numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches)
						sortingProgressBinding.Set("Sorting " + numberOfMatchesString + " Matches...")
					}
					sortingProgressBindingSet = true
				}

				time.Sleep(100 * time.Millisecond)
			}

			// This will only be reached if an error occurred
			errorToShow := errors.New("Error encountered while generating matches: " + encounteredError.Error())
			fyne.Do(func(){setErrorEncounteredPage(window, errorToShow, currentPage)})
		}

		loadingLabel := getBoldLabelCentered("Loading Matches...")

		loadingBar := getWidgetCentered(widget.NewProgressBarWithData(progressPercentageBinding))

		loadingDetailsLabel := widget.NewLabelWithData(sortingProgressBinding)
		loadingDetailsLabel.TextStyle = getFyneTextStyle_Italic()
		loadingDetailsLabelCentered := getWidgetCentered(loadingDetailsLabel)

		page := container.NewVBox(title, widget.NewSeparator(), buttonsRow, widget.NewSeparator(), sortByRow, widget.NewSeparator(), loadingLabel, loadingBar, loadingDetailsLabelCentered)

		setPageContent(page, window)	

		go startUpdateMatchesAndProgressBarFunction()

		return
	}

	getSplashContainer := func()(*fyne.Container, error){

		getRefreshResultsButtonText := func()(string, error){
			needsRefresh, err := myMatches.CheckIfMyMatchesNeedRefresh()
			if (err != nil) { return "", err }

			if (needsRefresh == false){
				return "Refresh Results", nil	
			}
			return "Refresh Results - Updates Available!", nil
		}

		refreshButtonText, err := getRefreshResultsButtonText()
		if (err != nil){ return nil, err }

		refreshResultsButton := getWidgetCentered(widget.NewButtonWithIcon(refreshButtonText, theme.ViewRefreshIcon(), func(){
				_ = mySettings.SetSetting("MatchesGeneratedStatus", "No")
				_ = mySettings.SetSetting("MatchesViewIndex", "0")
				currentPage()
			}))

		matchesReady, currentMatchesList, err := myMatches.GetMyMatchesList(appNetworkType)
		if (err != nil) { return nil, err }
		if (matchesReady == false){
			// This should not happen, as matches were found to be ready already.
			return nil, errors.New("Matches not ready after being ready already.")
		}

		if (len(currentMatchesList) == 0){

			noMatchesFoundLabel := getBoldLabelCentered("No Matches Found")

			description1 := getLabelCentered("Edit your desires to find more matches.")
			description2 := getLabelCentered("View your desire statistics to see which desires are reducing your matches.")

			viewDesireStatisticsButton := getWidgetCentered(widget.NewButtonWithIcon("View Desire Statistics", theme.VisibilityIcon(), func(){
					setMyMatchStatisticsPage(window, currentPage)
				}))

			noMatchesFoundContainer := container.NewVBox(noMatchesFoundLabel, refreshResultsButton, widget.NewSeparator(), description1, description2, viewDesireStatisticsButton)

			return noMatchesFoundContainer, nil
		}

		numberOfMatches := len(currentMatchesList)

		numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches)

		getNumberOfMatchesFoundText := func()string{
			if (numberOfMatches == 1){
				result := translate("1 Match Found")
				return result
			}

			result := numberOfMatchesString + " " + translate("Matches Found")

			return result
		}

		numberOfMatchesFoundText := getNumberOfMatchesFoundText()

		numberOfMatchesFoundLabel := getBoldLabelCentered(numberOfMatchesFoundText)

		getBeginRestartContinueButtons := func()(*fyne.Container, error){

			getMatchesViewIndex := func()(int, error){
				exists, currentIndex, err := mySettings.GetSetting("MatchesViewIndex")
				if (err != nil) { return 0, err }
				if (exists == false){
					return 0, nil
				}
				currentIndexInt, err := helpers.ConvertStringToInt(currentIndex)
				if (err != nil) { return 0, err }

				if (currentIndexInt > (numberOfMatches-1)){
					return 0, nil
				}

				return currentIndexInt, nil
			}

			currentViewIndex, err := getMatchesViewIndex()
			if (err != nil) { return nil, err }

			if (currentViewIndex == 0){
				beginButton := getWidgetCentered(widget.NewButtonWithIcon("Begin Browsing", theme.NavigateNextIcon(), func(){
						setBrowseMatchesPage(window, currentPage)
					}))
				return beginButton, nil
			}

			restartButton := getWidgetCentered(widget.NewButtonWithIcon("Restart From Beginning", theme.ContentUndoIcon(), func(){
					mySettings.SetSetting("MatchesViewIndex", "0")
					setBrowseMatchesPage(window, currentPage)
				}))

			currentViewIndexString := helpers.ConvertIntToString(currentViewIndex + 1)

			continueButtonText := "Continue from profile " + currentViewIndexString + " of " + numberOfMatchesString

			continueButton := getWidgetCentered(widget.NewButtonWithIcon(continueButtonText, theme.MailForwardIcon(), func(){
					setBrowseMatchesPage(window, currentPage)
				}))

			buttons := container.NewVBox(restartButton, continueButton)
			return buttons, nil
		}

		beginRestartContinueButtons, err := getBeginRestartContinueButtons()	
		if (err != nil) { return nil, err }

		splashPageContent := container.NewVBox(numberOfMatchesFoundLabel, widget.NewSeparator(), beginRestartContinueButtons, refreshResultsButton)

		return splashPageContent, nil
	}

	splashContainer, err := getSplashContainer()
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)}) 
		return
	}

	page := container.NewVBox(title, widget.NewSeparator(), buttonsRow, widget.NewSeparator(), sortByRow, widget.NewSeparator(), splashContainer)

	setPageContent(page, window)
}

// This page should only be called if matches are ready and at least 1 exists
func setBrowseMatchesPage(window fyne.Window, previousPage func()){

	currentPage := func(){setBrowseMatchesPage(window, previousPage)}

	title := getPageTitleCentered("Browse Matches")

	backButton := getBackButtonCentered(previousPage)

	getPageContent := func()(*fyne.Container, error){

		appNetworkType, err := getAppNetworkType.GetAppNetworkType()
		if (err != nil) { return nil, err }

		matchesReady, currentMatchesList, err := myMatches.GetMyMatchesList(appNetworkType)
		if (err != nil) { return nil, err }
		if (matchesReady == false) {
			// This should not happen, because this page should only be called if matches are already ready
			return nil, errors.New("setBrowseMatchesPage called when matches are not ready.")
		}

		if (len(currentMatchesList) == 0){
			return nil, errors.New("setBrowseMatchesPage called with no matches found.")		
		}

		numberOfMatches := len(currentMatchesList)

		getViewIndex := func()(int, error){

			exists, currentIndexString, err := mySettings.GetSetting("MatchesViewIndex")
			if (err != nil) { return 0, err }
			if (exists == false){
				return 0, nil
			}
			currentIndex, err := helpers.ConvertStringToInt(currentIndexString)
			if (err != nil){
				return 0, errors.New("MySettings malformed: Contains invalid MatchesViewIndex: " + currentIndexString)	
			}
			if (currentIndex < 0){

				return 0, nil
			}

			maximumIndex := numberOfMatches-1
			if (currentIndex > maximumIndex){

				return maximumIndex, nil
			}

			return currentIndex, nil
		}

		currentIndex, err := getViewIndex()
		if (err != nil) { return nil, err }

		pageContent := container.NewVBox()

		if (numberOfMatches > 1){

			getNavigationButtons := func() *fyne.Container{

				getGoToBeginningButton := func()fyne.Widget{
					if (currentIndex == 0){
						emptyButton := widget.NewButton("", nil)
						return emptyButton
					}

					goToBeginningButton := widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), func(){
							_ = mySettings.SetSetting("MatchesViewIndex", "0")
							currentPage()
						})
					return goToBeginningButton
				}

				getGoToEndButton := func()fyne.Widget{

					finalIndex := numberOfMatches - 1

					if (currentIndex >= finalIndex){
						emptyButton := widget.NewButton("", nil)
						return emptyButton
					}

					goToEndButton := widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), func(){
							finalIndexString := helpers.ConvertIntToString(finalIndex)
							_ = mySettings.SetSetting("MatchesViewIndex", finalIndexString)
							currentPage()
						})

					return goToEndButton
				}

				getLeftButton := func()fyne.Widget{

					if (currentIndex == 0){
						emptyButton := widget.NewButton("", nil)
						return emptyButton
					}
					leftButton := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){
							newIndex := helpers.ConvertIntToString(currentIndex-1)
							_ = mySettings.SetSetting("MatchesViewIndex", newIndex)
							currentPage()
						})
					return leftButton		
				}

				getRightButton := func()fyne.Widget{

					finalIndex := numberOfMatches - 1

					if (currentIndex >= finalIndex){

						emptyButton := widget.NewButton("", nil)
						return emptyButton
					}
					rightButton := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){
							newIndex := helpers.ConvertIntToString(currentIndex+1)
							_ = mySettings.SetSetting("MatchesViewIndex", newIndex)
							currentPage()
						})

					return rightButton		
				}

				goToBeginningButton := getGoToBeginningButton()
				goToEndButton := getGoToEndButton()

				leftButton := getLeftButton()
				rightButton := getRightButton()

				if (numberOfMatches == 1){
					buttons := getContainerCentered(container.NewGridWithRows(1, goToBeginningButton, leftButton, rightButton, goToEndButton))

					return buttons
				}

				matchRank := currentIndex + 1

				currentRankString := helpers.ConvertIntToString(matchRank)
				numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches)
				currentRankDescription := getBoldLabel(currentRankString + "/" + numberOfMatchesString)

				buttons := getContainerCentered(container.NewGridWithRows(1, goToBeginningButton, leftButton, currentRankDescription, rightButton, goToEndButton))

				return buttons
			}

			navButtons := getNavigationButtons()

			pageContent.Add(navButtons)
			pageContent.Add(widget.NewSeparator())
		}

		getMatchDisplay := func()(*fyne.Container, error){

			if (currentIndex >= len(currentMatchesList) || currentIndex < 0){
				return nil, errors.New("Match index out of range.")
			}

			matchIdentityHash := currentMatchesList[currentIndex]

			userIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(matchIdentityHash)	
			if (err != nil) { return nil, err }
			if (userIsBlocked == true){

				isBlockedDescription1 := getBoldLabelCentered("User is blocked.")
				isBlockedDescription2 := getLabelCentered("You have blocked this match.")
				isBlockedDescription3 := getLabelCentered("Refresh your matches to fix this.")

				refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){
						_ = mySettings.SetSetting("MatchesGeneratedStatus", "No")
						_ = mySettings.SetSetting("MatchesViewIndex", "0")
						setMatchesPage(window)
					}))

				userIsBlockedSection := container.NewVBox(isBlockedDescription1, isBlockedDescription2, isBlockedDescription3, refreshMatchesButton)
				return userIsBlockedSection, nil
			}

			getShowIgnoredMatchesBool := func()(bool, error){

				exists, isIgnoredIsEnabled, err := myLocalDesires.GetDesire("IsIgnored_FilterAll")
				if (err != nil) { return false, err }
				if (exists == false){
					// Desire is disabled.
					return true, nil
				}
				if (isIgnoredIsEnabled == "No"){
					// Desire is disabled.
					// We will show ignored matches
					return true, nil
				}
				exists, isIgnoredDesire, err := myLocalDesires.GetDesire("IsIgnored")
				if (err != nil) { return false, err }
				if (exists == false){
					// Desire does not exist.
					// We will show ignored matches
					return true, nil
				}

				// isIgnoredDesire is a list of base64 encoded values which the user wants
				// We see if "Yes" is contained within the list
				// If it is not, then the user desires only non-ignored users 
				myDesiredChoicesList := strings.Split(isIgnoredDesire, "+")

				yesBase64 := encoding.EncodeBytesToBase64String([]byte("Yes"))
				
				yesExists := slices.Contains(myDesiredChoicesList, yesBase64)
				if (yesExists == true){
					// We want to see ignored users in our matches
					return true, nil
				}

				return false, nil
			}

			showIgnoredMatchesBool, err := getShowIgnoredMatchesBool()
			if (err != nil) { return nil, err }

			userIsIgnored, _, _, _, err := myIgnoredUsers.CheckIfUserIsIgnored(matchIdentityHash)	
			if (err != nil) { return nil, err }
			if (userIsIgnored == true && showIgnoredMatchesBool == false){

				isIgnoredDescription1 := getBoldLabelCentered("User is ignored.")
				isIgnoredDescription2 := getLabelCentered("You have ignored this match.")
				isIgnoredDescription3 := getLabelCentered("Refresh your matches to fix this.")

				refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){
						_ = mySettings.SetSetting("MatchesGeneratedStatus", "No")
						_ = mySettings.SetSetting("MatchesViewIndex", "0")
						setMatchesPage(window)
					}))

				userIsIgnoredSection := container.NewVBox(isIgnoredDescription1, isIgnoredDescription2, isIgnoredDescription3, refreshMatchesButton)
				return userIsIgnoredSection, nil
			}

			profileExists, _, getAnyAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(matchIdentityHash, appNetworkType, true, false, true)
			if (err != nil) { return nil, err }
			if (profileExists == false) {

				matchNotFoundLabel := getBoldLabelCentered("Match Not Found")
		
				description1 := getLabelCentered("Their profile may have been deleted.")
				description2 := getLabelCentered("Refresh your matches to fix this.")

				refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){
						_ = mySettings.SetSetting("MatchesGeneratedStatus", "No")
						_ = mySettings.SetSetting("MatchesViewIndex", "0")
						setMatchesPage(window)
					}))

				matchNotFoundSection := container.NewVBox(matchNotFoundLabel, description1, description2, refreshMatchesButton)
				return matchNotFoundSection, nil
			}

			exists, _, userIsDisabled, err := getAnyAttributeFunction("Disabled")
			if (err != nil){ return nil, err }
			if (exists == true && userIsDisabled == "Yes"){

				matchIsDisabledLabel := getBoldLabelCentered("User Is Disabled")

				description1 := getLabelCentered("This user has disabled their profile.")
				description2 := getLabelCentered("Refresh your matches to fix this.")

				refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){
						_ = mySettings.SetSetting("MatchesGeneratedStatus", "No")
						_ = mySettings.SetSetting("MatchesViewIndex", "0")
						setMatchesPage(window)
					}))

				matchIsDisabledSection := container.NewVBox(matchIsDisabledLabel, description1, description2, refreshMatchesButton)
				return matchIsDisabledSection, nil
			}

			// We see if user's newest viewable profile still fulfills our desires

			profilePassesDesires, err := myMateDesires.CheckIfMateProfilePassesAllMyDesires(false, "", getAnyAttributeFunction)
			if (err != nil) { return nil, err }
			if (profilePassesDesires == false){

				userIsNotAMatchLabel := getBoldLabelCentered("User Is Not A Match Anymore")

				description1 := getLabelCentered("This user has changed their profile and they are no longer a match.")
				description2 := getLabelCentered("Refresh your matches to fix this.")

				refreshMatchesButton := getWidgetCentered(widget.NewButtonWithIcon("Refresh Matches", theme.ViewRefreshIcon(), func(){
						_ = mySettings.SetSetting("MatchesGeneratedStatus", "No")
						_ = mySettings.SetSetting("MatchesViewIndex", "0")
						setMatchesPage(window)
					}))

				userIsNotAMatchSection := container.NewVBox(userIsNotAMatchLabel, description1, description2, refreshMatchesButton)

				return userIsNotAMatchSection, nil
			}

			getAvatarOrImage := func()(*canvas.Image, error){

				photosExist, _, photosAttribute, err := getAnyAttributeFunction("Photos")
				if (err != nil) { return nil, err }

				if (photosExist == true){

					photosList := strings.Split(photosAttribute, "+")

					firstPhotoBase64String := photosList[0]

					goImageObject, err := imagery.ConvertJPEGBase64StringToCroppedDownsizedImageObject(firstPhotoBase64String)
					if (err != nil){
						return nil, errors.New("Database corrupt: Contains profile with invalid Photos attribute")
					}

					fyneImageObject := canvas.NewImageFromImage(goImageObject)
					fyneImageObject.FillMode = canvas.ImageFillContain

					return fyneImageObject, nil
				}

				getAvatarEmojiIdentifier := func()(int, error){

					exists, _, emojiIdentifier, err := getAnyAttributeFunction("Avatar")
					if (err != nil){ return 0, err }
					if (exists == false){
						// This is the avatar for users who have not selected an avatar
						return 2929, nil	
					}

					emojiIdentifierInt, err := helpers.ConvertStringToInt(emojiIdentifier)
					if (err != nil) {
						return 0, errors.New("Database corrupt: Contains user profile with invalid avatar: " + emojiIdentifier)
					}

					return emojiIdentifierInt, nil				
				}

				avatarEmojiIdentifier, err := getAvatarEmojiIdentifier()
				if (err != nil) { return nil, err }

				emojiImage, err := getEmojiImageObject(avatarEmojiIdentifier)
				if (err != nil){ return nil, err }

				emojiFyneImage := canvas.NewImageFromImage(emojiImage)
				emojiFyneImage.FillMode = canvas.ImageFillContain				

				return emojiFyneImage, nil
			}

			avatarOrImage, err := getAvatarOrImage()	
			if (err != nil) { return nil, err }
			avatarOrImage.SetMinSize(getCustomFyneSize(10))

			avatarOrImageHeightenerA := container.NewVBox(widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""))
			avatarOrImageHeightenerB := container.NewVBox(widget.NewLabel(""), widget.NewLabel(""), widget.NewLabel(""))

			avatarOrImageHeightened := getContainerCentered(container.NewGridWithRows(1, avatarOrImageHeightenerA, avatarOrImage, avatarOrImageHeightenerB))

			viewProfileButton := getWidgetCentered(widget.NewButtonWithIcon("View Profile", theme.VisibilityIcon(), func(){
					setViewPeerProfilePageFromIdentityHash(window, matchIdentityHash, currentPage)
				}))

			getAttributesSection := func()(*fyne.Container, error){

				getDisplayAttributesList := func()([]string, error){

					// First attribute is always the "Sort By" attribute				

					currentSortByAttribute, err := myMatches.GetMatchesSortByAttribute()
					if (err != nil) { return nil, err }

					exists, currentAttributesListString, err := mySettings.GetSetting("CustomMatchDisplayAttributesList")
					if (err != nil) { return nil, err }
					if (exists == false){
						newDisplayAttributesList := []string{currentSortByAttribute}
						return newDisplayAttributesList, nil
					}

					currentAttributesList := strings.Split(currentAttributesListString, ",")

					displayAttributesListPruned, _ := helpers.DeleteAllMatchingItemsFromList(currentAttributesList, currentSortByAttribute)

					newDisplayAttributesList := []string{currentSortByAttribute}

					newDisplayAttributesList = append(newDisplayAttributesList, displayAttributesListPruned...)

					return newDisplayAttributesList, nil
				}

				displayAttributesList, err := getDisplayAttributesList()
				if (err != nil){ return nil, err }

				attributesSection := container.NewVBox()

				for _, attributeName := range displayAttributesList{

					attributeTitle, _, formatValueFunction, attributeUnits, missingValueText, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
					if (err != nil) { return nil, err }

					attributeTitleLabel := getBoldLabel(attributeTitle + ":")

					getAttributeValueText := func()(string, error){

						attributeValueExists, _, attributeValue, err := getAnyAttributeFunction(attributeName)
						if (err != nil) { return "", err }
						if (attributeValueExists == false){
							return missingValueText, nil
						}

						formattedValue, err := formatValueFunction(attributeValue)
						if (err != nil) { return "", err }

						result := formattedValue + attributeUnits

						return result, nil
					}	

					attributeValueText, err := getAttributeValueText()
					if (err != nil) { return nil, err }

					attributeValueLabel := widget.NewLabel(attributeValueText)

					attributeRow := container.NewHBox(layout.NewSpacer(), attributeTitleLabel, attributeValueLabel, layout.NewSpacer())

					attributesSection.Add(attributeRow)
				}

				return attributesSection, nil
			}

			attributesSection, err := getAttributesSection()
			if (err != nil) { return nil, err }

			getLikeOrUnlikeButtonWithIcon := func()(*fyne.Container, error){

				userIsLiked, _, err := myLikedUsers.CheckIfUserIsLiked(matchIdentityHash)
				if (err != nil) { return nil, err }
				if (userIsLiked == true){
					unlikeIcon, err := getFyneImageIcon("Unlike")
					if (err != nil) { return nil, err }
					unlikeButton := widget.NewButton("Unlike", func(){
							setConfirmUnlikeUserPage(window, matchIdentityHash, currentPage, currentPage)
						})

					unlikeButtonWithIcon := container.NewGridWithColumns(1, unlikeIcon, unlikeButton)
					return unlikeButtonWithIcon, nil
				}

				likeIcon, err := getFyneImageIcon("Like")
				if (err != nil) { return nil, err }
				likeButton := widget.NewButton("Like", func(){
						setConfirmLikeUserPage(window, matchIdentityHash, currentPage, currentPage)
					})

				likeButtonWithIcon := container.NewGridWithColumns(1, likeIcon, likeButton)
				return likeButtonWithIcon, nil
			}

			likeOrUnlikeButtonWithIcon, err := getLikeOrUnlikeButtonWithIcon()
			if (err != nil) { return nil, err }

			getIgnoreOrUnignoreButtonWithIcon := func()(*fyne.Container, error){

				if (userIsIgnored == true){
					unignoreIcon := widget.NewIcon(theme.VisibilityIcon())

					unignoreButton := widget.NewButton("Unignore", func(){
							setConfirmUnignoreUserPage(window, matchIdentityHash, currentPage, currentPage)
						})

					unignoreButtonWithIcon := container.NewGridWithColumns(1, unignoreIcon, unignoreButton)
					return unignoreButtonWithIcon, nil
				}

				ignoreIcon := widget.NewIcon(theme.VisibilityOffIcon())

				ignoreButton := widget.NewButton("Ignore", func(){
						setConfirmIgnoreUserPage(window, matchIdentityHash, currentPage, currentPage)
					})

				ignoreButtonWithIcon := container.NewGridWithColumns(1, ignoreIcon, ignoreButton)
				return ignoreButtonWithIcon, nil		
			}

			ignoreOrUnignoreButtonWithIcon, err := getIgnoreOrUnignoreButtonWithIcon()
			if (err != nil) { return nil, err }

			greetIcon, err := getFyneImageIcon("Greet")
			if (err != nil) { return nil, err }
			greetButton := widget.NewButton("Greet", func(){
					setConfirmGreetOrRejectUserPage(window, "Greet", matchIdentityHash, currentPage, currentPage)
				})
			greetButtonWithIcon := container.NewGridWithColumns(1, greetIcon, greetButton)

			moreIcon, err := getFyneImageIcon("Plus")
			if (err != nil) { return nil, err }
			moreButton := widget.NewButton("More", func(){
					setViewPeerActionsPage(window, matchIdentityHash, currentPage)
				})
			moreButtonWithIcon := container.NewGridWithColumns(1, moreIcon, moreButton)

			actionsRow := getContainerCentered(container.NewGridWithRows(1, likeOrUnlikeButtonWithIcon, ignoreOrUnignoreButtonWithIcon, greetButtonWithIcon, moreButtonWithIcon))

			matchDisplay := container.NewVBox(avatarOrImageHeightened, viewProfileButton, widget.NewSeparator(), attributesSection, widget.NewSeparator(), actionsRow)

			return matchDisplay, nil
		}

		matchDisplay, err := getMatchDisplay()
		if (err != nil) { return nil, err }

		pageContent.Add(matchDisplay)

		return pageContent, nil
	}
	
	pageContent, err := getPageContent()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	
	page := container.NewVBox(title, backButton, widget.NewSeparator(), pageContent)

	setPageContent(page, window)
}

func setSelectMatchesSortByAttributePage(window fyne.Window, previousPage func()){

	appMemory.SetMemoryEntry("CurrentViewedPage", "SortBySelectPage_Matches")
	
	title := getPageTitleCentered("Select Sort By Attribute")

	backButton := getBackButtonCentered(previousPage)

	description := getLabelCentered("Choose the attribute to sort your matches by.")

	getPageContent := func()(*fyne.Container, error){

		generalAttributeButtonsGrid := container.NewGridWithColumns(1)
		physicalAttributeButtonsGrid := container.NewGridWithColumns(1)
		lifestyleAttributeButtonsGrid := container.NewGridWithColumns(1)
		mentalAttributeButtonsGrid := container.NewGridWithColumns(1)

		addAttributeSelectButton := func(attributeType string, attributeName string, sortDirection string)error{

			attributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
			if (err != nil) { return err }

			attributeButton := widget.NewButton(attributeTitle, func(){
				_ = mySettings.SetSetting("MatchesSortedStatus", "No")
				_ = mySettings.SetSetting("MatchesSortByAttribute", attributeName)
				_ = mySettings.SetSetting("MatchesSortDirection", sortDirection)
				_ = mySettings.SetSetting("MatchesViewIndex", "0")

				previousPage()
			})

			if (attributeType == "General"){

				generalAttributeButtonsGrid.Add(attributeButton)

			} else if (attributeType == "Physical"){

				physicalAttributeButtonsGrid.Add(attributeButton)

			} else if (attributeType == "Lifestyle"){

				lifestyleAttributeButtonsGrid.Add(attributeButton)

			} else if (attributeType == "Mental"){

				mentalAttributeButtonsGrid.Add(attributeButton)

			} else {
				return errors.New("addSelectButton called with invalid attributeType: " + attributeType)
			}

			return nil
		}

		generalLabel := getBoldLabelCentered("General")

		err := addAttributeSelectButton("General", "MatchScore", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("General", "Age", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("General", "Distance", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("General", "SearchTermsCount", "Descending")
		if (err != nil) { return nil, err }

		physicalLabel := getBoldLabelCentered("Physical")

		err = addAttributeSelectButton("Physical", "Height", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "BodyFat", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "BodyMuscle", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "SkinColor", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "HairTexture", "Ascending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "DeepAppearanceSimilarity", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "EyeColorSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "EyeColorGenesSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "HairColorSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "HairColorGenesSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "SkinColorSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "SkinColorGenesSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "HairTextureSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "HairTextureGenesSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "FacialStructureGenesSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "23andMe_AncestralSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "23andMe_MaternalHaplogroupSimilarity", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "23andMe_PaternalHaplogroupSimilarity", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "OffspringProbabilityOfAnyMonogenicDisease", "Ascending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "TotalPolygenicDiseaseRiskScore", "Ascending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "OffspringTotalPolygenicDiseaseRiskScore", "Ascending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "AutismRiskScore", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringAutismRiskScore", "Ascending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "ObesityRiskScore", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringObesityRiskScore", "Ascending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "OffspringBlueEyesProbability", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringGreenEyesProbability", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringHazelEyesProbability", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringBrownEyesProbability", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "OffspringLactoseToleranceProbability", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "OffspringStraightHairProbability", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringCurlyHairProbability", "Descending")
		if (err != nil) { return nil, err }

		// Numeric Traits:

		err = addAttributeSelectButton("Physical", "HomosexualnessScore", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringHomosexualnessScore", "Ascending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "PredictedHeight", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Physical", "OffspringPredictedHeight", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Physical", "23andMe_NeanderthalVariants", "Descending")
		if (err != nil) { return nil, err }

		lifestyleLabel := getBoldLabelCentered("Lifestyle")

		err = addAttributeSelectButton("Lifestyle", "WealthInGold", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Lifestyle", "Fame", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Lifestyle", "FruitRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "VegetablesRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "NutsRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "GrainsRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "DairyRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "SeafoodRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "BeefRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "PorkRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "PoultryRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "EggsRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "BeansRating", "Descending")
		if (err != nil) { return nil, err }

		err = addAttributeSelectButton("Lifestyle", "AlcoholFrequency", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "TobaccoFrequency", "Ascending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Lifestyle", "CannabisFrequency", "Ascending")
		if (err != nil) { return nil, err }

		mentalLabel := getBoldLabelCentered("Mental")

		err = addAttributeSelectButton("Mental", "PetsRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Mental", "CatsRating", "Descending")
		if (err != nil) { return nil, err }
		err = addAttributeSelectButton("Mental", "DogsRating", "Descending")
		if (err != nil) { return nil, err }

		generalAttributeButtonsGridCentered := getContainerCentered(generalAttributeButtonsGrid)
		physicalAttributeButtonsGridCentered := getContainerCentered(physicalAttributeButtonsGrid)
		lifestyleAttributeButtonsGridCentered := getContainerCentered(lifestyleAttributeButtonsGrid)
		mentalAttributeButtonsGridCentered := getContainerCentered(mentalAttributeButtonsGrid)

		pageContent := container.NewVBox(generalLabel, widget.NewSeparator(), generalAttributeButtonsGridCentered, widget.NewSeparator(), physicalLabel, widget.NewSeparator(), physicalAttributeButtonsGridCentered, widget.NewSeparator(), lifestyleLabel, widget.NewSeparator(), lifestyleAttributeButtonsGridCentered, widget.NewSeparator(), mentalLabel, widget.NewSeparator(), mentalAttributeButtonsGridCentered)

		return pageContent, nil
	}

	pageContent, err := getPageContent()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), pageContent)

	setPageContent(page, window)
}


func setMyMatchStatisticsPage(window fyne.Window, previousPage func()){

	pageIdentifier, err := helpers.GetNewRandomHexString(16)
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)

	checkIfPageHasChangedFunction := func()bool{
		exists, currentViewedPage := appMemory.GetMemoryEntry("CurrentViewedPage")
		if (exists == true && currentViewedPage == pageIdentifier){
			return false
		}
		return true
	}

	currentPage := func(){setMyMatchStatisticsPage(window, previousPage)}

	title := getPageTitleCentered("My Match Statistics")

	backButton := getBackButtonCentered(previousPage)

	description := getLabelCentered("Below are statistics about your matches.")

	accuracyWarningButton := getWidgetCentered(widget.NewButtonWithIcon("Statistics Warning", theme.WarningIcon(), func(){
			setMateDesireStatisticsWarningPage(window, currentPage)
		}))

	totalMateUsersBinding := binding.NewString()
	totalBlockedUsersBinding := binding.NewString()
	numberOfMatchesBinding := binding.NewString()
	matchPercentageBinding := binding.NewString()

	totalMateUsersDescription := widget.NewLabel("Total Mate Users:")
	totalMateUsersLabel := widget.NewLabelWithData(totalMateUsersBinding)
	totalMateUsersLabel.TextStyle = getFyneTextStyle_Bold()
	totalMateUsersRow := container.NewHBox(layout.NewSpacer(), totalMateUsersDescription, totalMateUsersLabel, layout.NewSpacer())

	totalBlockedUsersDescription := widget.NewLabel("Total Blocked Users:")
	totalBlockedUsersLabel := widget.NewLabelWithData(totalBlockedUsersBinding)
	totalBlockedUsersLabel.TextStyle = getFyneTextStyle_Bold()
	totalBlockedUsersRow := container.NewHBox(layout.NewSpacer(), totalBlockedUsersDescription, totalBlockedUsersLabel, layout.NewSpacer())

	numberOfMatchesDescription := widget.NewLabel("Number of Matches:")
	numberOfMatchesLabel := widget.NewLabelWithData(numberOfMatchesBinding)
	numberOfMatchesLabel.TextStyle = getFyneTextStyle_Bold()
	numberOfMatchesRow := container.NewHBox(layout.NewSpacer(), numberOfMatchesDescription, numberOfMatchesLabel, layout.NewSpacer())

	matchPercentageDescription := widget.NewLabel("Match Percentage:")
	matchPercentageLabel := widget.NewLabelWithData(matchPercentageBinding)
	matchPercentageLabel.TextStyle = getFyneTextStyle_Bold()
	matchPercentageRow := container.NewHBox(layout.NewSpacer(), matchPercentageDescription, matchPercentageLabel, layout.NewSpacer())

	appNetworkType, err := getAppNetworkType.GetAppNetworkType()
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	updateBindingsFunction := func(){

		var resultsReadyBoolMutex sync.RWMutex
		resultsReadyBool := false

		updateBindingsWithLoadingText := func(){

			secondsElapsed := 0

			for{

				resultsReadyBoolMutex.RLock()
				resultsReady := resultsReadyBool
				resultsReadyBoolMutex.RUnlock()

				if (resultsReady == true){
					return
				}
				pageHasChanged := checkIfPageHasChangedFunction()
				if (pageHasChanged == true){
					return
				}

				if (secondsElapsed % 3 == 0){
					totalMateUsersBinding.Set("Loading.")
					numberOfMatchesBinding.Set("Loading.")
					matchPercentageBinding.Set("Loading.")
				} else if (secondsElapsed %3 == 1){
					totalMateUsersBinding.Set("Loading..")
					numberOfMatchesBinding.Set("Loading..")
					matchPercentageBinding.Set("Loading..")
				} else {
					totalMateUsersBinding.Set("Loading...")
					numberOfMatchesBinding.Set("Loading...")
					matchPercentageBinding.Set("Loading...")
				}

				time.Sleep(time.Second)
				secondsElapsed += 1
			}
		}

		go updateBindingsWithLoadingText()

		updateBindingsWithResultFunction := func()error{

			// We use below to make sure we do not count ourselves as a peer profile
			myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Mate")
			if (err != nil) { return err }

			mateIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Mate")
			if (err != nil) { return err }

			numberOfUsersWithEnabledViewableProfile := 0
			numberOfBlockedUsers := 0
			numberOfMatches := 0

			for _, userIdentityHash := range mateIdentityHashesList{

				if (myIdentityExists == true && userIdentityHash == myIdentityHash){
					// We don't count ourselves as a potential match
					continue
				}

				profileExists, _, getAnyUserProfileAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(userIdentityHash, appNetworkType, true, false, false)
				if (err != nil) { return err }
				if (profileExists == false) {
					continue
				}

				exists, _, _, err := getAnyUserProfileAttributeFunction("Disabled")
				if (err != nil) { return err }
				if (exists == true){
					// Profile is disabled
					continue
				}

				numberOfUsersWithEnabledViewableProfile += 1

				userIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(userIdentityHash)
				if (err != nil) { return err }
				if (userIsBlocked == true){
					numberOfBlockedUsers += 1
					continue
				}

				passesMyDesires, err := myMateDesires.CheckIfMateProfilePassesAllMyDesires(false, "", getAnyUserProfileAttributeFunction)
				if (err != nil) { return err }
				if (passesMyDesires == true){
					numberOfMatches += 1
				}
			}

			resultsReadyBoolMutex.Lock()
			resultsReadyBool = true
			resultsReadyBoolMutex.Unlock()

			getMatchPercentage := func()float64{
				if (numberOfUsersWithEnabledViewableProfile == 0){
					return 0
				}

				matchPercentage := (float64(numberOfMatches)/float64(numberOfUsersWithEnabledViewableProfile)) * 100
				return matchPercentage				
			}

			matchPercentage := getMatchPercentage()

			totalUsersString := helpers.ConvertIntToString(numberOfUsersWithEnabledViewableProfile)
			totalBlockedUsersString := helpers.ConvertIntToString(numberOfBlockedUsers)
			numberOfMatchesString := helpers.ConvertIntToString(numberOfMatches)
			matchPercentageString := helpers.ConvertFloat64ToStringRounded(matchPercentage, 1)

			totalMateUsersBinding.Set(totalUsersString)
			totalBlockedUsersBinding.Set(totalBlockedUsersString)
			numberOfMatchesBinding.Set(numberOfMatchesString)
			matchPercentageBinding.Set(matchPercentageString + "%")

			return nil
		}

		err := updateBindingsWithResultFunction()
		if (err != nil){
			pageHasChanged := checkIfPageHasChangedFunction()
			if (pageHasChanged == false){
				fyne.Do(func(){setErrorEncounteredPage(window, err, previousPage)})
			}
		}
	}

	viewAllDesireStatisticsButton := getWidgetCentered(widget.NewButtonWithIcon("View All My Desire Statistics", theme.VisibilityIcon(), func(){
			setViewAllMyDesireStatisticsPage(window, false, 0, 0, nil, currentPage)
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), accuracyWarningButton, widget.NewSeparator(), totalMateUsersRow, totalBlockedUsersRow, numberOfMatchesRow, matchPercentageRow, viewAllDesireStatisticsButton)

	setPageContent(page, window)

	go updateBindingsFunction()
}


func setCustomizeMatchDisplayPage(window fyne.Window, previousPage func()){

	appMemory.SetMemoryEntry("CurrentViewedPage", "CustomizeMatchDisplay")

	currentPage := func(){setCustomizeMatchDisplayPage(window, previousPage)}

	title := getPageTitleCentered("Customize Match Display")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Select the attributes to show when browsing matches.")
	description2 := getLabelCentered("A maximum of 5 attributes can be selected.")

	getCustomDisplayAttributesList := func()([]string, error){

		exists, currentAttributesListString, err := mySettings.GetSetting("CustomMatchDisplayAttributesList")
		if (err != nil) { return nil, err }
		if (exists == false){
			emptyList := make([]string, 0)
			return emptyList, nil
		}

		currentAttributesList := strings.Split(currentAttributesListString, ",")
		return currentAttributesList, nil
	}

	customDisplayAttributesList, err := getCustomDisplayAttributesList()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	addAttributeButton := getWidgetCentered(widget.NewButtonWithIcon("Add Attribute", theme.ContentAddIcon(), func(){
			if (len(customDisplayAttributesList) >= 5){
				dialogTitle := translate("Attribute Limit Reached.")
				dialogMessageA := getLabelCentered("You can only select 5 attributes.")
				dialogContent := container.NewVBox(dialogMessageA)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}
			setAddAttributeToCustomMatchDisplayPage(window, currentPage, currentPage)
		}))

	if (len(customDisplayAttributesList) == 0){

		noAttributesSelectedLabel := getBoldLabelCentered("No Attributes Selected")

		page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), noAttributesSelectedLabel, addAttributeButton)

		setPageContent(page, window)
		return
	}

	currentAttributesLabel := getItalicLabelCentered("Current Attributes:")

	getCurrentAttributesGrid := func()(*fyne.Container, error){

		attributeIndexesColumn := container.NewVBox()
		attributeTitlesColumn := container.NewVBox()
		attributeRemoveButtonsColumn := container.NewVBox()

		for index, attributeName := range customDisplayAttributesList{

			attributeIndexString := helpers.ConvertIntToString(index+1) + "."

			attributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
			if (err != nil) { return nil, err }

			attributeIndexLabel := getBoldLabelCentered(attributeIndexString)
			attributeTitleLabel := getBoldLabelCentered(attributeTitle)

			deleteAttributeButton := widget.NewButtonWithIcon("", theme.DeleteIcon(), func(){

					newAttributesList, _ := helpers.DeleteAllMatchingItemsFromList(customDisplayAttributesList, attributeName)

					if (len(newAttributesList) == 0){

						err := mySettings.DeleteSetting("CustomMatchDisplayAttributesList")
						if (err != nil) {
							setErrorEncounteredPage(window, err, currentPage)
							return
						}

						currentPage()
						return
					}

					newAttributesListString := strings.Join(newAttributesList, ",")

					err := mySettings.SetSetting("CustomMatchDisplayAttributesList", newAttributesListString)
					if (err != nil) {
						setErrorEncounteredPage(window, err, currentPage)
						return
					}

					currentPage()
				})

			attributeIndexesColumn.Add(attributeIndexLabel)
			attributeTitlesColumn.Add(attributeTitleLabel)
			attributeRemoveButtonsColumn.Add(deleteAttributeButton)

			attributeIndexesColumn.Add(widget.NewSeparator())
			attributeTitlesColumn.Add(widget.NewSeparator())
			attributeRemoveButtonsColumn.Add(widget.NewSeparator())
		}

		attributesGrid := container.NewHBox(layout.NewSpacer(), attributeIndexesColumn, attributeTitlesColumn, attributeRemoveButtonsColumn, layout.NewSpacer())

		return attributesGrid, nil
	}

	currentAttributesGrid, err := getCurrentAttributesGrid()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addAttributeButton, widget.NewSeparator(), currentAttributesLabel, currentAttributesGrid)

	setPageContent(page, window)
}

func setAddAttributeToCustomMatchDisplayPage(window fyne.Window, previousPage func(), nextPage func()){

	currentPage := func(){setAddAttributeToCustomMatchDisplayPage(window, previousPage, nextPage)}
	
	title := getPageTitleCentered("Add Attribute")

	backButton := getBackButtonCentered(previousPage)

	description := getLabelCentered("Choose the attribute to display.")

	getPageContent := func()(*fyne.Container, error){

		getCustomDisplayAttributesList := func()([]string, error){

			exists, currentAttributesListString, err := mySettings.GetSetting("CustomMatchDisplayAttributesList")
			if (err != nil) { return nil, err }
			if (exists == false){
				emptyList := make([]string, 0)
				return emptyList, nil
			}
			currentAttributesList := strings.Split(currentAttributesListString, ",")
			return currentAttributesList, nil
		}

		customDisplayAttributesList, err := getCustomDisplayAttributesList()
		if (err != nil){ return nil, err }

		if (len(customDisplayAttributesList) >= 5){
			return nil, errors.New("setAddAttributeToCustomMatchDisplayPage called when attributesList is full.")
		}

		generalAttributeButtonsGrid := container.NewGridWithColumns(1)
		physicalAttributeButtonsGrid := container.NewGridWithColumns(1)
		lifestyleAttributeButtonsGrid := container.NewGridWithColumns(1)
		mentalAttributeButtonsGrid := container.NewGridWithColumns(1)

		addAttributeButton := func(attributeName string, attributeType string)error{

			isSelected := slices.Contains(customDisplayAttributesList, attributeName)
			if (isSelected == true){
				// Attribute has already been selected
				// We will not show it
				return nil
			}

			attributeTitle, _, _, _, _, err := attributeDisplay.GetProfileAttributeDisplayInfo(attributeName)
			if (err != nil) { return err }

			attributeButton := widget.NewButton(attributeTitle, func(){

					newAttributesList := append(customDisplayAttributesList, attributeName)

					newAttributesListString := strings.Join(newAttributesList, ",")

					err := mySettings.SetSetting("CustomMatchDisplayAttributesList", newAttributesListString)
					if (err != nil) {
						setErrorEncounteredPage(window, err, currentPage)
						return
					}
					nextPage()
				})

			if (attributeType == "General"){

				generalAttributeButtonsGrid.Add(attributeButton)

			} else if (attributeType == "Physical"){

				physicalAttributeButtonsGrid.Add(attributeButton)

			} else if (attributeType == "Lifestyle"){

				lifestyleAttributeButtonsGrid.Add(attributeButton)

			} else if (attributeType == "Mental"){

				mentalAttributeButtonsGrid.Add(attributeButton)

			} else {
				return errors.New("addAttributeButton called with invalid attributeType: " + attributeType)
			}

			return nil
		}

		generalAttributeNamesList := []string{
			"MatchScore",
			"Username",
			"Age",
			"Sexuality",
			"Distance",
			"HasMessagedMe",
			"IHaveMessaged",
			"HasRejectedMe",
			"IsLiked",
			"IsMyContact",
			"SearchTermsCount",
			"PrimaryLocationCountry",
		}

		physicalAttributeNamesList := []string{
			"Height",
			"Sex",
			"HairColor",
			"HairTexture",
			"EyeColor",
			"SkinColor",
			"DeepAppearanceSimilarity",
			"HasHIV",
			"HasGenitalHerpes",
			"BodyFat",
			"BodyMuscle",
			"23andMe_MaternalHaplogroup",
			"23andMe_PaternalHaplogroup",
			"23andMe_NeanderthalVariants",
			"EyeColorSimilarity",
			"EyeColorGenesSimilarity",
			"HairColorSimilarity",
			"HairColorGenesSimilarity",
			"SkinColorSimilarity",
			"SkinColorGenesSimilarity",
			"HairTextureSimilarity",
			"HairTextureGenesSimilarity",
			"FacialStructureGenesSimilarity",
			"23andMe_AncestralSimilarity",
			"23andMe_MaternalHaplogroupSimilarity",
			"23andMe_PaternalHaplogroupSimilarity",
			"OffspringProbabilityOfAnyMonogenicDisease",
			"TotalPolygenicDiseaseRiskScore",
			"OffspringTotalPolygenicDiseaseRiskScore",
			"AutismRiskScore",
			"OffspringAutismRiskScore",
			"ObesityRiskScore",
			"OffspringObesityRiskScore",
			"PredictedEyeColor",
			"OffspringBlueEyesProbability",
			"OffspringGreenEyesProbability",
			"OffspringHazelEyesProbability",
			"OffspringBrownEyesProbability",
			"PredictedLactoseTolerance",
			"OffspringLactoseToleranceProbability",
			"PredictedHairTexture",
			"OffspringStraightHairProbability",
			"OffspringCurlyHairProbability",
			"HomosexualnessScore",
			"OffspringHomosexualnessScore",
			"PredictedHeight",
			"OffspringPredictedHeight",
		}

		lifestyleAttributeNamesList := []string{
			"Fame",
			"WealthInGold",
			"AlcoholFrequency",
			"TobaccoFrequency",
			"CannabisFrequency",
			"FruitRating",
			"VegetablesRating",
			"NutsRating",
			"GrainsRating",
			"DairyRating",
			"SeafoodRating",
			"BeefRating",
			"PorkRating",
			"PoultryRating",
			"EggsRating",
			"BeansRating",
		}

		mentalAttributeNamesList := []string{
			"GenderIdentity",
			"PetsRating",
			"DogsRating",
			"CatsRating",
		}

		for _, attributeName := range generalAttributeNamesList{

			err = addAttributeButton(attributeName, "General")
			if (err != nil) { return nil, err }
		}

		for _, attributeName := range physicalAttributeNamesList{

			err = addAttributeButton(attributeName, "Physical")
			if (err != nil) { return nil, err }
		}

		for _, attributeName := range lifestyleAttributeNamesList{

			err = addAttributeButton(attributeName, "Lifestyle")
			if (err != nil) { return nil, err }
		}

		for _, attributeName := range mentalAttributeNamesList{

			err = addAttributeButton(attributeName, "Mental")
			if (err != nil) { return nil, err }
		}


		generalLabel := getBoldLabelCentered("General")
		physicalLabel := getBoldLabelCentered("Physical")
		lifestyleLabel := getBoldLabelCentered("Lifestyle")
		mentalLabel := getBoldLabelCentered("Mental")

		generalAttributeButtonsGridCentered := getContainerCentered(generalAttributeButtonsGrid)
		physicalAttributeButtonsGridCentered := getContainerCentered(physicalAttributeButtonsGrid)
		lifestyleAttributeButtonsGridCentered := getContainerCentered(lifestyleAttributeButtonsGrid)
		mentalAttributeButtonsGridCentered := getContainerCentered(mentalAttributeButtonsGrid)

		pageContent := container.NewVBox(generalLabel, widget.NewSeparator(), generalAttributeButtonsGridCentered, widget.NewSeparator(), physicalLabel, widget.NewSeparator(), physicalAttributeButtonsGridCentered, widget.NewSeparator(), lifestyleLabel, widget.NewSeparator(), lifestyleAttributeButtonsGridCentered, widget.NewSeparator(), mentalLabel, widget.NewSeparator(), mentalAttributeButtonsGridCentered)

		return pageContent, nil
	}

	pageContent, err := getPageContent()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), pageContent)

	setPageContent(page, window)
}


func setConfigureMatchScorePage(window fyne.Window, previousPage func()){

	setLoadingScreen(window, "Configure Match Score", "Loading Configure Match Score page...")

	currentPage := func(){setConfigureMatchScorePage(window, previousPage)}

	title := getPageTitleCentered("Configure Match Score")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Customize how a user's match score is calculated.")
	description2 := getLabelCentered("Each desire's points will be added to a user's match score if they fulfills the desire.")
	description3 := getLabelCentered("Increase the Point value for desires that are more important to you.")

	viewMatchScoreDistributionButton := getWidgetCentered(widget.NewButtonWithIcon("View Match Score Distribution", theme.VisibilityIcon(), func(){
			setViewUserAttributeStatisticsPage_BarChart(window, "Mate", "MatchScore", "Number Of Users", " users", true, false, false, nil, false, nil, nil, currentPage)
		}))

	//TODO: Split into multiple pages and add Navigation buttons

	getDesirePointsGrid := func()(*fyne.Container, error){

		desireNameLabel := getItalicLabelCentered("Desire Name")
		pointsLabel := getItalicLabelCentered("Points")
		emptyLabel := widget.NewLabel("")

		desireTitleColumn := container.NewVBox(desireNameLabel, widget.NewSeparator())
		desirePointsColumn := container.NewVBox(pointsLabel, widget.NewSeparator())
		editPointsButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())

		allMyDesiresList := myMateDesires.GetAllMyDesiresList(false)

		for _, desireName := range allMyDesiresList{

			desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName)
			if (err != nil) { return nil, err }

			desireTitleTranslated := translate(desireTitle)

			desireTitleLabel := getBoldLabelCentered(desireTitleTranslated)

			desirePoints, err := myMatchScore.GetMyMatchScoreDesirePoints(desireName)
			if (err != nil) { return nil, err }

			desirePointsString := helpers.ConvertIntToString(desirePoints)
			desirePointsLabel := getBoldLabelCentered(desirePointsString)

			editPointsButton := widget.NewButtonWithIcon("", theme.DocumentCreateIcon(), func(){
					setEditMatchScoreDesirePointsPage(window, desireName, currentPage)
				})

			desireTitleColumn.Add(desireTitleLabel)
			desirePointsColumn.Add(desirePointsLabel)
			editPointsButtonsColumn.Add(editPointsButton)

			desireTitleColumn.Add(widget.NewSeparator())
			desirePointsColumn.Add(widget.NewSeparator())
			editPointsButtonsColumn.Add(widget.NewSeparator())
		}

		desirePointsGrid := container.NewHBox(layout.NewSpacer(), desireTitleColumn, desirePointsColumn, editPointsButtonsColumn, layout.NewSpacer())
		return desirePointsGrid, nil
	}

	desirePointsGrid, err := getDesirePointsGrid()
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), viewMatchScoreDistributionButton, widget.NewSeparator(), desirePointsGrid)
	
	setPageContent(page, window)
}


func setEditMatchScoreDesirePointsPage(window fyne.Window, desireName string, previousPage func()){

	currentPage := func(){setEditMatchScoreDesirePointsPage(window, desireName, previousPage)}

	title := getPageTitleCentered("Edit Attribute Points")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Edit your desire's points.")
	description2 := getLabelCentered("These will be added to a user's match score if they fulfill the desire.")

	desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName)
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return					
	}

	desireNameLabel := widget.NewLabel("Desire Name:")
	desireTitleLabel := getBoldLabel(desireTitle)
	desireTitleRow := container.NewHBox(layout.NewSpacer(), desireNameLabel, desireTitleLabel, layout.NewSpacer())

	desirePoints, err := myMatchScore.GetMyMatchScoreDesirePoints(desireName)
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	desirePointsString := helpers.ConvertIntToString(desirePoints)

	desirePointsTitle := getBoldLabelCentered("Desire Points:")

	desirePointsLabel := getBoldLabelCentered(desirePointsString)

	getDecreasePointsButton := func()fyne.Widget{

		if (desirePoints > 1){
			decreaseButton := widget.NewButton("-", func(){ 
					err := myMatchScore.SetMyMatchScoreDesirePoints(desireName, desirePoints-1)
					if (err != nil) {
						setErrorEncounteredPage(window, err, currentPage)
						return					
					}
					currentPage()
				})
			return decreaseButton			
		}

		emptyButton := widget.NewButton("", nil)

		return emptyButton
	}

	getIncreasePointsButton := func()fyne.Widget{

		if (desirePoints < 100){
			increaseButton := widget.NewButton("+", func(){ 
					err := myMatchScore.SetMyMatchScoreDesirePoints(desireName, desirePoints+1)
					if (err != nil) {
						setErrorEncounteredPage(window, err, currentPage)
						return					
					}
					currentPage()
				})
			return increaseButton
		}
		emptyButton := widget.NewButton("", nil)
		return emptyButton
	}
			
	decreasePointsButton := getDecreasePointsButton()
	increasePointsButton := getIncreasePointsButton()

	decreaseIncreaseButtons := getContainerCentered(container.NewGridWithColumns(2, decreasePointsButton, increasePointsButton))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, widget.NewSeparator(), desireTitleRow, widget.NewSeparator(), desirePointsTitle, desirePointsLabel, decreaseIncreaseButtons)

	setPageContent(page, window)
}


// This page will explain a user's match score by describing which desires the user passes
func setViewUserMatchScoreBreakdownPage(window fyne.Window, userIdentityHash [16]byte, previousPage func()){

	userIdentityType, err := identity.GetIdentityTypeFromIdentityHash(userIdentityHash)
	if (err != nil){
		userIdentityHashHex := encoding.EncodeBytesToHexString(userIdentityHash[:])
		setErrorEncounteredPage(window, errors.New("setViewUserMatchScoreBreakdownPage called with invalid userIdentityHash: " + userIdentityHashHex), previousPage)
		return
	}
	if (userIdentityType != "Mate"){
		setErrorEncounteredPage(window, errors.New("setViewUserMatchScoreBreakdownPage called with non-mate identity hash."), previousPage)
		return
	}

	currentPage := func(){setViewUserMatchScoreBreakdownPage(window, userIdentityHash, previousPage)}

	title := getPageTitleCentered("User Match Score Breakdown")

	backButton := getBackButtonCentered(previousPage)

	appNetworkType, err := getAppNetworkType.GetAppNetworkType()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	profileExists, _, getAnyUserProfileAttributeFunction, err := viewableProfiles.GetRetrieveAnyNewestViewableUserProfileAttributeFunction(userIdentityHash, appNetworkType, true, false, true)
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (profileExists == false) {
		// Profile must have been deleted or it is not viewable
		description1 := getBoldLabelCentered("User's profile is not found.")
		description2 := getLabelCentered("Thus, the user's match score breakdown cannot be shown.")
		description3 := getLabelCentered("Their profile was either banned or deleted.")
		description4 := getLabelCentered("Deletion should only happen if the user is no longer a match.")

		page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3, description4)

		setPageContent(page, window)
		return
	}

	exists, _, userIsDisabled, err := getAnyUserProfileAttributeFunction("Disabled")
	if (err != nil) { 
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (exists == true && userIsDisabled == "Yes"){
		// User's newest viewable profile is disabled.
		description1 := getBoldLabelCentered("User's profile is disabled.")
		description2 := getLabelCentered("Thus, the user's match score breakdown cannot be shown.")

		page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2)

		setPageContent(page, window)
		return
	}

	// This bool will track if we have any desires at all
	anyPreferenceExists := false

	userMatchScore := 0

	// This map stores the number of points we added for each desire
	// Map Structure: Desire name -> Points we added
	desirePointsMap := make(map[string]int)

	// This is a list of desires which the user fulfills
	fulfilledDesiresList := make([]string, 0)

	// This is a list of desires which the user does not fulfill
	unfulfilledDesiresList := make([]string, 0)

	// This is a list of desires for which we do not know if the user fulfills our preference, not caused by them not responding
	unknownStatusDesiresList := make([]string, 0)

	// This is a list of desires for which the user has no response
	noResponseExistsDesiresList := make([]string, 0)

	// This is a list of desires which we have no preference for
	noPreferenceExistsDesiresList := make([]string, 0)

	allMyDesiresList := myMateDesires.GetAllMyDesiresList(false)

	for _, desireName := range allMyDesiresList{

		myDesireExists, statusIsKnown, userFulfillsDesire, err := myMateDesires.CheckIfMateProfileFulfillsMyDesire(desireName, getAnyUserProfileAttributeFunction)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}
		if (myDesireExists == false){

			noPreferenceExistsDesiresList = append(noPreferenceExistsDesiresList, desireName)
			continue
		}

		anyPreferenceExists = true

		if (statusIsKnown == false){

			noResponseIsPossible, _ := mateDesires.CheckIfDesireAllowsRequireResponse(desireName)
			if (noResponseIsPossible == false){
				// Whether or not the user responded does not effect the desire's status is known status
				// An example is OffspringProbabilityOfAnyMonogenicDisease, which requires information from us *and* the user
				unknownStatusDesiresList = append(unknownStatusDesiresList, desireName)
				continue
			}
			noResponseExistsDesiresList = append(noResponseExistsDesiresList, desireName)
			continue
		}

		if (userFulfillsDesire == false){

			unfulfilledDesiresList = append(unfulfilledDesiresList, desireName)
			continue
		}

		fulfilledDesiresList = append(fulfilledDesiresList, desireName)

		pointsToAdd, err := myMatchScore.GetMyMatchScoreDesirePoints(desireName)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}

		desirePointsMap[desireName] += pointsToAdd

		userMatchScore += pointsToAdd
	}

	if (anyPreferenceExists == false){

		description1 := getBoldLabelCentered("You have not added any desires.")
		description2 := getLabelCentered("Thus, the match score for all users will be 0.")
		description3 := getLabelCentered("Visit the Desires page to enter some desires.")

		page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, description3)

		setPageContent(page, window)
		return
	}

	matchScoreTitle := widget.NewLabel("User Match Score:")

	matchScoreString := helpers.ConvertIntToString(userMatchScore)

	matchScoreLabel := getBoldLabel(matchScoreString)

	matchScoreRow := container.NewHBox(layout.NewSpacer(), matchScoreTitle, matchScoreLabel, layout.NewSpacer())

	getMatchScoreBreakdownGrid := func()(*fyne.Container, error){

		desireNameHeader := getItalicLabelCentered("Desire Name")
		userFulfillsDesireHeader := getItalicLabelCentered("User Fulfills Desire")
		pointsAddedHeader := getItalicLabelCentered("Points Added")

		desireNameColumn := container.NewVBox(desireNameHeader, widget.NewSeparator())
		userFulfillsDesireColumn := container.NewVBox(userFulfillsDesireHeader, widget.NewSeparator())
		pointsAddedColumn := container.NewVBox(pointsAddedHeader, widget.NewSeparator())

		for _, desireName := range fulfilledDesiresList{

			desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName)
			if (err != nil) { return nil, err }

			desireTitleLabel := getBoldLabelCentered(desireTitle)

			userFulfillsDesireLabel := getBoldLabelCentered(translate("Yes"))

			pointsAdded, exists := desirePointsMap[desireName]
			if (exists == false){
				return nil, errors.New("desirePointsMap missing desire: " + desireName)
			}

			pointsAddedString := helpers.ConvertIntToString(pointsAdded)
			pointsAddedLabel := getBoldLabelCentered("+" + pointsAddedString)

			desireNameColumn.Add(desireTitleLabel)
			userFulfillsDesireColumn.Add(userFulfillsDesireLabel)
			pointsAddedColumn.Add(pointsAddedLabel)

			desireNameColumn.Add(widget.NewSeparator())
			userFulfillsDesireColumn.Add(widget.NewSeparator())
			pointsAddedColumn.Add(widget.NewSeparator())
		}

		for _, desireName := range unfulfilledDesiresList{

			desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName)
			if (err != nil) { return nil, err }

			desireTitleLabel := getBoldLabelCentered(desireTitle)

			userFulfillsDesireLabel := getBoldLabelCentered(translate("No"))

			pointsAddedLabel := getBoldLabelCentered("0")

			desireNameColumn.Add(desireTitleLabel)
			userFulfillsDesireColumn.Add(userFulfillsDesireLabel)
			pointsAddedColumn.Add(pointsAddedLabel)

			desireNameColumn.Add(widget.NewSeparator())
			userFulfillsDesireColumn.Add(widget.NewSeparator())
			pointsAddedColumn.Add(widget.NewSeparator())	
		}

		for _, desireName := range unknownStatusDesiresList{

			desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName)
			if (err != nil) { return nil, err }

			desireTitleLabel := getBoldLabelCentered(desireTitle)

			userFulfillsDesireLabel := getItalicLabelCentered(translate("Unknown"))

			pointsAddedLabel := getBoldLabelCentered("0")

			desireNameColumn.Add(desireTitleLabel)
			userFulfillsDesireColumn.Add(userFulfillsDesireLabel)
			pointsAddedColumn.Add(pointsAddedLabel)

			desireNameColumn.Add(widget.NewSeparator())
			userFulfillsDesireColumn.Add(widget.NewSeparator())
			pointsAddedColumn.Add(widget.NewSeparator())
		}

		for _, desireName := range noResponseExistsDesiresList{

			desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName)
			if (err != nil) { return nil, err }

			desireTitleLabel := getBoldLabelCentered(desireTitle)

			userFulfillsDesireLabel := getItalicLabelCentered(translate("No Response"))

			pointsAddedLabel := getBoldLabelCentered("0")

			desireNameColumn.Add(desireTitleLabel)
			userFulfillsDesireColumn.Add(userFulfillsDesireLabel)
			pointsAddedColumn.Add(pointsAddedLabel)

			desireNameColumn.Add(widget.NewSeparator())
			userFulfillsDesireColumn.Add(widget.NewSeparator())
			pointsAddedColumn.Add(widget.NewSeparator())
		}

		matchScoreBreakdownGrid := container.NewHBox(layout.NewSpacer(), desireNameColumn, userFulfillsDesireColumn, pointsAddedColumn, layout.NewSpacer())

		return matchScoreBreakdownGrid, nil
	}

	matchScoreBreakdownGrid, err := getMatchScoreBreakdownGrid()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	page := container.NewVBox(title, backButton, widget.NewSeparator(), matchScoreRow, widget.NewSeparator(), matchScoreBreakdownGrid)

		numberOfNoPreferenceDesires := len(noPreferenceExistsDesiresList)

	if (numberOfNoPreferenceDesires != 0){

		numberOfNoPreferenceDesiresString := helpers.ConvertIntToString(numberOfNoPreferenceDesires)

		description1 := getLabelCentered("You have " + numberOfNoPreferenceDesiresString + " desires with no preference.")
		description2 := getLabelCentered("These desires will have no impact on match scores.")

		viewMyNoPreferenceDesiresButton := getWidgetCentered(widget.NewButtonWithIcon("View My No Preference Desires", theme.VisibilityIcon(), func(){
				setViewMyNoPreferenceDesiresPage(window, noPreferenceExistsDesiresList, currentPage)
			}))

		page.Add(widget.NewSeparator())
		page.Add(description1)
		page.Add(description2)
		page.Add(viewMyNoPreferenceDesiresButton)
	}

	setPageContent(page, window)
}


func setViewMyNoPreferenceDesiresPage(window fyne.Window, myNoPreferenceDesiresList []string, previousPage func()){

	title := getPageTitleCentered("My No Preference Desires")

	backButton := getBackButtonCentered(previousPage)

	description := getLabelCentered("Below is a list of your desires for which you have no provided no preference.")

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator())

	for _, desireName := range myNoPreferenceDesiresList{

		desireTitle, err := mateDesires.GetDesireTitleFromDesireName(desireName)
		if (err != nil) {
			setErrorEncounteredPage(window, err, previousPage)
			return
		}

		desireTitleLabel := getBoldLabelCentered(desireTitle)

		page.Add(desireTitleLabel)
	}

	setPageContent(page, window)
}


