package gui

// buildProfileGui_General.go impements pages to build the general portion of a user profile
// Some attributes support multiple profileTypes, most attributes are only used for Mate profiles

import "fyne.io/fyne/v2"
import "fyne.io/fyne/v2/canvas"
import "fyne.io/fyne/v2/container"
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/worldLanguages"
import "seekia/internal/worldLocations"
import "seekia/internal/imageFiles"
import "seekia/internal/allowedText"
import "seekia/internal/globalSettings"
import "seekia/internal/helpers"
import "seekia/internal/imagery"
import "seekia/internal/mateQuestionnaire"
import "seekia/internal/profiles/myLocalProfiles"

import "strings"
import "errors"
import "image"
import "reflect"
import "slices"


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

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

	title := getPageTitleCentered(translate("Build Mate Profile"))

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Build your Mate profile.")
	description2 := getLabelCentered("All information is optional.")

	generalIcon, err := getFyneImageIcon("General")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	physicalIcon, err := getFyneImageIcon("Person")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	
	lifestyleIcon, err := getFyneImageIcon("Lifestyle")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	
	mentalIcon, err := getFyneImageIcon("Mental")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	generalButton := widget.NewButton(translate("General"), func(){
			setBuildMateProfileCategoryPage_General(window, currentPage)
		})
	generalButtonWithIcon := container.NewGridWithRows(2, generalIcon, generalButton)

	physicalButton := widget.NewButton(translate("Physical"), func(){
			setBuildMateProfileCategoryPage_Physical(window, currentPage)
		})
	physicalButtonWithIcon := container.NewGridWithRows(2, physicalIcon, physicalButton)

	lifestyleButton := widget.NewButton(translate("Lifestyle"), func(){
			setBuildMateProfileCategoryPage_Lifestyle(window, currentPage)
		})
	lifestyleButtonWithIcon := container.NewGridWithRows(2, lifestyleIcon, lifestyleButton)

	mentalButton := widget.NewButton(translate("Mental"), func(){
			setBuildMateProfileCategoryPage_Mental(window, currentPage)
		})
	mentalButtonWithIcon := container.NewGridWithRows(2, mentalIcon, mentalButton)
	
	categoriesRow := container.NewGridWithRows(1, generalButtonWithIcon, physicalButtonWithIcon, lifestyleButtonWithIcon, mentalButtonWithIcon)

	categoriesRowCentered := getContainerCentered(categoriesRow)

	categoriesRowPadded := container.NewPadded(categoriesRowCentered)

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


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

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

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	profileLanguageButton := widget.NewButton(translate("Profile Language"), func(){
			setBuildProfilePage_ProfileLanguage(window, "Mate", currentPage)
		})

	usernameButton := widget.NewButton(translate("Username"), func(){
			setBuildProfilePage_Username(window, "Mate", currentPage)
		})

	ageButton := widget.NewButton(translate("Age"), func(){
			setBuildMateProfilePage_Age(window, currentPage)
		})

	locationButton := widget.NewButton(translate("Location"), func(){
			setBuildMateProfilePage_Location(window, currentPage)
		})

	descriptionButton := widget.NewButton(translate("Description"), func(){
			setBuildProfilePage_Description(window, "Mate", currentPage)
		})

	photosButton := widget.NewButton(translate("Photos"), func(){
			setBuildMateProfilePage_Photos(window, 0, currentPage)
		})

	avatarButton :=  widget.NewButton(translate("Avatar"), func(){
			setBuildProfilePage_Avatar(window, "Mate", currentPage)
		})

	sexualityButton := widget.NewButton(translate("Sexuality"), func(){
			setBuildMateProfilePage_Sexuality(window, currentPage)
		})

	tagsButton := widget.NewButton(translate("Tags"), func(){
			setBuildMateProfilePage_Tags(window, currentPage)
		})

	questionnaireButton := widget.NewButton(translate("Questionnaire"), func(){
			setBuildMateProfilePage_Questionnaire(window, currentPage)
		})	

	buttonsGrid := container.NewGridWithColumns(1, profileLanguageButton, usernameButton, ageButton, locationButton, descriptionButton, photosButton, avatarButton, sexualityButton, tagsButton, questionnaireButton)

	buttonsGridCentered := getContainerCentered(buttonsGrid)

	buttonsGridPadded := container.NewPadded(buttonsGridCentered)

	page := container.NewVBox(title, backButton, widget.NewSeparator(), buttonsGridPadded)
	
	setPageContent(page, window)
}

func setBuildProfilePage_ProfileLanguage(window fyne.Window, profileType string, previousPage func()){

	currentPage := func(){setBuildProfilePage_ProfileLanguage(window, profileType, previousPage)}

	title := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Profile Language"))

	description1 := getLabelCentered("Choose your profile language.")
	description2 := getLabelCentered("This is the language that your profile is written in.")
	description3 := getLabelCentered("For example, select English if your description/hobbies/... are written in English.")

	currentLanguageExists, currentLanguageIdentifier, err := myLocalProfiles.GetProfileData(profileType, "ProfileLanguage")
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

		if (currentLanguageExists == false){

			noResponseLabel := getBoldItalicLabel("No Response")

			return noResponseLabel, nil
		}

		currentLanguageIdentifierInt, err := helpers.ConvertStringToInt(currentLanguageIdentifier)
		if (err != nil){
			return nil, errors.New("MyLocalProfile is malformed: Contains invalid ProfileLanguage: " + currentLanguageIdentifier)		
		}

		currentLanguageObject, err := worldLanguages.GetLanguageObjectFromLanguageIdentifier(currentLanguageIdentifierInt)
		if (err != nil) { return nil, err }

		currentLanguageNamesList := currentLanguageObject.NamesList

		languageDescription := helpers.TranslateAndJoinStringListItems(currentLanguageNamesList, "/")
	
		languageNamesLabel := getBoldLabel(languageDescription)

		return languageNamesLabel, nil
	}

	currentLanguageLabel, err := getCurrentLanguageLabel()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	myProfileLanguageLabel := widget.NewLabel("My Profile Language:")
	
	myProfileLanguageRow := container.NewHBox(layout.NewSpacer(), myProfileLanguageLabel, currentLanguageLabel, layout.NewSpacer())

	getChooseOrEditButtonText := func()string{
		if (currentLanguageExists == false){
			result := translate("Choose Language")

			return result
		}
		result := translate("Edit Language")

		return result
	}

	chooseOrEditButtonText := getChooseOrEditButtonText()

	chooseOrEditLanguageButton := widget.NewButtonWithIcon(chooseOrEditButtonText, theme.DocumentCreateIcon(), func(){
			setBuildProfilePage_ChooseProfileLanguage(window, profileType, currentPage, currentPage)
		})

	noResponseButton := widget.NewButtonWithIcon("No Response", theme.CancelIcon(), func(){
			err := myLocalProfiles.DeleteProfileData(profileType, "ProfileLanguage")
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			currentPage()
		})

	buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, chooseOrEditLanguageButton, noResponseButton))

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

	setPageContent(page, window)
}

func setBuildProfilePage_ChooseProfileLanguage(window fyne.Window, profileType string, previousPage func(), nextPage func()){

	currentPage := func(){setBuildProfilePage_ChooseProfileLanguage(window, profileType, previousPage, nextPage)}

	title := getPageTitleCentered(translate("Build Mate Profile - General"))	

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered("Choose Language")

	description := getLabelCentered("Choose your profile language.")

	worldLanguageObjectsList, err := worldLanguages.GetWorldLanguageObjectsList()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	// This list stores the translated language names
	worldLanguageDescriptionsList := make([]string, 0, len(worldLanguageObjectsList))

	// This map will store the language identifiers
	//Map Structure: Language Description -> Language identifier
	worldLanguageIdentifiersMap := make(map[string]int)

	for _, languageObject := range worldLanguageObjectsList{

		languageIdentifier := languageObject.Identifier
		languageNamesList := languageObject.NamesList

		languageDescription := helpers.TranslateAndJoinStringListItems(languageNamesList, "/")
		
		worldLanguageDescriptionsList = append(worldLanguageDescriptionsList, languageDescription)

		worldLanguageIdentifiersMap[languageDescription] = languageIdentifier
	}

	helpers.SortStringListToUnicodeOrder(worldLanguageDescriptionsList)

	onSelectedFunction := func(itemIndex int){

		languageDescription := worldLanguageDescriptionsList[itemIndex]

		languageIdentifier, exists := worldLanguageIdentifiersMap[languageDescription]
		if (exists == false){
			setErrorEncounteredPage(window, errors.New("worldLanguageIdentifiersMap missing languageDescription: " + languageDescription), currentPage)
			return
		}

		languageIdentifierString := helpers.ConvertIntToString(languageIdentifier)

		err := myLocalProfiles.SetProfileData(profileType, "ProfileLanguage", languageIdentifierString)
		if (err != nil){
			setErrorEncounteredPage(window, err, currentPage)
			return
		}

		nextPage()
	}

	languagesWidgetList, err := getFyneWidgetListFromStringList(worldLanguageDescriptionsList, onSelectedFunction)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	page := container.NewBorder(header, nil, nil, nil, languagesWidgetList)

	setPageContent(page, window)
}

func setBuildProfilePage_Username(window fyne.Window, profileType string, previousPage func()){

	currentPage := func(){setBuildProfilePage_Username(window, profileType, previousPage)}

	pageTitle := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))
	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getPageSubtitleCentered(translate("Username"))

	description1 := getLabelCentered(translate("Create your username."))
	description2 := getLabelCentered(translate("You do not have to enter your real name."))
	description3 := getLabelCentered(translate("If you want to protect your privacy, don't share your surname."))

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

		myUsernameLabel := widget.NewLabel("My Username:")

		exists, currentUsername, err := myLocalProfiles.GetProfileData(profileType, "Username")
		if (err != nil) { return nil, err }
		if (exists == false){

			noResponseLabel := getBoldItalicLabel("No Response")
			currentUsernameRow := container.NewHBox(layout.NewSpacer(), myUsernameLabel, noResponseLabel, layout.NewSpacer())

			return currentUsernameRow, nil
		}

		isAllowed := allowedText.VerifyStringIsAllowed(currentUsername)
		if (isAllowed == false){
			return nil, errors.New("My current " + profileType + " username is not allowed: Not valid utf8: " + currentUsername)
		}

		containsTabsOrNewlines := helpers.CheckIfStringContainsTabsOrNewlines(currentUsername)
		if (containsTabsOrNewlines == true){
			return nil, errors.New("My current " + profileType + " username is not allowed: Contains newline/tab: " + currentUsername)
		}

		if( len(currentUsername) > 25){
			return nil, errors.New("Current " + profileType + " username is too long: " + currentUsername)
		}

		currentUsernameLabel := getBoldLabel(currentUsername)

		currentUsernameRow := container.NewHBox(layout.NewSpacer(), myUsernameLabel, currentUsernameLabel, layout.NewSpacer())

		return currentUsernameRow, nil
	}

	currentUsernameRow, err := getMyCurrentUsernameRow()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	editButton := widget.NewButtonWithIcon("Edit", theme.DocumentCreateIcon(), func(){
			setBuildProfilePage_EditUsername(window, profileType, currentPage, currentPage)
		})

	noResponseButton := widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ 
			myLocalProfiles.DeleteProfileData(profileType, "Username")
			currentPage()
		})

	buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, editButton, noResponseButton))

	page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), currentUsernameRow, widget.NewSeparator(), buttonsGrid)

	setPageContent(page, window)
}

func setBuildProfilePage_EditUsername(window fyne.Window, profileType string, previousPage func(), nextPage func()){

	title := getPageTitleCentered("Edit " + profileType + " Username")

	backButton := getBackButtonCentered(previousPage)

	usernameEntry := widget.NewEntry()

	// Outputs:
	//	-bool: Current username exists
	//	-string: Current username
	//	-error
	getCurrentUsername := func()(bool, string, error){	

		exists, currentUsername, err := myLocalProfiles.GetProfileData(profileType, "Username")
		if (err != nil) { return false, "", err }
		if (exists == false){
			return false, "", nil
		}

		isAllowed := allowedText.VerifyStringIsAllowed(currentUsername)
		if (isAllowed == false){
			return false, "", errors.New("My current " + profileType + " username is not allowed: " + currentUsername)
		}

		containsTabsOrNewlines := helpers.CheckIfStringContainsTabsOrNewlines(currentUsername)
		if (containsTabsOrNewlines == true){
			return false, "", errors.New("My current " + profileType + " username is not allowed: " + currentUsername)
		}

		if( len(currentUsername) > 25){
			return false, "", errors.New("My current " + profileType + " username is too long: " + currentUsername)
		}

		return true, currentUsername, nil
	}

	currentUsernameExists, currentUsername, err := getCurrentUsername()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (currentUsernameExists == false){
		usernameEntry.SetPlaceHolder("Enter a username...")
	} else {
		usernameEntry.SetText(currentUsername)
	}

	submitButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Save"), theme.ConfirmIcon(), func(){

		newUsername := usernameEntry.Text
	
		isAllowed := allowedText.VerifyStringIsAllowed(newUsername)
		if (isAllowed == false){
			title := translate("Invalid Username")
			dialogMessageA := getLabelCentered(translate("Your username contains an invalid character."))
			dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		containsTabsOrNewlines := helpers.CheckIfStringContainsTabsOrNewlines(newUsername)
		if (containsTabsOrNewlines == true){
			title := translate("Invalid Username")
			dialogMessageA := getLabelCentered(translate("Your username contains a tab or a newline character."))
			dialogMessageB := getLabelCentered(translate("Remove this character and resubmit."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		currentBytesLength := len(newUsername)
		if (currentBytesLength > 25){

			currentLengthString := helpers.ConvertIntToString(currentBytesLength)

			title := translate("Invalid Username")
			dialogMessageA := getLabelCentered(translate("Username is too long."))
			dialogMessageB := getLabelCentered(translate("Username cannot be longer than 25 bytes."))
			dialogMessageC := getLabelCentered(translate("Your submission length: ") + currentLengthString + " bytes.")
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		if (newUsername == ""){
			myLocalProfiles.DeleteProfileData(profileType, "Username")
		} else {
			myLocalProfiles.SetProfileData(profileType, "Username", newUsername)
		}
		nextPage()
	}))

	emptyLabelA := widget.NewLabel("")
	emptyLabelB := widget.NewLabel("")
	emptyLabelC := widget.NewLabel("")

	submitButtonWithSpacers := container.NewVBox(submitButton, emptyLabelA, emptyLabelB, emptyLabelC)

	usernameEntryBoxed := getWidgetBoxed(usernameEntry)

	usernameEntryWithSubmitButton := container.NewBorder(nil, submitButtonWithSpacers, nil, nil, usernameEntryBoxed)

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

	page := container.NewBorder(header, nil, nil, nil, usernameEntryWithSubmitButton)

	setPageContent(page, window)
}


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

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

	pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getPageSubtitleCentered(translate("Age"))

	description1 := getLabelCentered(translate("Enter your age. You must be at least 18."))
	description2 := getLabelCentered(translate("You should update your age as you get older."))

	ageEntry := widget.NewEntry()

	ageExists, currentAge, err := myLocalProfiles.GetProfileData("Mate", "Age")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (ageExists == true){
		isValid, err := helpers.VerifyStringIsIntWithinRange(currentAge, 18, 150)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}
		if (isValid == false){
			setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid age."), previousPage)
			return
		}

		ageEntry.SetText(currentAge)
	} else {
		ageEntry.SetPlaceHolder(translate("Enter Age"))
	}
	
	getCurrentAgeLabel := func()fyne.Widget{

		if (ageExists == false){
			result := translate("No Response")

			ageLabel := getBoldItalicLabel(result)
			return ageLabel
		}

		ageLabel := getBoldLabel(currentAge)
		return ageLabel
	}

	currentAgeLabel := getCurrentAgeLabel()

	myAgeLabel := widget.NewLabel("My Age:")

	currentAgeRow := container.NewHBox(layout.NewSpacer(), myAgeLabel, currentAgeLabel, layout.NewSpacer())

	submitChangesButton := widget.NewButtonWithIcon("Submit", theme.ConfirmIcon(), func(){

		newAge := ageEntry.Text

		isValid, err := helpers.VerifyStringIsIntWithinRange(newAge, 18, 150)
		if (err != nil){
			setErrorEncounteredPage(window, err, currentPage)
			return
		}
		if (isValid == false){

			ageEntry.SetText("")
			ageEntry.SetPlaceHolder(translate("Enter Age"))

			title := translate("Invalid Age")
			dialogMessageA := getLabelCentered(translate("Invalid age entered."))
			dialogMessageB := getLabelCentered(translate("You must be at least 18 to use Seekia."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		err = myLocalProfiles.SetProfileData("Mate", "Age", newAge)
		if (err != nil){
			setErrorEncounteredPage(window, err, currentPage)
			return
		}

		currentPage()
	})

	ageEntryWithSubmitButton := getContainerCentered(container.NewGridWithRows(1, ageEntry, submitChangesButton))

	noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ 
			err := myLocalProfiles.DeleteProfileData("Mate", "Age")
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			currentPage()
		}))

	page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), currentAgeRow, widget.NewSeparator(), ageEntryWithSubmitButton, noResponseButton)
	
	setPageContent(page, window)
}


func setBuildProfilePage_Description(window fyne.Window, profileType string, previousPage func()){

	currentPage := func(){setBuildProfilePage_Description(window, profileType, previousPage)}

	pageTitle := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getPageSubtitleCentered(translate("Description"))

	pageDescription := getLabelCentered(translate("Create your profile description."))

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

		myDescriptionLabel := widget.NewLabel("My Description:")

		exists, currentDescription, err := myLocalProfiles.GetProfileData(profileType, "Description")
		if (err != nil) { return nil, err }
		if (exists == false){

			noResponseLabel := getBoldItalicLabel("No Response")
			currentDescriptionRow := container.NewHBox(layout.NewSpacer(), myDescriptionLabel, noResponseLabel, layout.NewSpacer())

			return currentDescriptionRow, nil
		}

		isAllowed := allowedText.VerifyStringIsAllowed(currentDescription)
		if (isAllowed == false){
			return nil, errors.New("My current " + profileType + "description is not allowed: Not utf8.")
		}

		getSizeLimit := func()int{
			if (profileType == "Moderator"){
				return 500
			}
			if (profileType == "Host"){
				return 300
			}
			return 3000
		}

		sizeLimit := getSizeLimit()

		if (len(currentDescription) > sizeLimit){
			return nil, errors.New("My current " + profileType + " description is too long.")
		}

		currentDescriptionTrimmed, _, err := helpers.TrimAndFlattenString(currentDescription, 15)
		if (err != nil) { return nil, err }

		currentDescriptionLabel := getBoldLabel(currentDescriptionTrimmed)

		viewMyDescriptionButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
				setViewTextPage(window, "Viewing Description", currentDescription, false, currentPage)
			})

		currentDescriptionRow := container.NewHBox(layout.NewSpacer(), myDescriptionLabel, currentDescriptionLabel, viewMyDescriptionButton, layout.NewSpacer())

		return currentDescriptionRow, nil
	}

	currentDescriptionRow, err := getMyCurrentDescriptionRow()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	editButton := widget.NewButtonWithIcon("Edit", theme.DocumentCreateIcon(), func(){
			setBuildProfilePage_EditDescription(window, profileType, currentPage, currentPage)
		})

	noResponseButton := widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){
			setBuildProfilePage_DeleteDescription(window, profileType, currentPage, currentPage)
		})

	buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, editButton, noResponseButton))
	
	page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), pageDescription, widget.NewSeparator(), currentDescriptionRow, widget.NewSeparator(), buttonsGrid)

	setPageContent(page, window)
}

func setBuildProfilePage_DeleteDescription(window fyne.Window, profileType string, previousPage func(), nextPage func()){

	title := getPageTitleCentered("Delete " + profileType + " Description")

	backButton := getBackButtonCentered(previousPage)

	description1 := getBoldLabelCentered("Delete " + profileType + " Description?")

	description2 := getLabelCentered("Confirm to delete your description?")

	confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
			myLocalProfiles.DeleteProfileData(profileType, "Description")
			nextPage()
		}))

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

	setPageContent(page, window)
}


func setBuildProfilePage_EditDescription(window fyne.Window, profileType string, previousPage func(), nextPage func()){

	title := getPageTitleCentered("Edit " + profileType + " Description")

	backButton := getBackButtonCentered(previousPage)

	// Returns byte length limit for description length
	getSizeLimit := func()int{
		if (profileType == "Moderator"){
			return 500
		}
		if (profileType == "Host"){
			return 300
		}
		return 3000
	}
	
	sizeLimit := getSizeLimit()

	descriptionEntry := widget.NewMultiLineEntry()
	descriptionEntry.Wrapping = 3

	//Outputs:
	//	-bool: Current description exists
	//	-string: Current description
	//	-error
	getCurrentDescription := func()(bool, string, error){	

		exists, currentDescription, err := myLocalProfiles.GetProfileData(profileType, "Description")
		if (err != nil) { return false, "", err }
		if (exists == false){
			return false, "", nil
		}

		isAllowed := allowedText.VerifyStringIsAllowed(currentDescription)
		if (isAllowed == false){
			return false, "", errors.New("My current " + profileType + "description is not allowed.")
		}

		if (len(currentDescription) > sizeLimit){
			return false, "", errors.New("My current " + profileType + " description is too long.")
		}

		return true, currentDescription, nil
	}

	currentDescriptionExists, currentDescription, err := getCurrentDescription()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (currentDescriptionExists == false){
		descriptionEntry.SetPlaceHolder("Enter a description...")
	} else {
		descriptionEntry.SetText(currentDescription)
	}

	submitButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Save"), theme.ConfirmIcon(), func(){

		newDescription := descriptionEntry.Text

		isAllowed := allowedText.VerifyStringIsAllowed(newDescription)
		if (isAllowed == false){
			title := translate("Invalid Description")
			dialogMessageA := getLabelCentered(translate("Your description contains an invalid character."))
			dialogMessageB := getLabelCentered(translate("Your description must be encoded in UTF-8."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		newDescriptionLength := len(newDescription)
		if (newDescriptionLength > sizeLimit){

			sizeLimitString := helpers.ConvertIntToStringWithCommas(sizeLimit)
			currentLengthString := helpers.ConvertIntToString(newDescriptionLength)

			title := translate("Invalid Description")
			dialogMessageA := getLabelCentered(translate("Description is too long."))
			dialogMessageB := getLabelCentered(translate("Description cannot be longer than " + sizeLimitString + " bytes."))
			dialogMessageC := getLabelCentered(translate("Your submission is " + currentLengthString + " bytes."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		if (newDescription == ""){
			myLocalProfiles.DeleteProfileData(profileType, "Description")
		} else {
			myLocalProfiles.SetProfileData(profileType, "Description", newDescription)
		}

		nextPage()
	}))

	emptyLabel := widget.NewLabel("")

	submitButtonWithSpacer := container.NewVBox(submitButton, emptyLabel)

	descriptionEntryBoxed := getWidgetBoxed(descriptionEntry)

	descriptionEntryWithSubmitButton := container.NewBorder(nil, submitButtonWithSpacer, nil, nil, descriptionEntryBoxed)

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

	page := container.NewBorder(header, nil, nil, nil, descriptionEntryWithSubmitButton)

	setPageContent(page, window)
}

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

	setLoadingScreen(window, "Build Mate Profile - General", "Loading...")

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

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Location"))

	description1 := getLabelCentered("Add your location to your profile.")
	description2 := getLabelCentered("You can add a primary and a secondary location.")

	//Outputs:
	//	-bool: Location exists
	//	-float64: Location latitude
	//	-float64: Location longitude
	//	-bool: Location country exists
	//	-int: Location country identifier
	//	-error
	getMyLocationInfo := func(rank string)(bool, float64, float64, bool, int, error){

		if (rank != "Primary" && rank != "Secondary"){
			return false, 0, 0, false, 0, errors.New("getMyLocationInfo called with invalid rank: " + rank)
		}

		exists, locationLatitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLatitude")
		if (err != nil) { return false, 0, 0, false, 0, err }
		if (exists == false){

			return false, 0, 0, false, 0, nil
			
		}
		exists, locationLongitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLongitude")
		if (err != nil) { return false, 0, 0, false, 0, err }
		if (exists == false){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains " + rank + "LocationLatitude but missing " + rank + "LocationLongitude")
		}

		locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitude)
		if (err != nil){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLatitude: " + locationLatitude)
		}

		locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitude)
		if (err != nil){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLongitude: " + locationLongitude)
		}

		exists, locationCountryIdentifier, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationCountry")
		if (err != nil) { return false, 0, 0, false, 0, err }
		if (exists == false){
			return true, locationLatitudeFloat64, locationLongitudeFloat64, false, 0, nil
		}

		locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
		if (err != nil){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationCountry: " + locationCountryIdentifier)
		}

		return true, locationLatitudeFloat64, locationLongitudeFloat64, true, locationCountryIdentifierInt, nil
	}

	primaryLocationExists, primaryLocationLatitude, primaryLocationLongitude, primaryLocationCountryExists, primaryLocationCountryIdentifier, err := getMyLocationInfo("Primary")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	secondaryLocationExists, secondaryLocationLatitude, secondaryLocationLongitude, secondaryLocationCountryExists, secondaryLocationCountryIdentifier, err := getMyLocationInfo("Secondary")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	if (primaryLocationExists == false && secondaryLocationExists == true){
		setErrorEncounteredPage(window, errors.New("Primary location does not exist, secondary location exists."), previousPage)
		return
	}

	addLocationButton := getWidgetCentered(widget.NewButtonWithIcon("Add Location", theme.ContentAddIcon(), func(){
			if (primaryLocationExists == true && secondaryLocationExists == true){
				title := translate("Cannot Add Location.")
				dialogMessageA := getLabelCentered(translate("You can only have 2 locations."))
				dialogMessageB := getLabelCentered(translate("Delete a location first."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			setBuildMateProfilePage_AddLocation_ChooseCountry(window, currentPage, currentPage)
		}))

	if (primaryLocationExists == false){

		noLocationsExistLabel := getBoldLabelCentered("No Locations Exist.")

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

		setPageContent(page, window)
		return
	}

	visibilityLabel := getBoldLabel("Visibility:")

	visibilityOptions := []string{translate("Show On Profile"), translate("Hide From Profile")}
	visibilitySelector := widget.NewSelect(visibilityOptions, func(newVisibility string){

			getNewVisibilityStatus := func()string{

				if (newVisibility == translate("Show On Profile")){
					return "Yes"				
				}

				return "No"
			}

			newVisibilityStatus := getNewVisibilityStatus()

			err := myLocalProfiles.SetProfileData("Mate", "VisibilityStatus_Location", newVisibilityStatus)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
		})

	exists, visibilityStatus, err := myLocalProfiles.GetProfileData("Mate", "VisibilityStatus_Location")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (exists == true && visibilityStatus == "No"){
		visibilitySelector.Selected = translate("Hide From Profile")
	} else {
		visibilitySelector.Selected = translate("Show On Profile")
	}

	profileVisibilityHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
			setMateProfileAttributeVisibilityExplainerPage(window, currentPage)
		})

	visibilitySelectorRow := container.NewHBox(layout.NewSpacer(), visibilityLabel, visibilitySelector, profileVisibilityHelpButton, layout.NewSpacer())

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

		rankLabel := getItalicLabelCentered("Rank")

		countryLabel := getItalicLabelCentered("Country")

		locationLabel := getItalicLabelCentered("Location")

		emptyLabel := widget.NewLabel("")

		rankColumn := container.NewVBox(rankLabel, widget.NewSeparator())
		countryColumn := container.NewVBox(countryLabel, widget.NewSeparator())	
		locationColumn := container.NewVBox(locationLabel, widget.NewSeparator())
		manageButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())

		addLocationRow := func(locationRank string, locationLatitude float64, locationLongitude float64, locationCountryExists bool, locationCountryIdentifier int)error{

			getCountryText := func()(string, error){
				if (locationCountryExists == false){
					noneText := translate("None")
					return noneText, nil
				}

				locationObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(locationCountryIdentifier)
				if (err != nil) { return "", err }

				locationNamesList := locationObject.NamesList

				locationDescription := helpers.TranslateAndJoinStringListItems(locationNamesList, "/")

				locationCountryTranslatedTrimmed, _, err := helpers.TrimAndFlattenString(locationDescription, 20)
				if (err != nil) { return "", err }

				return locationCountryTranslatedTrimmed, nil
			}

			countryText, err := getCountryText()
			if (err != nil) { return err }

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

				locationCityFound, locationCity, locationState, _, err := worldLocations.GetCityFromCoordinates(locationLatitude, locationLongitude)
				if (err != nil) { return "", err }
				if (locationCityFound == false){

					locationLatitudeString := helpers.ConvertFloat64ToStringRounded(locationLatitude, 5)
					locationLongitudeString := helpers.ConvertFloat64ToStringRounded(locationLongitude, 5)

					formattedCoordinates := locationLatitudeString + "°, " + locationLongitudeString + "°"

					return formattedCoordinates, nil
				}

				locationCityFormatted := locationCity + ", " + locationState
 
				locationCityTrimmed, _, err := helpers.TrimAndFlattenString(locationCityFormatted, 25)
				if (err != nil) { return "", err }

				return locationCityTrimmed, nil
			}

			locationText, err := getLocationText()
			if (err != nil) { return err }

			locationRankLabel := getBoldLabelCentered(locationRank)
			locationCountryLabel := getBoldLabelCentered(countryText)
			locationTextLabel := getBoldLabelCentered(locationText)

			manageLocationButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
					setBuildMateProfilePage_ManageLocation(window, locationRank, currentPage, currentPage)
				})

			rankColumn.Add(locationRankLabel)
			countryColumn.Add(locationCountryLabel)
			locationColumn.Add(locationTextLabel)
			manageButtonsColumn.Add(manageLocationButton)

			rankColumn.Add(widget.NewSeparator())
			countryColumn.Add(widget.NewSeparator())
			locationColumn.Add(widget.NewSeparator())
			manageButtonsColumn.Add(widget.NewSeparator())

			return nil
		}

		err = addLocationRow("Primary", primaryLocationLatitude, primaryLocationLongitude, primaryLocationCountryExists, primaryLocationCountryIdentifier)
		if (err != nil) { return nil, err }

		if (secondaryLocationExists == true){
			err := addLocationRow("Secondary", secondaryLocationLatitude, secondaryLocationLongitude, secondaryLocationCountryExists, secondaryLocationCountryIdentifier)
			if (err != nil) { return nil, err }
		}

		locationsGrid := container.NewHBox(layout.NewSpacer(), rankColumn, countryColumn, locationColumn, manageButtonsColumn, layout.NewSpacer())

		if (secondaryLocationExists == false){

			locationGridWithAddLocationButton := container.NewVBox(locationsGrid, addLocationButton)	

			return locationGridWithAddLocationButton, nil
		}

		swapLocationsFunction := func()error{

			primaryLocationLatitudeString := helpers.ConvertFloat64ToString(primaryLocationLatitude)
			primaryLocationLongitudeString := helpers.ConvertFloat64ToString(primaryLocationLongitude)

			secondaryLocationLatitudeString := helpers.ConvertFloat64ToString(secondaryLocationLatitude)
			secondaryLocationLongitudeString := helpers.ConvertFloat64ToString(secondaryLocationLongitude)

			err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLatitude", secondaryLocationLatitudeString)
			if (err != nil) { return err }

			err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLongitude", secondaryLocationLongitudeString)
			if (err != nil) { return err }

			if (secondaryLocationCountryExists == true){

				secondaryLocationCountryIdentifierString := helpers.ConvertIntToString(secondaryLocationCountryIdentifier)

				err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationCountry", secondaryLocationCountryIdentifierString)
				if (err != nil) { return err }
			} else {
				err := myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationCountry")
				if (err != nil) { return err }
			}

			err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLatitude", primaryLocationLatitudeString)
			if (err != nil) { return err }

			err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLongitude", primaryLocationLongitudeString)
			if (err != nil) { return err }

			if (primaryLocationCountryExists == true){

				primaryLocationCountryIdentifierString := helpers.ConvertIntToString(primaryLocationCountryIdentifier)

				err := myLocalProfiles.SetProfileData("Mate", "SecondaryLocationCountry", primaryLocationCountryIdentifierString)
				if (err != nil) { return err }
			} else {
				err := myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationCountry")
				if (err != nil) { return err }
			}

			return nil
		}

		swapLocationsButton := widget.NewButtonWithIcon("Swap Locations", theme.ContentRedoIcon(), func(){
				err := swapLocationsFunction()
				if (err != nil){
					setErrorEncounteredPage(window, err, currentPage)
					return
				}
				currentPage()
			})

		buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, swapLocationsButton, addLocationButton))

		locationGridWithButtons := container.NewVBox(locationsGrid, buttonsGrid)

		return locationGridWithButtons, nil
	}

	locationsGrid, err := getLocationsGrid()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	setPageContent(page, window)
}

func setBuildMateProfilePage_ManageLocation(window fyne.Window, locationRank string, previousPage func(), afterDeletePage func()){

	if (locationRank != "Primary" && locationRank != "Secondary"){
		setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_ManageLocation called with invalid locationRank: " + locationRank), previousPage)
		return
	}

	setLoadingScreen(window, "Build Mate Profile - General", "Loading...")

	currentPage := func(){setBuildMateProfilePage_ManageLocation(window, locationRank, previousPage, afterDeletePage)}

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Manage Location"))

	description1 := getLabelCentered("This is your " + locationRank + " location.")

	//Outputs:
	//	-bool: Location exists
	//	-float64: Location latitude
	//	-float64: Location longitude
	//	-bool: Location country exists
	//	-int: Location country identifier
	//	-error
	getMyLocationInfo := func(rank string)(bool, float64, float64, bool, int, error){

		if (rank != "Primary" && rank != "Secondary"){
			return false, 0, 0, false, 0, errors.New("getMyLocationInfo called with invalid rank: " + rank)
		}

		exists, locationLatitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLatitude")
		if (err != nil) { return false, 0, 0, false, 0, err }
		if (exists == false){

			return false, 0, 0, false, 0, nil
		}

		exists, locationLongitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLongitude")
		if (err != nil) { return false, 0, 0, false, 0, err }
		if (exists == false){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains " + rank + "LocationLatitude but missing " + rank + "LocationLongitude")
		}

		locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitude)
		if (err != nil){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLatitude: " + locationLatitude)
		}

		locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitude)
		if (err != nil){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLongitude: " + locationLongitude)
		}

		exists, locationCountryIdentifier, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationCountry")
		if (err != nil) { return false, 0, 0, false, 0, err }
		if (exists == false){
			return true, locationLatitudeFloat64, locationLongitudeFloat64, false, 0, nil
		}

		locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
		if (err != nil){
			return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid locationCountryIdentifier: " + locationCountryIdentifier)
		}

		return true, locationLatitudeFloat64, locationLongitudeFloat64, true, locationCountryIdentifierInt, nil
	}

	locationExists, locationLatitude, locationLongitude, locationCountryExists, locationCountryIdentifier, err := getMyLocationInfo(locationRank)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (locationExists == false){
		setErrorEncounteredPage(window, errors.New("setBuildMateProfilePage_ManageLocation called with missing location"), previousPage)
		return
	}

	getCountryText := func()(string, error){
		if (locationCountryExists == false){
			noneText := translate("None")
			return noneText, nil
		}

		locationObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(locationCountryIdentifier)
		if (err != nil) { return "", err }

		locationNamesList := locationObject.NamesList

		locationDescription := helpers.TranslateAndJoinStringListItems(locationNamesList, "/")

		return locationDescription, nil
	}

	countryText, err := getCountryText()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return	
	}

	countryLabel := getLabelCentered("Country:")
	locationCountryLabel := getBoldLabel(countryText)

	coordinatesLabel := getLabelCentered("Coordinates:")

	locationLatitudeString := helpers.ConvertFloat64ToString(locationLatitude)
	locationLongitudeString := helpers.ConvertFloat64ToString(locationLongitude)	

	locationCoordinatesLabel := getBoldLabel(locationLatitudeString + "°, " + locationLongitudeString + "°")

	cityLabel := getLabelCentered("City:")

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

		// We will either find the exact city, or find the closest city

		cityName, cityState, cityCountryIdentifier, cityDistanceKilometers, err := worldLocations.GetClosestCityFromCoordinates(locationLatitude, locationLongitude)
		if (err != nil) { return nil, err }

		if (cityDistanceKilometers == 0){

			locationCityFormatted := cityName + ", " + cityState

			locationCityLabel := getBoldLabelCentered(locationCityFormatted)

			return locationCityLabel, nil
		}

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

			currentUnitsExist, currentUnits, err := globalSettings.GetSetting("MetricOrImperial")
			if (err != nil){ return "", err }

			if (currentUnitsExist == true && currentUnits == "Imperial"){

				distanceMiles, err := helpers.ConvertKilometersToMiles(cityDistanceKilometers)
				if (err != nil){ return "", err }

				distanceMilesString := helpers.ConvertFloat64ToStringRounded(distanceMiles, 1)
				result := distanceMilesString + " miles"
				return result, nil
			}

			distanceKilometersString := helpers.ConvertFloat64ToStringRounded(cityDistanceKilometers, 1)

			result := distanceKilometersString + " kilometers"

			return result, nil
		}

		nearbyCityDistanceFormattedString, err := getNearbyCityDistanceFormattedString()
		if (err != nil){ return nil, err }

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

			if (locationCountryExists == true && cityCountryIdentifier == locationCountryIdentifier){
				result := cityName + ", " + cityState
				return result, nil
			}

			locationObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(cityCountryIdentifier)
			if (err != nil) { return "", err }

			locationPrimaryName := locationObject.NamesList[0]

			locationPrimaryNameTranslated := translate(locationPrimaryName)

			result := cityName + ", " + cityState + ", " + locationPrimaryNameTranslated
			return result, nil
		}
	
		nearbyCityNameFormatted, err := getNearbyCityNameFormatted()
		if (err != nil) { return nil, err }

		nearbyCityNameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nearbyCityNameFormatted, 40)
		if (err != nil) { return nil, err }

		locationDistanceLabel := getBoldLabelCentered(nearbyCityDistanceFormattedString + " from")

		locationCityNameLabel := getBoldLabelCentered(nearbyCityNameFormattedAndTrimmed)
	
		cityInfoContainer := container.NewVBox(locationDistanceLabel, locationCityNameLabel)		

		return cityInfoContainer, nil
	}

	cityContent, err := getCityContent()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return	
	}

	deleteFunction := func()error{

		if (locationRank == "Secondary"){

			err := myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLatitude")
			if (err != nil) { return err }

			err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLongitude")
			if (err != nil) { return err }

			err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationCountry")
			if (err != nil) { return err }

			return nil
		}

		// We want to delete the primary location
		// If there is a secondary location, we set it as the primary after deleting the primary	

		primaryLocationExists, _, _, _, _, err := getMyLocationInfo("Primary")
		if (err != nil){ return err }
		if (primaryLocationExists == false){
			return errors.New("Trying to delete the primary location, but the location does not exist.")
		}

		err = myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationLatitude")
		if (err != nil) { return err }

		err = myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationLongitude")
		if (err != nil) { return err }

		err = myLocalProfiles.DeleteProfileData("Mate", "PrimaryLocationCountry")
		if (err != nil) { return err }	

		secondaryLocationExists, secondaryLocationLatitude, secondaryLocationLongitude, secondaryLocationCountryExists, secondaryLocationCountryIdentifier, err := getMyLocationInfo("Secondary")
		if (err != nil){ return err }
		if (secondaryLocationExists == false){

			return nil
		}

		err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLatitude")
		if (err != nil) { return err }

		err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationLongitude")
		if (err != nil) { return err }

		err = myLocalProfiles.DeleteProfileData("Mate", "SecondaryLocationCountry")
		if (err != nil) { return err }

		secondaryLocationLatitudeString := helpers.ConvertFloat64ToString(secondaryLocationLatitude)
		secondaryLocationLongitudeString := helpers.ConvertFloat64ToString(secondaryLocationLongitude)

		err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLatitude", secondaryLocationLatitudeString)
		if (err != nil) { return err }

		err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLongitude", secondaryLocationLongitudeString)
		if (err != nil) { return err }

		if (secondaryLocationCountryExists == true){

			secondaryLocationCountryIdentifierString := helpers.ConvertIntToString(secondaryLocationCountryIdentifier)

			err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationCountry", secondaryLocationCountryIdentifierString)
			if (err != nil) { return err }
		}

		return nil
	}

	deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
		err := deleteFunction()
		if (err != nil){
			setErrorEncounteredPage(window, err, currentPage)
			return
		}
		afterDeletePage()
	}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, widget.NewSeparator(), countryLabel, locationCountryLabel, widget.NewSeparator(), coordinatesLabel, locationCoordinatesLabel, widget.NewSeparator(), cityLabel, cityContent, widget.NewSeparator(), deleteButton)

	setPageContent(page, window)
}

func setBuildMateProfilePage_AddLocation_ChooseCountry(window fyne.Window, previousPage func(), visitOnCompletePage func()){

	currentPage := func(){setBuildMateProfilePage_AddLocation_ChooseCountry(window, previousPage, visitOnCompletePage)}

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Add Location"))

	description1 := getLabelCentered("Choose the country of the location to add.")
	description2 := getLabelCentered("If your country is not listed, select Country Is Missing")

	allCountryObjectsList, err := worldLocations.GetAllCountryObjectsList()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	allCountryDescriptionsList := make([]string, 0, len(allCountryObjectsList))

	// Map Structure: Country Description -> Country identifier
	countryIdentifiersMap := make(map[string]int)

	for _, countryObject := range allCountryObjectsList{

		countryIdentifier := countryObject.Identifier
		countryNamesList := countryObject.NamesList

		countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")

		countryIdentifiersMap[countryDescription] = countryIdentifier
		allCountryDescriptionsList = append(allCountryDescriptionsList, countryDescription)
	}

	helpers.SortStringListToUnicodeOrder(allCountryDescriptionsList)

	onSelectedFunction := func(selectedCountryIndex int) {

		selectedCountryDescription := allCountryDescriptionsList[selectedCountryIndex]

		selectedCountryIdentifier, exists := countryIdentifiersMap[selectedCountryDescription]
		if (exists == false){
			setErrorEncounteredPage(window, errors.New("countryIdentifiersMap missing country description: " + selectedCountryDescription), currentPage)
			return
		}

		setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, false, "", currentPage, visitOnCompletePage)
	}

	widgetList, err := getFyneWidgetListFromStringList(allCountryDescriptionsList, onSelectedFunction)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	countryIsMissingButton := getWidgetCentered(widget.NewButton("Country Is Missing", func(){
			setBuildMateProfilePage_AddLocation_CustomCoordinates(window, false, 0, currentPage, visitOnCompletePage)
		}))

	page := container.NewBorder(header, countryIsMissingButton, nil, nil, widgetList)

	setPageContent(page, window)
}

func setBuildMateProfilePage_AddLocation_ChooseCity(window fyne.Window, selectedCountryIdentifier int, searchTermExists bool, searchTerm string, previousPage func(), visitOnCompletePage func()){

	if (searchTermExists == true){
		setLoadingScreen(window, "Build Mate Profile - General", "Loading Search Results...")
	} else {
		setLoadingScreen(window, "Build Mate Profile - General", "Loading...")
	}

	currentPage := func(){setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, searchTermExists, searchTerm, previousPage, visitOnCompletePage)}

	allCountryCityObjectsList, err := worldLocations.GetAllCityObjectsInCountry(selectedCountryIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	if (len(allCountryCityObjectsList) == 0){
		// This country has no cities in the database
		// We go directly to the custom location screen
		setBuildMateProfilePage_AddLocation_CustomCoordinates(window, true, selectedCountryIdentifier, previousPage, visitOnCompletePage)
		return
	}

	countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(selectedCountryIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	countryNamesList := countryObject.NamesList

	countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered("Choose Location")

	description1 := getLabelCentered("You must now enter a specific location in " + countryDescription + ".")
	description2 := getLabelCentered("You can search for and choose a city from below.")
	description3 := getLabelCentered("If your city is not listed, or to add a custom location, select Add Custom")

	addCustomLocationButton := getWidgetCentered(widget.NewButtonWithIcon("Add Custom", theme.ContentAddIcon(), func(){
			setBuildMateProfilePage_AddLocation_CustomCoordinates(window, true, selectedCountryIdentifier, currentPage, visitOnCompletePage)	
		}))

	enterSearchTermLabel := getBoldLabelCentered("Enter Search Term:")
	enterSearchTermEntry := widget.NewEntry()
	if (searchTermExists == true){
		enterSearchTermEntry.SetText(searchTerm)
	} else {
		enterSearchTermEntry.SetPlaceHolder("Enter search term...")
	}

	searchButton := widget.NewButtonWithIcon("Search", theme.SearchIcon(), func(){

			newSearchTerm := enterSearchTermEntry.Text
			if (newSearchTerm == searchTerm){
				return
			}

			if (newSearchTerm == ""){
				setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, false, "", previousPage, visitOnCompletePage)
				return
			}

			setBuildMateProfilePage_AddLocation_ChooseCity(window, selectedCountryIdentifier, true, newSearchTerm, previousPage, visitOnCompletePage)
		})

	enterSearchTermRow := getContainerCentered(container.NewGridWithRows(1, enterSearchTermLabel, enterSearchTermEntry, searchButton))

	header := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), addCustomLocationButton, widget.NewSeparator(), enterSearchTermRow, widget.NewSeparator())

	getCityObjectsToDisplayList := func()[]worldLocations.CityObject{

		if (searchTermExists == false){

			return allCountryCityObjectsList
		}

		allCityObjectsWithSearchTermList := make([]worldLocations.CityObject, 0)

		searchTermLowercase := strings.ToLower(searchTerm)

		for _, cityObject := range allCountryCityObjectsList{

			cityName := cityObject.Name
			cityNameLowercase := strings.ToLower(cityName)

			containsAny := strings.Contains(cityNameLowercase, searchTermLowercase)	
			if (containsAny == true){
				allCityObjectsWithSearchTermList = append(allCityObjectsWithSearchTermList, cityObject)
			}
		}

		return allCityObjectsWithSearchTermList
	}

	cityObjectsToDisplayList := getCityObjectsToDisplayList()

	if (len(cityObjectsToDisplayList) == 0){

		noCitiesFoundLabel := getBoldLabelCentered("No cities found.")

		page := container.NewVBox(header, noCitiesFoundLabel)

		setPageContent(page, window)
		return	
	}

	cityNamesFormattedList := make([]string, 0, len(cityObjectsToDisplayList))

	for _, cityObject := range cityObjectsToDisplayList{

		stateName := cityObject.State
		cityName := cityObject.Name

		nameFormatted := cityName + ", " + stateName

		nameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nameFormatted, 50)
		if (err != nil) {
			setErrorEncounteredPage(window, err, previousPage)
			return
		}

		cityNamesFormattedList = append(cityNamesFormattedList, nameFormattedAndTrimmed)
	}

	onClickedFunction := func(cityIndex int){

		selectedCityObject := cityObjectsToDisplayList[cityIndex]

		selectedCityLatitude := selectedCityObject.Latitude
		selectedCityLongitude := selectedCityObject.Longitude

		setBuildMateProfilePage_ConfirmAddLocation(window, true, selectedCountryIdentifier, selectedCityLatitude, selectedCityLongitude, currentPage, visitOnCompletePage)
	}

	citySearchResultsWidgetList, err := getFyneWidgetListFromStringList(cityNamesFormattedList, onClickedFunction)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	page := container.NewBorder(header, nil, nil, nil, citySearchResultsWidgetList)

	setPageContent(page, window)
	return
}

func setBuildMateProfilePage_AddLocation_CustomCoordinates(window fyne.Window, countryExists bool, countryIdentifier int, previousPage func(), visitOnCompletePage func()){

	currentPage := func(){setBuildMateProfilePage_AddLocation_CustomCoordinates(window, countryExists, countryIdentifier, previousPage, visitOnCompletePage)}

	title := getPageTitleCentered(translate("Build Mate Profile - General"))
	
	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Add Custom Location"))

	getDescription1Text := func()(string, error){
		if (countryExists == false){
			result := translate("Enter a location.")
			return result, nil
		}

		countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(countryIdentifier)
		if (err != nil){ return "", err }

		countryNamesList := countryObject.NamesList
	
		countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")

		result := translate("Enter a location in ") + countryDescription

		return result, nil
	}

	description1Text, err := getDescription1Text()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	description1 := getLabelCentered(description1Text)
	description2 := getBoldLabelCentered("We advise against entering your exact location.")
	description3 := getLabelCentered("Choose a location that is not a house.")
	description4 := getLabelCentered("Use an online map explorer to find the coordinates.")
	description5 := getItalicLabelCentered("Latitude Examples: N 31.7619°, S -40.7128°, -65.358579")
	description6 := getItalicLabelCentered("Longitude Examples: E 74.0060°, W -106.4850°, -14.1592")

	enterLatitudeLabel := getBoldLabelCentered("Enter Latitude:")
	enterLongitudeLabel := getBoldLabelCentered("Enter Longitude:")

	latitudeEntry := widget.NewEntry()
	longitudeEntry := widget.NewEntry()

	latitudeEntry.SetPlaceHolder("Enter Latitude...")
	longitudeEntry.SetPlaceHolder("Enter Longitude...")

	entryGrid := getContainerCentered(container.NewGridWithColumns(2, enterLatitudeLabel, latitudeEntry, enterLongitudeLabel, longitudeEntry))

	confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){

			latitudeEntered := latitudeEntry.Text
			longitudeEntered := longitudeEntry.Text

			latitudeFormattedA := strings.TrimRight(latitudeEntered, "°NS")
			latitudeFormattedB := strings.TrimLeft(latitudeFormattedA, "NS")
			latitudeFormattedC := strings.TrimSpace(latitudeFormattedB)

			longitudeFormattedA := strings.TrimRight(longitudeEntered, "°EW")
			longitudeFormattedB := strings.TrimLeft(longitudeFormattedA, "EW")
			longitudeFormattedC := strings.TrimSpace(longitudeFormattedB)

			latitudeFloat64, err := helpers.ConvertStringToFloat64(latitudeFormattedC)
			if (err != nil){
				title := translate("Invalid Latitude.")
				dialogMessage := getLabelCentered(translate("Latitude is not a number."))
				dialogContent := container.NewVBox(dialogMessage)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			longitudeFloat64, err := helpers.ConvertStringToFloat64(longitudeFormattedC)
			if (err != nil){
				title := translate("Invalid Longitude.")
				dialogMessage := getLabelCentered(translate("Longitude is not a number."))
				dialogContent := container.NewVBox(dialogMessage)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			latitudeIsValid := helpers.VerifyLatitude(latitudeFloat64)
			if (latitudeIsValid == false){
				title := translate("Invalid Latitude.")
				dialogMessage := getLabelCentered(translate("Latitude should be a number between -90 and 90"))
				dialogContent := container.NewVBox(dialogMessage)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			longitudeIsValid :=  helpers.VerifyLongitude(longitudeFloat64)
			if (longitudeIsValid == false){
				title := translate("Invalid Longitude.")
				dialogMessage := getLabelCentered(translate("Longitude should be a number between -180 and 180"))
				dialogContent := container.NewVBox(dialogMessage)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			setBuildMateProfilePage_ConfirmAddLocation(window, countryExists, countryIdentifier, latitudeFloat64, longitudeFloat64, currentPage, visitOnCompletePage)
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, description3, description4, widget.NewSeparator(), description5, description6, widget.NewSeparator(), entryGrid, confirmButton)

	setPageContent(page, window)
}

func setBuildMateProfilePage_ConfirmAddLocation(window fyne.Window, newLocationCountryExists bool, newLocationCountryIdentifier int, newLocationLatitude float64, newLocationLongitude float64, previousPage func(), visitOnCompletePage func()){

	currentPage := func(){setBuildMateProfilePage_ConfirmAddLocation(window, newLocationCountryExists, newLocationCountryIdentifier, newLocationLatitude, newLocationLongitude, previousPage, visitOnCompletePage)}

	setLoadingScreen(window, "Build Mate Profile - General", "Loading...")

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Add Location"))

	description1 := getBoldLabelCentered("Confirm Add Location?")
	description2 := getLabelCentered("This location will be publicly displayed on your profile.")

	countryLabel := getLabelCentered("Country:")

	getLocationCountryLabelText := func()(string, error){
		if (newLocationCountryExists == false){
			result := translate("None")
			return result, nil
		}

		countryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(newLocationCountryIdentifier)
		if (err != nil){ return "", err }

		countryNamesList := countryObject.NamesList

		countryDescription := helpers.TranslateAndJoinStringListItems(countryNamesList, "/")

		result := translate(countryDescription)
		return result, nil
	}

	locationCountryLabelText, err := getLocationCountryLabelText()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return	
	}

	locationCountryLabel := getBoldLabel(locationCountryLabelText)

	coordinatesLabel := getLabelCentered("Coordinates:")

	locationLatitudeString := helpers.ConvertFloat64ToString(newLocationLatitude)
	locationLongitudeString := helpers.ConvertFloat64ToString(newLocationLongitude)	

	locationCoordinatesLabel := getBoldLabel(locationLatitudeString + "°, " + locationLongitudeString + "°")

	cityLabel := getLabelCentered("City:")

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

		// We will either find the exact city, or find the closest city

		cityName, cityState, cityCountryIdentifier, cityDistanceKilometers, err := worldLocations.GetClosestCityFromCoordinates(newLocationLatitude, newLocationLongitude)
		if (err != nil) { return nil, err }

		if (cityDistanceKilometers == 0){

			locationCityFormatted := cityName + ", " + cityState

			locationCityLabel := getBoldLabelCentered(locationCityFormatted)

			return locationCityLabel, nil
		}

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

			currentUnitsExist, currentUnits, err := globalSettings.GetSetting("MetricOrImperial")
			if (err != nil){ return "", err }

			if (currentUnitsExist == true && currentUnits == "Imperial"){

				distanceMiles, err := helpers.ConvertKilometersToMiles(cityDistanceKilometers)
				if (err != nil) { return "", err }

				distanceMilesString := helpers.ConvertFloat64ToStringRounded(distanceMiles, 1)

				result := distanceMilesString + " miles"

				return result, nil
			}

			distanceKilometersString := helpers.ConvertFloat64ToStringRounded(cityDistanceKilometers, 1)

			result := distanceKilometersString + " kilometers"
			
			return result, nil
		}

		nearbyCityDistanceFormattedString, err := getNearbyCityDistanceFormattedString()
		if (err != nil) { return nil, err }

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

			if (cityCountryIdentifier == newLocationCountryIdentifier){
				result := cityName + ", " + cityState
				return result, nil
			}

			cityCountryObject, err := worldLocations.GetCountryObjectFromCountryIdentifier(cityCountryIdentifier)
			if (err != nil){ return "", err }

			cityCountryNamesList := cityCountryObject.NamesList
			
			countryPrimaryName := cityCountryNamesList[0]

			countryNameTranslated := translate(countryPrimaryName)

			result := cityName + ", " + cityState + ", " + countryNameTranslated

			return result, nil
		}
	
		nearbyCityNameFormatted, err := getNearbyCityNameFormatted()
		if (err != nil) { return nil, err }

		nearbyCityNameFormattedAndTrimmed, _, err := helpers.TrimAndFlattenString(nearbyCityNameFormatted, 40)
		if (err != nil) { return nil, err }

		locationDistanceLabel := getBoldLabelCentered(nearbyCityDistanceFormattedString + " from")

		locationCityNameLabel := getBoldLabelCentered(nearbyCityNameFormattedAndTrimmed)
	
		cityInfoContainer := container.NewVBox(locationDistanceLabel, locationCityNameLabel)		

		return cityInfoContainer, nil
	}

	cityContent, err := getCityContent()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return	
	}

	addLocationFunction := func()error{

		//Outputs:
		//	-bool: Location exists
		//	-float64: Location latitude
		//	-float64: Location longitude
		//	-bool: Location country exists
		//	-int: Location country identifier
		//	-error
		getMyLocationInfo := func(rank string)(bool, float64, float64, bool, int, error){

			if (rank != "Primary" && rank != "Secondary"){
				return false, 0, 0, false, 0, errors.New("getMyLocationInfo called with invalid rank: " + rank)
			}

			exists, locationLatitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLatitude")
			if (err != nil) { return false, 0, 0, false, 0, err }
			if (exists == false){

				return false, 0, 0, false, 0, nil
			}

			exists, locationLongitude, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationLongitude")
			if (err != nil) { return false, 0, 0, false, 0, err }
			if (exists == false){
				return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains " + rank + "LocationLatitude but missing " + rank + "LocationLongitude")
			}

			locationLatitudeFloat64, err := helpers.ConvertStringToFloat64(locationLatitude)
			if (err != nil){
				return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLatitude")
			}

			locationLongitudeFloat64, err := helpers.ConvertStringToFloat64(locationLongitude)
			if (err != nil){
				return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationLongitude")
			}

			exists, locationCountryIdentifier, err := myLocalProfiles.GetProfileData("Mate", rank + "LocationCountry")
			if (err != nil) { return false, 0, 0, false, 0, err }
			if (exists == false){
				return true, locationLatitudeFloat64, locationLongitudeFloat64, false, 0, nil
			}

			locationCountryIdentifierInt, err := helpers.ConvertStringToInt(locationCountryIdentifier)
			if (err != nil){
				return false, 0, 0, false, 0, errors.New("MyLocalProfiles contains invalid " + rank + "LocationCountry: " + locationCountryIdentifier)
			}

			return true, locationLatitudeFloat64, locationLongitudeFloat64, true, locationCountryIdentifierInt, nil
		}

		primaryLocationExists, primaryLocationLatitude, primaryLocationLongitude, primaryLocationCountryExists, primaryLocationCountryIdentifier, err := getMyLocationInfo("Primary")
		if (err != nil){ return err }

		secondaryLocationExists, _, _, _, _, err := getMyLocationInfo("Secondary")
		if (err != nil){ return err }

		if (primaryLocationExists == false && secondaryLocationExists == true){
			return errors.New("My Profile has a secondary location, but not a primary location.")
		}
		if (primaryLocationExists == true && secondaryLocationExists == true){
			return errors.New("Trying to add a location to a profile with 2 locations.")
		}

		if (primaryLocationExists == false){

			err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLatitude", locationLatitudeString)
			if (err != nil) { return err }

			err = myLocalProfiles.SetProfileData("Mate", "PrimaryLocationLongitude", locationLongitudeString)
			if (err != nil) { return err }

			if (newLocationCountryExists == true){
	
				newLocationCountryIdentifierString := helpers.ConvertIntToString(newLocationCountryIdentifier)
	
				err := myLocalProfiles.SetProfileData("Mate", "PrimaryLocationCountry", newLocationCountryIdentifierString)
				if (err != nil) { return err }
			}		

			return nil
		}

		// Primary location exists
		// We see if the location already exists

		if (primaryLocationLatitude == newLocationLatitude && primaryLocationLongitude == newLocationLongitude){
			if (newLocationCountryExists == false && primaryLocationCountryExists == false){
				// Location is identical, Nothing left to do.
				return nil
			}
			if (newLocationCountryExists == true && primaryLocationCountryExists == true && primaryLocationCountryIdentifier == newLocationCountryIdentifier){
				// Location is identical, Nothing left to do.
				return nil
			}
		}

		err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLatitude", locationLatitudeString)
		if (err != nil) { return err }

		err = myLocalProfiles.SetProfileData("Mate", "SecondaryLocationLongitude", locationLongitudeString)
		if (err != nil) { return err }

		if (newLocationCountryExists == true){

			newLocationCountryIdentifierString := helpers.ConvertIntToString(newLocationCountryIdentifier)

			err := myLocalProfiles.SetProfileData("Mate", "SecondaryLocationCountry", newLocationCountryIdentifierString)
			if (err != nil) { return err }
		}		

		return nil
	}

	confirmButton := getWidgetCentered(widget.NewButtonWithIcon("Confirm", theme.ConfirmIcon(), func(){

			err := addLocationFunction()
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			visitOnCompletePage()
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), countryLabel, locationCountryLabel, widget.NewSeparator(), coordinatesLabel, locationCoordinatesLabel, widget.NewSeparator(), cityLabel, cityContent, widget.NewSeparator(), confirmButton)
	
	setPageContent(page, window)
}


func setBuildMateProfilePage_Photos(window fyne.Window, currentPhotoIndex int, previousPage func()){

	currentPage := func(){setBuildMateProfilePage_Photos(window, currentPhotoIndex, previousPage)}

	pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getBoldLabelCentered(translate("Photos"))

	description1 := getLabelCentered(translate("Add photos to your Mate profile."))
	description2 := getLabelCentered(translate("You can add 5 photos."))

	addImageFileCallbackFunction := func(fileObject fyne.URIReadCloser, err error){

		if (err != nil) {
			title := translate("Failed to open image file.")
			dialogMessage := getLabelCentered(translate("Report this error to Seekia developers: " + err.Error()))
			dialogContent := container.NewVBox(dialogMessage)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		if (fileObject == nil) {
			return
		}

		setLoadingScreen(window, "Add Image", "Importing image...")

		filePath := fileObject.URI().String()
		filePath = strings.TrimPrefix(filePath, "file://")

		fileExists, ableToReadImage, imageObject, err := imagery.ReadImageFile(filePath)
		if (err != nil) {
			currentPage()
			title := translate("Failed to open image file.")
			dialogMessage := getLabelCentered(translate("Report this error to Seekia developers:") + " " + err.Error())
			dialogContent := container.NewVBox(dialogMessage)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}
		if (fileExists == false) {
			currentPage()
			title := translate("Failed to open image file.")
			dialogMessage := getLabelCentered(translate("Report this error to Seekia developers: Image file not found."))
			dialogContent := container.NewVBox(dialogMessage)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		if (ableToReadImage == false) {
			currentPage()
			title := translate("Failed to import image file.")
			dialogMessageA := getLabelCentered(translate("Seekia only supports these image file formats:"))
			dialogMessageB := getLabelCentered("JPG, JPEG, PNG, WEBP")
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		// We resize image to something that will be more managable when we edit it
		// When we export it, we will downsize it even further
		imageObjectResized, err := imagery.DownsizeGolangImage(imageObject, 1500)
		if (err != nil) {
			currentPage()
			title := translate("Failed To Process Image File.")
			dialogMessageA := getLabelCentered(translate("Your file may be too large."))

			errorString := err.Error()

			errorTrimmed, _, err := helpers.TrimAndFlattenString(errorString, 20)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			errorTrimmedLabel := getBoldLabel("Error: " + errorTrimmed)

			viewFullErrorButton := widget.NewButtonWithIcon("", theme.VisibilityIcon(), func(){
					setViewTextPage(window, "Viewing Error", errorString, false, currentPage)		
				})

			errorDescriptionRow := container.NewHBox(layout.NewSpacer(), errorTrimmedLabel, viewFullErrorButton, layout.NewSpacer())

			dialogContent := container.NewVBox(dialogMessageA, errorDescriptionRow)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		setSubmitImageToSubmitPageFunction := func(finalImage image.Image, prevPage func()){

			setLoadingScreen(window, "Add Image To Mate Profile", "Compressing Image...")

			newImageBase64String, err := imagery.ConvertImageObjectToStandardJPEGBase64String(finalImage)
			if (err != nil) {
				setErrorEncounteredPage(window, err, prevPage)
				return
			}

			setConfirmAddImageToMyMateProfilePage(window, newImageBase64String, prevPage, currentPage)
		}

		setEditImagePage(window, imageObjectResized, false, nil, imageObjectResized, currentPage, setSubmitImageToSubmitPageFunction)
	}

	//Outputs:
	//	-bool: Any images exist
	//	-[]string: Jpeg Base64 images list
	//	-error
	getCurrentImagesList := func()(bool, []string, error){

		exists, photosAttributeString, err := myLocalProfiles.GetProfileData("Mate", "Photos")
		if (err != nil){ return false, nil, err }
		if (exists == false){
			return false, nil, nil
		}

		jpegImagesList := strings.Split(photosAttributeString, "+")

		if (len(jpegImagesList) > 5){
			return false, nil, errors.New("My Photos attribute malformed: More than 5 photos")
		}

		return true, jpegImagesList, nil
	}

	anyImagesExist, myCurrentBase64JpegsList, err := getCurrentImagesList()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (anyImagesExist == false){

		noImagesExistLabel := getBoldLabelCentered("No Photos Exist.")

		addImageButton := getWidgetCentered(widget.NewButtonWithIcon("Add Image", theme.ContentAddIcon(), func(){			
				dialog.ShowFileOpen(addImageFileCallbackFunction, window)
			}))

		page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addImageButton, widget.NewSeparator(), noImagesExistLabel)

		setPageContent(page, window)
		return
	}

	numberOfImages := len(myCurrentBase64JpegsList)

	addImageButton := getWidgetCentered(widget.NewButtonWithIcon("Add Image", theme.ContentAddIcon(), func(){

		if (numberOfImages >= 5){
			title := translate("Image Limit Reached.")
			dialogMessageA := getLabelCentered(translate("Your profile can only contain 5 images."))
			dialogMessageB := getLabelCentered(translate("Delete existing images to add more."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(title, translate("Close"), dialogContent, window)
			return
		}

		dialog.ShowFileOpen(addImageFileCallbackFunction, window)
	}))

	myPhotosLabel := getBoldLabelCentered("My Photos:")
	
	// We use this function to fix an out-of-bound currentPhotoIndex
	getCurrentImageIndex := func()int{
		if (currentPhotoIndex < 0){
			return 0
		}

		finalIndex := len(myCurrentBase64JpegsList) - 1
		if (currentPhotoIndex > finalIndex){
			return finalIndex
		}
		return currentPhotoIndex
	}

	currentImageIndex := getCurrentImageIndex()

	selectButtonsRow := container.NewHBox(layout.NewSpacer())

	for imageIndex, _ := range myCurrentBase64JpegsList{

		imageIndexString := helpers.ConvertIntToString(imageIndex+1)

		selectButton := widget.NewButton(imageIndexString, func(){
				setBuildMateProfilePage_Photos(window, imageIndex, previousPage)
			})

		if (imageIndex == currentImageIndex){
			selectButton.Importance = widget.HighImportance
		}

		selectButtonsRow.Add(selectButton)
	}

	selectButtonsRow.Add(layout.NewSpacer())

	currentImageBase64 := myCurrentBase64JpegsList[currentImageIndex]

	imageObject, err := imagery.ConvertJPEGBase64StringToCroppedDownsizedImageObject(currentImageBase64)
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	fyneImageObject := canvas.NewImageFromImage(imageObject)
	fyneImageObject.FillMode = canvas.ImageFillContain
	fyneImageSize := getCustomFyneSize(50)
	fyneImageObject.SetMinSize(fyneImageSize)

	getBackButton := func()fyne.Widget{
		if (currentImageIndex <= 0) {
			button := widget.NewButton("", nil)
			return button
		}
		button := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){
				newImageIndex := currentImageIndex - 1
				setBuildMateProfilePage_Photos(window, newImageIndex, previousPage)
			})
		return button
	}
	getNextButton := func()fyne.Widget{
		if (currentImageIndex >= numberOfImages-1) {
			button := widget.NewButton("", nil)
			return button
		}
		button := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){
				newImageIndex := currentImageIndex + 1
				setBuildMateProfilePage_Photos(window, newImageIndex, previousPage)
			})
		return button
	}

	navigateBackButton := getBackButton()
	navigateNextButton := getNextButton()

	zoomButton := widget.NewButtonWithIcon("", theme.ZoomInIcon(), func(){
			setViewFullpageImagePage(window, imageObject, currentPage)
		})

	navAndZoomButtonsRow := getContainerCentered(container.NewGridWithRows(1, navigateBackButton, zoomButton, navigateNextButton))

	deleteButton := getWidgetCentered(widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){

		confirmDialogCallbackFunction := func(response bool){
			if (response == false){
				return
			}
			if (numberOfImages == 1){
				err := myLocalProfiles.DeleteProfileData("Mate", "Photos")
				if (err != nil){
					setErrorEncounteredPage(window, err, currentPage)
					return
				}
				currentPage()
				return
			}

			newList, err := helpers.DeleteIndexFromStringList(myCurrentBase64JpegsList, currentImageIndex)
			if (err != nil) {
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			newPhotosAttribute := strings.Join(newList, "+")

			err = myLocalProfiles.SetProfileData("Mate", "Photos", newPhotosAttribute)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			currentPage()
		}

		dialogTitle := translate("Confirm Delete Image?")
		dialogMessage := getLabelCentered("Confirm to delete this image?")
		dialogContent := container.NewVBox(dialogMessage)
		dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window)	
	}))

	page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, widget.NewSeparator(), addImageButton, widget.NewSeparator(), myPhotosLabel, selectButtonsRow, widget.NewSeparator(), fyneImageObject, widget.NewSeparator(), navAndZoomButtonsRow, deleteButton)

	setPageContent(page, window)
}

func setConfirmAddImageToMyMateProfilePage(window fyne.Window, newImageBase64String string, previousPage func(), nextPage func()){

	currentPage := func(){setConfirmAddImageToMyMateProfilePage(window, newImageBase64String, previousPage, nextPage)}

	title := getPageTitleCentered(translate("Add Image To Mate Profile"))

	backButton := getBackButtonCentered(previousPage)

	description1 := getBoldLabelCentered("Confirm add image to your Mate profile?")
	description2 := getLabelCentered("This image will be displayed on your profile.")
	description3 := getLabelCentered("Be aware that the image has been compressed.")

	submitButton := getWidgetCentered(widget.NewButtonWithIcon("Add Image", theme.ConfirmIcon(), func(){

		setLoadingScreen(window, "Add Image To Mate Profile", "Adding Image...")

		addImageFunction := func()error{

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

				exists, currentPhotosAttributeValue, err := myLocalProfiles.GetProfileData("Mate", "Photos")
				if (err != nil) { return "", err }
				if (exists == false){
					return newImageBase64String, nil
				}
				existingJpegPhotosList := strings.Split(currentPhotosAttributeValue, "+")

				newList := append(existingJpegPhotosList, newImageBase64String)

				newListJoined := strings.Join(newList, "+")

				return newListJoined, nil
			}

			newAttributeValue, err := getNewAttributeValue()
			if (err != nil){ return err }

			err = myLocalProfiles.SetProfileData("Mate", "Photos", newAttributeValue)
			if (err != nil){ return err }

			return nil
		}

		err := addImageFunction()
		if (err != nil) {
			setErrorEncounteredPage(window, err, currentPage)
			return
		}
		nextPage()
	}))

	croppedImageObject, err := imagery.ConvertJPEGBase64StringToCroppedDownsizedImageObject(newImageBase64String)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	currentImageFyne := canvas.NewImageFromImage(croppedImageObject)
	currentImageFyne.FillMode = canvas.ImageFillContain

	viewFullpageButton := getWidgetCentered(widget.NewButtonWithIcon("", theme.ZoomInIcon(), func(){
			setViewFullpageImagePage(window, croppedImageObject, currentPage)
		}))

	emptyLabelA := widget.NewLabel("")

	zoomButtonWithSpacer := container.NewVBox(viewFullpageButton, emptyLabelA)

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

	page := container.NewBorder(header, zoomButtonWithSpacer, nil, nil, currentImageFyne)

	setPageContent(page, window)
}


func setBuildProfilePage_Avatar(window fyne.Window, profileType string, previousPage func()){

	currentPage := func(){setBuildProfilePage_Avatar(window, profileType, previousPage)}

	title := getPageTitleCentered(translate("Build " + profileType + " Profile - General"))
	
	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Avatar"))

	description := getLabelCentered(translate("Choose your avatar."))

	currentAvatarLabel := getBoldLabelCentered("Current Avatar:")

	getCurrentAvatarIdentifier := func()(int, error){
	
		exists, myAvatarIdentifier, err := myLocalProfiles.GetProfileData(profileType, "Avatar")
		if (err != nil) { return 0, err }
		if (exists == false){
			return 2929, nil
		}
		myAvatarIdentifierInt, err := helpers.ConvertStringToInt(myAvatarIdentifier)
		if (err != nil) {
			return 0, errors.New("MyLocalProfile is malformed: Invalid avatar: " + myAvatarIdentifier)
		}

		return myAvatarIdentifierInt, nil
	}

	myAvatarIdentifier, err := getCurrentAvatarIdentifier()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	emojiImage, err := getEmojiImageObject(myAvatarIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	emojiImageFyne := canvas.NewImageFromImage(emojiImage)

	nextPageFunction := func(newEmojiIdentifier int){

		isValid := imageFiles.VerifyEmojiIdentifier(newEmojiIdentifier)
		if (isValid == false){
			setErrorEncounteredPage(window, errors.New("Invalid emoji identifier selected from chooseEmojiPage"), currentPage)
			return
		}

		newEmojiIdentifierString := helpers.ConvertIntToString(newEmojiIdentifier)
	
		err := myLocalProfiles.SetProfileData(profileType, "Avatar", newEmojiIdentifierString)
		if (err != nil){
			setErrorEncounteredPage(window, err, currentPage)
			return
		}
		currentPage()
	}

	chooseAvatarButton := getWidgetCentered(widget.NewButtonWithIcon("Choose Avatar", theme.GridIcon(), func(){
			setChooseEmojiPage(window, "Choose Avatar", "People", 0, currentPage, nextPageFunction)
		}))

	emptyLabelA := widget.NewLabel("")
	emptyLabelB := widget.NewLabel("")
	emptyLabelC := widget.NewLabel("")

	chooseAvatarButtonHeightened := container.NewVBox(chooseAvatarButton, emptyLabelA, emptyLabelB, emptyLabelC)

	avatarWithButton := getContainerCentered(container.NewGridWithColumns(1, emojiImageFyne, chooseAvatarButtonHeightened))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), description, widget.NewSeparator(), currentAvatarLabel, avatarWithButton)
	
	setPageContent(page, window)
}


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

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

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getPageSubtitleCentered(translate("Sexuality"))

	description := getLabelCentered(translate("What sex(es) are you interested in mating with?"))

	option1Translated := translate("Male")
	option2Translated := translate("Female")
	option3Translated := translate("Male And Female")

	untranslatedOptionsMap := map[string]string{
		option1Translated: "Male",
		option2Translated: "Female",
		option3Translated: "Male And Female",
	}

	sexualitySelectorOptions := []string{option1Translated, option2Translated, option3Translated}

	sexualitySelector := widget.NewRadioGroup(sexualitySelectorOptions, func(response string){

			if (response == ""){
				myLocalProfiles.DeleteProfileData("Mate", "Sexuality")
				return
			}

			responseUntranslated, exists := untranslatedOptionsMap[response]
			if (exists == false){
				setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing response: " + response), currentPage)
				return
			}
			myLocalProfiles.SetProfileData("Mate", "Sexuality", responseUntranslated)
		})

	exists, currentSexuality, err := myLocalProfiles.GetProfileData("Mate", "Sexuality")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (exists == true){
		if (currentSexuality != "Male" && currentSexuality != "Female" && currentSexuality != "Male And Female"){
			setErrorEncounteredPage(window, errors.New("MyLocalProfiles contains invalid sexuality: " + currentSexuality), previousPage)
			return
		}
		sexualitySelector.Selected = translate(currentSexuality)
	}
	sexualitySelectorCentered := getWidgetCentered(sexualitySelector)

	noResponseButton := getWidgetCentered(widget.NewButtonWithIcon(translate("No Response"), theme.CancelIcon(), func(){ 
			myLocalProfiles.DeleteProfileData("Mate", "Sexuality")
			currentPage()
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description, widget.NewSeparator(), sexualitySelectorCentered, noResponseButton)
	
	setPageContent(page, window)
}


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

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

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getBoldLabelCentered(translate("Tags"))

	description1 := getLabelCentered("Add tags to your profile.")
	description2 := getLabelCentered("Users can search for matches whose tags match custom terms.")

	addTagButton := getWidgetCentered(widget.NewButtonWithIcon("Add Tag", theme.ContentAddIcon(), func(){
			setBuildMateProfilePage_AddTag(window, currentPage, currentPage)
		}))

	myTagsExist, myTagsAttributeValue, err := myLocalProfiles.GetProfileData("Mate", "Tags")
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (myTagsExist == false){

		noTagsExistLabel := getBoldLabelCentered("No tags exist.")

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

		setPageContent(page, window)
		return
	}

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

		myTagsList := strings.Split(myTagsAttributeValue, "+&")

		nameLabel := getItalicLabelCentered("Name")
		emptyLabel := widget.NewLabel("")

		tagNameColumn := container.NewVBox(nameLabel, widget.NewSeparator())
		deleteButtonsColumn := container.NewVBox(emptyLabel, widget.NewSeparator())

		for _, tagName := range myTagsList{

			tagNameLabel := getBoldLabelCentered(tagName)

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

					newList, deletedAny := helpers.DeleteAllMatchingItemsFromList(myTagsList, tagName)
					if (deletedAny == false){
						setErrorEncounteredPage(window, errors.New("Cannot delete tag: tag not found."), currentPage)
						return	
					}

					if (len(newList) == 0){
						err := myLocalProfiles.DeleteProfileData("Mate", "Tags")
						if (err != nil){
							setErrorEncounteredPage(window, err, currentPage)
							return
						}
						currentPage()
						return
					}

					newTagsAttributeValue := strings.Join(newList, "+&")

					err := myLocalProfiles.SetProfileData("Mate", "Tags", newTagsAttributeValue)
					if (err != nil){
						setErrorEncounteredPage(window, err, currentPage)
						return
					}
					currentPage()
				})

			tagNameColumn.Add(tagNameLabel)
			deleteButtonsColumn.Add(deleteButton)

			tagNameColumn.Add(widget.NewSeparator())
			deleteButtonsColumn.Add(widget.NewSeparator())
		}
		
		tagsGrid := container.NewHBox(layout.NewSpacer(), tagNameColumn, deleteButtonsColumn, layout.NewSpacer())

		return tagsGrid, nil
	}
	
	tagsGrid, err := getTagsGrid()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	setPageContent(page, window)
}

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

	currentPage := func(){setBuildMateProfilePage_AddTag(window, previousPage, nextPage)}

	title := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered(translate("Add Tag"))

	enterTagNameLabel := getBoldLabelCentered(translate("Enter Tag Name:"))

	enterTagEntry := widget.NewEntry()
	enterTagEntry.SetPlaceHolder("Enter Tag Name...")

	myTagsExist, myTagsAttributeValue, err := myLocalProfiles.GetProfileData("Mate", "Tags")
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	addTagButton := getWidgetCentered(widget.NewButtonWithIcon("Add Tag", theme.ContentAddIcon(), func(){

			newTag := enterTagEntry.Text

			if (newTag == ""){
				title := translate("Cannot Add Tag.")
				dialogMessage := getLabelCentered(translate("Tag is empty."))
				dialogContent := container.NewVBox(dialogMessage)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			containsDelimiter := strings.Contains(newTag, "+&")
			if (containsDelimiter == true){
				title := translate("Cannot Add Tag.")
				dialogMessageA := getLabelCentered(translate("Tag contains invalid substring: " + `"+&"`))
				dialogMessageB := getLabelCentered(translate("Remove this substring and resubmit."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			newTagBytesLength := len(newTag)

			if (newTagBytesLength > 40){

				newTagBytesLengthString := helpers.ConvertIntToString(newTagBytesLength)

				title := translate("Cannot Add Tag.")				
				dialogMessageA := getLabelCentered(translate("Tag is too long."))
				dialogMessageB := getLabelCentered(translate("Tag cannot be longer than 40 bytes."))
				dialogMessageC := getLabelCentered(translate("Your tag length:") + newTagBytesLengthString)
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			if (myTagsExist == false){			

				err := myLocalProfiles.SetProfileData("Mate", "Tags", newTag)
				if (err != nil){
					setErrorEncounteredPage(window, err, previousPage)
					return
				}
				nextPage()
				return
			}

			existingTagsList := strings.Split(myTagsAttributeValue, "+&")

			if (len(existingTagsList) >= 30){
				title := translate("Cannot Create Tag.")
				dialogMessageA := getLabelCentered(translate("You cannot have more than 30 tags."))
				dialogMessageB := getLabelCentered(translate("You must first delete an existing tag."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			existingTagsTotalByteLength := 0

			for _, tagString := range existingTagsList{

				if (tagString == ""){
					setErrorEncounteredPage(window, errors.New("My tags are invalid: Existing tag is empty."), currentPage)
					return
				}

				tagByteLength := len(tagString)

				if (tagByteLength > 40){
					setErrorEncounteredPage(window, errors.New("My tags are invalid: Existing tag is too long."), currentPage)
					return
				}
				existingTagsTotalByteLength += tagByteLength
			}

			newTagListByteLength := existingTagsTotalByteLength + newTagBytesLength

			if (newTagListByteLength > 500){
				title := translate("Cannot Add Tag.")
				dialogMessageA := getLabelCentered(translate("Byte length of all tags exceeds 500."))
				dialogMessageB := getLabelCentered(translate("Enter a shorter tag or delete an existing tag."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			tagAlreadyExists := slices.Contains(existingTagsList, newTag)
			if (tagAlreadyExists == true){
				title := translate("Cannot Add Tag.")
				dialogMessage := getLabelCentered(translate("Tag already exists."))
				dialogContent := container.NewVBox(dialogMessage)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			isAllowed := allowedText.VerifyStringIsAllowed(newTag)
			if (isAllowed == false){
				title := translate("Invalid Tag")
				dialogMessageA := getLabelCentered(translate("Your tag contains an invalid character."))
				dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8"))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			containsTabOrNewline := helpers.CheckIfStringContainsTabsOrNewlines(newTag)
			if (containsTabOrNewline == true){
				title := translate("Invalid Tag")
				dialogMessageA := getLabelCentered(translate("Your tag contains a tab or newline character."))
				dialogMessageB := getLabelCentered(translate("Remove the character and resubmit."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			newTagsList := append(existingTagsList, newTag)

			newTagsAttributeValue := strings.Join(newTagsList, "+&")

			err := myLocalProfiles.SetProfileData("Mate", "Tags", newTagsAttributeValue)
			if (err != nil){
				setErrorEncounteredPage(window, err, previousPage)
				return
			}

			nextPage()
		}))

	enterTagEntryWithLabel := getContainerCentered(container.NewGridWithColumns(1, enterTagNameLabel, enterTagEntry))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), enterTagEntryWithLabel, addTagButton)

	setPageContent(page, window)
}


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

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

	pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getPageSubtitleCentered(translate("Questionnaire"))

	description1 := getLabelCentered("Create a questionnaire for users to fill out.")
	description2 := getLabelCentered("You can filter users who choose your desired answers.")
	description3 := getLabelCentered("Your desired answers are private.")

	buildQuestionnaireIcon, err := getFyneImageIcon("BuildQuestionnaire")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	viewQuestionnaireIcon, err := getFyneImageIcon("Questionnaire")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	buildQuestionnaireButton := widget.NewButton("Build Questionnaire", func(){
			setBuildMateProfilePage_QuestionnaireQuestions(window, 0, currentPage)
		})
	chooseDesiresButton := widget.NewButton("Choose Desired Answers", func(){
			//TODO
			showUnderConstructionDialog(window)
		})
	viewQuestionnaireButton := widget.NewButton("View My Questionnaire", func(){

			exists, myQuestionnaireRaw, err := myLocalProfiles.GetProfileData("Mate", "Questionnaire")
			if (err != nil) {
				setErrorEncounteredPage(window, err, previousPage)
				return
			}
			if (exists == false){
				title := translate("No Questions Exist")
				dialogMessageA := getLabelCentered("You must add a question before viewing your questionnaire.")
				dialogMessageB := getLabelCentered("Add a question on the Build Questionnaire page.")
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			myQuestionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(myQuestionnaireRaw)
			if (err != nil){
				setErrorEncounteredPage(window, err, previousPage)
				return
			}

			submitPageFunction := func(_ string, _ func()){
					currentPage()
				}

			emptyMap := make(map[string]string)
			setTakeQuestionnairePage(window, myQuestionnaireObject, 0, emptyMap, currentPage, submitPageFunction)
		})

	buildQuestionnaireButtonWithIcon := container.NewGridWithColumns(1, buildQuestionnaireIcon, buildQuestionnaireButton)
	chooseDesiresButtonWithIcon := container.NewGridWithColumns(1, desiresIcon, chooseDesiresButton)
	viewQuestionnaireButtonWithIcon := container.NewGridWithColumns(1, viewQuestionnaireIcon, viewQuestionnaireButton)

	buttonsSection := getContainerCentered(container.NewGridWithColumns(1, buildQuestionnaireButtonWithIcon, chooseDesiresButtonWithIcon, viewQuestionnaireButtonWithIcon))

	page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), buttonsSection)
	
	setPageContent(page, window)
}

func setBuildMateProfilePage_QuestionnaireQuestions(window fyne.Window, pageIndex int, previousPage func()){

	setLoadingScreen(window, "View Questionnaire Questions ", "Loading questionnaire questions...")

	currentPage := func(){setBuildMateProfilePage_QuestionnaireQuestions(window, pageIndex, previousPage)}

	pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getPageSubtitleCentered(translate("Questionnaire Questions"))

	exists, myQuestionnaireRaw, err := myLocalProfiles.GetProfileData("Mate", "Questionnaire")
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (exists == false) {
		noQuestionsExistLabel := getBoldLabelCentered("No questions exist.")

		addQuestionButton := getWidgetCentered(widget.NewButtonWithIcon("Add Question", theme.ContentAddIcon(), func(){
				setBuildMateProfilePage_AddQuestionnaireQuestion(window, currentPage, currentPage)
			}))

		page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), noQuestionsExistLabel, addQuestionButton)

		setPageContent(page, window)
		return	
	}

	myQuestionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(myQuestionnaireRaw)
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	deleteQuestionnaireQuestionFunction := func(questionIdentifier string)error{

		newQuestionnaireQuestionsList := make([]mateQuestionnaire.QuestionObject, 0)

		for _, questionObject := range myQuestionnaireObject{

			currentQuestionIdentifier := questionObject.Identifier

			if (currentQuestionIdentifier != questionIdentifier){
				newQuestionnaireQuestionsList = append(newQuestionnaireQuestionsList, questionObject)
			}
		}

		if (len(newQuestionnaireQuestionsList) == 0){
			err := myLocalProfiles.DeleteProfileData("Mate", "Questionnaire")
			if (err != nil) { return err }
			
			return nil
		}

		newQuestionnaireRaw, err := mateQuestionnaire.CreateQuestionnaireString(newQuestionnaireQuestionsList)
		if (err != nil) { return err }
		
		err = myLocalProfiles.SetProfileData("Mate", "Questionnaire", newQuestionnaireRaw)
		if (err != nil) { return err }
	
		return nil
	}

	increaseOrDecreaseQuestionIndexFunction := func(questionIdentifier string, increaseOrDecrease string)error{

		// Increase: Move question towards the end of questionnaire
		// Decrease: Move question towards the beginning of questionnaire

		if (increaseOrDecrease != "Increase" && increaseOrDecrease != "Decrease"){
			return errors.New("increaseOrDecreaseQuestionIndexFunction called with invalid increaseOrDecrease: " + increaseOrDecrease)
		}

		if (len(myQuestionnaireObject) <= 1){
			return errors.New("increaseOrDecreaseQuestionIndexFunction called when questionnaire has <=1 question.")
		}

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

			for index, questionObject := range myQuestionnaireObject{

				currentQuestionIdentifier := questionObject.Identifier

				if (currentQuestionIdentifier == questionIdentifier){
					return index, nil
				}
			}
			//Should not happen, buttons will only be shown for existing questions
			return 0, errors.New("Question to increase/decrease not found.")
		}

		questionIndex, err := getQuestionCurrentIndex()
		if (err != nil) { return err }

		finalIndex := len(myQuestionnaireObject) - 1

		if (questionIndex == 0 && increaseOrDecrease == "Decrease"){
			return errors.New("Trying to decrease the index of first question.")
		}
		if (questionIndex == finalIndex && increaseOrDecrease == "Increase"){
			return errors.New("Trying to increase the index of last question.")
		}

		swapFunction := reflect.Swapper(myQuestionnaireObject)

		if (increaseOrDecrease == "Increase"){
			swapFunction(questionIndex, questionIndex+1)
		} else {
			swapFunction(questionIndex-1, questionIndex)
		}

		newRawQuestionnaire, err := mateQuestionnaire.CreateQuestionnaireString(myQuestionnaireObject)
		if (err != nil) { return err }

		err = myLocalProfiles.SetProfileData("Mate", "Questionnaire", newRawQuestionnaire)
		if (err != nil) { return err }

		return nil
	}

	addQuestionButton := getWidgetCentered(widget.NewButtonWithIcon("Add Question", theme.ContentAddIcon(), func(){

			if (len(myQuestionnaireObject) >= 25){
				title := translate("Maximum Question Limit Reached")
				dialogMessageA := getLabelCentered("You have added the maximum of 25 questions.")
				dialogMessageB := getLabelCentered("You must delete a question to add a new question.")
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(title, translate("Close"), dialogContent, window)
				return
			}

			setViewFinalQuestionnaireQuestionsPage := func(){setBuildMateProfilePage_QuestionnaireQuestions(window, 25, previousPage)}
			setBuildMateProfilePage_AddQuestionnaireQuestion(window, currentPage, setViewFinalQuestionnaireQuestionsPage)
		}))

	getQuestionViewIndex := func()int{

		if (pageIndex <= 0){
			return 0
		}

		numberOfQuestions := len(myQuestionnaireObject)

		finalQuestionIndex := numberOfQuestions - 1

		if (pageIndex > finalQuestionIndex){

			// Index is out of range, show last page of questions

			if (numberOfQuestions <= 5){
				return 0
			}

			lastPageNumberOfQuestions := numberOfQuestions % 5
			if (lastPageNumberOfQuestions == 0){
				lastPageViewIndex := finalQuestionIndex - 4
				return lastPageViewIndex
			}
			lastPageViewIndex := finalQuestionIndex - lastPageNumberOfQuestions + 1
			return lastPageViewIndex
		}

		return pageIndex
	}

	questionViewIndex := getQuestionViewIndex()

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

		emptyLabelA := widget.NewLabel("")
		typeLabel := getItalicLabelCentered("Type")
		contentLabel := getItalicLabelCentered("Content")
		emptyLabelB := widget.NewLabel("")	

		questionIndexColumn := container.NewVBox(emptyLabelA, widget.NewSeparator())
		questionTypeColumn := container.NewVBox(typeLabel, widget.NewSeparator())
		questionContentColumn := container.NewVBox(contentLabel, widget.NewSeparator())
		buttonsColumn := container.NewVBox(emptyLabelB, widget.NewSeparator())

		addQuestionRow := func(questionIndex int, questionObject mateQuestionnaire.QuestionObject)error{

			questionIdentifier := questionObject.Identifier
			questionType := questionObject.Type
			questionContent := questionObject.Content
			questionOptions := questionObject.Options

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

				if (questionType == "Entry"){
					questionTypeText := "Entry - " + questionOptions

					return questionTypeText, nil
				}
				if (questionType == "Choice"){

					maximumAnswersAllowed, choicesListString, delimiterFound := strings.Cut(questionOptions, "#")
					if (delimiterFound == false){
						return "", errors.New("Invalid choice question options: " + questionOptions)					
					}

					choicesList := strings.Split(choicesListString, "$¥")
					numberOfOptionsString := helpers.ConvertIntToString(len(choicesList))
					if (len(choicesList) > 6){
						return "", errors.New("Invalid choices list: too many choices.")
					}

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

						maximumAnswersAllowedInt, err := helpers.ConvertStringToInt(maximumAnswersAllowed)
						if (err != nil) { return 0, err }

						if (maximumAnswersAllowedInt < 1 || maximumAnswersAllowedInt > 6){
							return 0, errors.New("Invalid choice maximum answers allowed")
						}
						if (maximumAnswersAllowedInt > len(choicesList)){
							return len(choicesList), nil
						}
						return maximumAnswersAllowedInt, nil
					}

					maximumAnswersAllowedAdjusted, err := getMaximumAnswersAllowedAdjusted()
					if (err != nil) { return "", err }

					maximumAnswersAllowedAdjustedString := helpers.ConvertIntToString(maximumAnswersAllowedAdjusted)

					questionTypeText := numberOfOptionsString + " Choices - Max: " + maximumAnswersAllowedAdjustedString

					return questionTypeText, nil
				}
				return "", errors.New("Malformed question map: invalid question type: " + questionType)
			}

			questionTypeText, err := getQuestionTypeText()
			if (err != nil) { return err }

			questionContentTrimmed, _, err := helpers.TrimAndFlattenString(questionContent, 15)
			if (err != nil) { return err }

			emptyLabelA := widget.NewLabel("")
			questionIndexString := helpers.ConvertIntToString(questionIndex + 1)
			questionIndexLabel := getBoldLabel(questionIndexString + ".")
			questionIndexCell := container.NewVBox(questionIndexLabel, emptyLabelA)

			emptyLabelB := widget.NewLabel("")
			questionTypeLabel := getBoldLabelCentered(questionTypeText)
			questionTypeCell := container.NewVBox(questionTypeLabel, emptyLabelB)

			emptyLabelC := widget.NewLabel("")
			questionContentLabel := getBoldLabelCentered(questionContentTrimmed)
			questionContentCell := container.NewVBox(questionContentLabel, emptyLabelC)

			getQuestionIncreaseDecreaseIndexButtons := func()*fyne.Container{

				getUpButton := func()fyne.Widget{
					if (questionIndex == 0){
						emptyButton := widget.NewButton("", nil)
						return emptyButton
					}
					upButton := widget.NewButtonWithIcon("", theme.MoveUpIcon(), func(){
							err := increaseOrDecreaseQuestionIndexFunction(questionIdentifier, "Decrease")
							if (err != nil) {
								setErrorEncounteredPage(window, err, previousPage)
								return
							}
							currentPage()
						})
					return upButton
				}

				getDownButton := func()fyne.Widget{
					if (questionIndex >= len(myQuestionnaireObject)-1){
						emptyButton := widget.NewButton("", nil)
						return emptyButton
					}
					downButton := widget.NewButtonWithIcon("", theme.MoveDownIcon(), func(){
							err := increaseOrDecreaseQuestionIndexFunction(questionIdentifier, "Increase")
							if (err != nil) {
								setErrorEncounteredPage(window, err, previousPage)
								return
							}
							currentPage()
						})
					return downButton
				}
				upButton := getUpButton()
				downButton := getDownButton()

				buttonsGrid := container.NewGridWithColumns(1, upButton, downButton)
				return buttonsGrid
			}

			deleteQuestionButtonFunction := func(){

				confirmDialogCallbackFunction := func(response bool){
					if (response == false){
						return
					}
					err := deleteQuestionnaireQuestionFunction(questionIdentifier)
					if (err != nil) {
						setErrorEncounteredPage(window, err, previousPage)
						return
					}
					currentPage()
				}

				dialogTitle := translate("Confirm Delete Question?")
				dialogMessage := getLabelCentered("Confirm to delete this question?")
				dialogContent := container.NewVBox(dialogMessage)
				dialog.ShowCustomConfirm(dialogTitle, translate("Yes"), translate("No"), dialogContent, confirmDialogCallbackFunction, window)
			}

			deleteQuestionButton := widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), deleteQuestionButtonFunction)

			previewQuestionButton := widget.NewButtonWithIcon("Preview", theme.VisibilityIcon(), func(){
					setPreviewMyQuestionnaireQuestionPage(window, questionObject, currentPage)
				})

			deletePreviewButtons := container.NewVBox(previewQuestionButton, deleteQuestionButton)

			getButtonsCell := func()*fyne.Container{

				if (len(myQuestionnaireObject) < 2){
					return deletePreviewButtons
				}

				questionIncreaseDecreaseIndexButtons := getQuestionIncreaseDecreaseIndexButtons()		

				buttonsCell := container.NewHBox(questionIncreaseDecreaseIndexButtons, deletePreviewButtons)			
			
				return buttonsCell			
			}
			
			buttonsCell := getButtonsCell()

			questionIndexColumn.Add(questionIndexCell)
			questionTypeColumn.Add(questionTypeCell)
			questionContentColumn.Add(questionContentCell)
			buttonsColumn.Add(buttonsCell)

			questionIndexColumn.Add(widget.NewSeparator())
			questionTypeColumn.Add(widget.NewSeparator())
			questionContentColumn.Add(widget.NewSeparator())
			buttonsColumn.Add(widget.NewSeparator())
			
			return nil
		}

		pageQuestionsList := myQuestionnaireObject[questionViewIndex:]

		questionsGrid := container.NewHBox(layout.NewSpacer(), questionIndexColumn, questionTypeColumn, questionContentColumn, buttonsColumn, layout.NewSpacer())

		counter := 0
		for index, questionObject := range pageQuestionsList{

			if (counter >= 5){
				break
			}

			questionIndex := questionViewIndex + index
			err := addQuestionRow(questionIndex, questionObject)
			if (err != nil) { return nil, err }

			counter += 1
		}

		return questionsGrid, nil
	}

	//Outputs:
	//	-bool: Either button exists
	//	-*fyne.Container: Buttons
	getPageNavigationButtons := func()(bool, *fyne.Container){	

		finalQuestionIndex := len(myQuestionnaireObject) -1

		if (questionViewIndex == 0 && questionViewIndex > (finalQuestionIndex-5)){
			return false, nil
		}

		getPreviousPageButton := func()fyne.Widget{
			if (questionViewIndex == 0){
				emptyButton := widget.NewButton("", nil)
				return emptyButton
			}
			previousButton := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), func(){
					setBuildMateProfilePage_QuestionnaireQuestions(window, questionViewIndex-5, previousPage)
				})
			return previousButton
		}

		getNextPageButton := func()fyne.Widget{
			if (questionViewIndex > (finalQuestionIndex-5)){
				emptyButton := widget.NewButton("", nil)
				return emptyButton				
			}

			nextButton := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), func(){
					setBuildMateProfilePage_QuestionnaireQuestions(window, questionViewIndex+5, previousPage)
				})

			return nextButton
		}

		previousPageButton := getPreviousPageButton()
		nextPageButton := getNextPageButton()

		buttonsCentered := getContainerCentered(container.NewGridWithColumns(2, previousPageButton, nextPageButton))

		return true, buttonsCentered
	}

	page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), addQuestionButton, widget.NewSeparator())

	navigationButtonsNeeded, navigationButtons := getPageNavigationButtons()
	if (navigationButtonsNeeded == true){
		page.Add(navigationButtons)
	}

	questionsGrid, err := getQuestionsGrid()
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	questionsGridCentered := getContainerCentered(questionsGrid)
	page.Add(questionsGridCentered)

	setPageContent(page, window)
}

func setPreviewMyQuestionnaireQuestionPage(window fyne.Window, questionObject mateQuestionnaire.QuestionObject, previousPage func()){

	currentPage := func(){setPreviewMyQuestionnaireQuestionPage(window, questionObject, previousPage)}

	title := getPageTitleCentered(translate("Preview Question"))

	backButton := getBackButtonCentered(previousPage)

	myResponsesMap := make(map[string]string)

	viewQuestionContainer, err := getViewQuestionnaireQuestionContainer(window, currentPage, questionObject, myResponsesMap)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	setPageContent(page, window)
}


func setBuildMateProfilePage_AddQuestionnaireQuestion(window fyne.Window, previousPage func(), afterCreatePage func()){

	currentPage := func(){setBuildMateProfilePage_AddQuestionnaireQuestion(window, previousPage, afterCreatePage)}

	pageTitle := getPageTitleCentered(translate("Build Mate Profile - General"))

	backButton := getBackButtonCentered(previousPage)

	pageSubtitle := getPageSubtitleCentered(translate("Add Questionnaire Question"))

	addQuestionToMyQuestionnaireFunction := func(newQuestionObject mateQuestionnaire.QuestionObject)error{

		getNewQuestionnaireObject := func()([]mateQuestionnaire.QuestionObject, error){

			exists, myQuestionnaireRaw, err := myLocalProfiles.GetProfileData("Mate", "Questionnaire")
			if (err != nil) { return nil, err }
			if (exists == false) {

				newQuestionnaireObject := []mateQuestionnaire.QuestionObject{newQuestionObject}

				return newQuestionnaireObject, nil
			}

			existingQuestionnaireObject, err := mateQuestionnaire.ReadQuestionnaireString(myQuestionnaireRaw)
			if (err != nil) { return nil, err }

			newQuestionnaireObject := append(existingQuestionnaireObject, newQuestionObject)

			return newQuestionnaireObject, nil
		}

		newQuestionnaireObject, err := getNewQuestionnaireObject()
		if (err != nil) { return err }

		newQuestionnaireString, err := mateQuestionnaire.CreateQuestionnaireString(newQuestionnaireObject)
		if (err != nil) { return err }

		err = myLocalProfiles.SetProfileData("Mate", "Questionnaire", newQuestionnaireString)
		if (err != nil) { return err }	

		return nil
	}
	
	addQuestionDescription := getLabelCentered("Choose the type of question to add:")

	iconSize := getCustomFyneSize(10)

	choiceIcon, err := getFyneImageIcon("Choice")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	choiceIcon.SetMinSize(iconSize)

	entryIcon, err := getFyneImageIcon("Entry")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	entryIcon.SetMinSize(iconSize)

	choiceButton := widget.NewButton("Choice", func(){
			setBuildMateProfilePage_AddQuestionnaireQuestion_Choice(window, addQuestionToMyQuestionnaireFunction, currentPage, afterCreatePage)

		})
	entryButton := widget.NewButton("Entry", func(){
			setBuildMateProfilePage_AddQuestionnaireQuestion_Entry(window, addQuestionToMyQuestionnaireFunction, currentPage, afterCreatePage)		
		})

	choiceButtonWithIcon := container.NewVBox(choiceIcon, choiceButton)
	entryButtonWithIcon := container.NewVBox(entryIcon, entryButton)

	choicesSection := container.NewHBox(layout.NewSpacer(), choiceButtonWithIcon, entryButtonWithIcon, layout.NewSpacer())

	page := container.NewVBox(pageTitle, backButton, widget.NewSeparator(), pageSubtitle, widget.NewSeparator(), addQuestionDescription, choicesSection)

	setPageContent(page, window)
}


func setBuildMateProfilePage_AddQuestionnaireQuestion_Choice(window fyne.Window, addQuestionFunction func(mateQuestionnaire.QuestionObject)error, previousPage func(), afterCreatePage func()){

	title := getPageTitleCentered("Add Questionnaire Question - Choice")

	backButton := getBackButtonCentered(previousPage)

	description := getLabelCentered("A choice question has multiple answers to choose from.")

	enterQuestionLabel := getBoldLabelCentered("                                  Enter Question:                                  ")

	enterQuestionEntry := widget.NewEntry()
	enterQuestionEntry.SetPlaceHolder("Enter question.")
	enterQuestionEntryBoxed := getWidgetBoxed(enterQuestionEntry)

	enterQuestionLabelWithEntry := getContainerCentered(container.NewGridWithColumns(1, enterQuestionLabel, enterQuestionEntryBoxed))

	maximumAnswersAllowedLabel := widget.NewLabel("Maximum Answers Allowed:")
	selectOptions := []string{"1", "2", "3", "4", "5", "6"}
	maximumAnswersAllowedSelector := widget.NewSelect(selectOptions, nil)
	maximumAnswersAllowedSelector.Selected = "6"
	maximumAnswersAllowedInfoButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){

			dialogTitle := translate("Maximum Answers Allowed")
			dialogMessageA := getLabelCentered(translate("Choose the maximum number of answers a user can submit."))
			dialogMessageB := getLabelCentered(translate("For example, if you select 1, users can only select 1 choice."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
		})

	maximumAnswersAllowedRow := container.NewHBox(layout.NewSpacer(), maximumAnswersAllowedLabel, maximumAnswersAllowedSelector, maximumAnswersAllowedInfoButton, layout.NewSpacer())

	emptyLabelA := widget.NewLabel("")
	choiceContentLabel := getItalicLabelCentered("                       Choice Content                       ")
	emptyLabelB := widget.NewLabel("")

	choiceIndexColumn := container.NewVBox(emptyLabelA, widget.NewSeparator())
	choiceEntryColumn := container.NewVBox(choiceContentLabel, widget.NewSeparator())
	clearChoiceButtonsColumn := container.NewVBox(emptyLabelB, widget.NewSeparator())

	addChoiceRow := func(index string)func()string{

		indexLabel := getBoldLabelCentered(index)

		choiceEntry := widget.NewEntry()
		choiceEntry.SetPlaceHolder("Enter choice...")

		clearChoiceButton := widget.NewButtonWithIcon("Clear", theme.CancelIcon(), func(){
				choiceEntry.SetText("")
				choiceEntry.SetPlaceHolder("Enter choice...")
			})

		choiceEntryColumn.Add(choiceEntry)
		choiceIndexColumn.Add(indexLabel)
		clearChoiceButtonsColumn.Add(clearChoiceButton)

		getChoiceFunction := func()string{
			return choiceEntry.Text
		}

		return getChoiceFunction
	}

	getChoice1Function := addChoiceRow("1.")
	getChoice2Function := addChoiceRow("2.")
	getChoice3Function := addChoiceRow("3.")
	getChoice4Function := addChoiceRow("4.")
	getChoice5Function := addChoiceRow("5.")
	getChoice6Function := addChoiceRow("6.")

	choicesGrid := container.NewHBox(layout.NewSpacer(), choiceIndexColumn, choiceEntryColumn, clearChoiceButtonsColumn, layout.NewSpacer())

	submitButton := getWidgetCentered(widget.NewButtonWithIcon("Create Question", theme.ConfirmIcon(), func(){

		newQuestionContent := enterQuestionEntry.Text

		if (newQuestionContent == ""){
			dialogTitle := translate("No question provided.")
			dialogMessage := translate("You must enter a question.")
			dialogContent := getLabelCentered(dialogMessage)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
			return
		}

		isAllowed := allowedText.VerifyStringIsAllowed(newQuestionContent)
		if (isAllowed == false){
			dialogTitle := translate("Question Is Invalid.")
			dialogMessageA := getLabelCentered(translate("Question contains a prohibited character."))
			dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
			return
		}

		// The choice cannot contain these strings because we user them for encoding
		unallowedStringsList := []string{"+&", "%¢"}

		for _, unallowedString := range unallowedStringsList{

			isContained := strings.Contains(newQuestionContent, unallowedString)
			if (isContained == true){
				dialogTitle := translate("Question Is Invalid.")
				dialogMessageA := getLabelCentered(translate("Question contains prohibited string: ") + unallowedString)
				dialogMessageB := getLabelCentered(translate("Remove this string and resubmit."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}			
		}

		if (len(newQuestionContent) > 500){

			questionNumberOfBytesString := helpers.ConvertIntToString(len(newQuestionContent))

			dialogTitle := translate("Question Is Invalid.")
			dialogMessageA := getLabelCentered(translate("Question is longer than 500 bytes."))
			dialogMessageB := getLabelCentered(translate("Your question byte count: " + questionNumberOfBytesString))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
			return
		}

		choice1 := getChoice1Function()
		choice2 := getChoice2Function()
		choice3 := getChoice3Function()
		choice4 := getChoice4Function()
		choice5 := getChoice5Function()
		choice6 := getChoice6Function()		

		choiceEntryValuesList := []string{choice1, choice2, choice3, choice4, choice5, choice6}

		newChoicesList := make([]string, 0)

		// We use a map to detect duplicates.
		choicesMap := make(map[string]struct{})

		for index, choiceString := range choiceEntryValuesList{

			if (choiceString == ""){
				continue
			}

			choiceIndexString := helpers.ConvertIntToString(index+1)

			choiceIsAllowed := allowedText.VerifyStringIsAllowed(choiceString)
			if (choiceIsAllowed == false){
				dialogTitle := translate("Choice " + choiceIndexString + " Is Invalid.")
				dialogMessageA := getLabelCentered(translate("Choice " + choiceIndexString + " contains a prohibited character."))
				dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			unallowedStringsList := []string{"+&", "%¢", "$¥"}

			for _, unallowedString := range unallowedStringsList{

				isContained := strings.Contains(choiceString, unallowedString)
				if (isContained == true){
					dialogTitle := translate("Question Is Invalid.")
					dialogMessageA := getLabelCentered(translate("Choice " + choiceIndexString + " contains prohibited string: ") + unallowedString)
					dialogMessageB := getLabelCentered(translate("Remove this string and resubmit."))
					dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
					dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
					return
				}
			}

			if (len(choiceString) > 100){

				choiceNumberOfBytesString := helpers.ConvertIntToString(len(choiceString))

				dialogTitle := translate("Choice Is Invalid.")
				dialogMessageA := getLabelCentered(translate("Choice " + choiceIndexString + " is longer than 100 bytes."))
				dialogMessageB := getLabelCentered(translate("Choice byte count: " + choiceNumberOfBytesString))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			_, exists := choicesMap[choiceString]
			if (exists == true){
				dialogTitle := translate("Duplicate Choice Exists")
				dialogMessage := translate("Each choice must be unique.")
				dialogContent := getLabelCentered(dialogMessage)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			choicesMap[choiceString] = struct{}{}

			newChoicesList = append(newChoicesList, choiceString)
		}

		if (len(newChoicesList) < 2){
			dialogTitle := translate("Not enough choices.")
			dialogMessage := translate("You must enter at least 2 choices.")
			dialogContent := getLabelCentered(dialogMessage)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
			return
		}

		maximumAnswersAllowedString := maximumAnswersAllowedSelector.Selected

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

		questionOptionsChoicesJoined := strings.Join(newChoicesList, "$¥")
		questionOptions := maximumAnswersAllowedString + "#" + questionOptionsChoicesJoined

		newQuestionObject := mateQuestionnaire.QuestionObject{
			Identifier: newQuestionIdentifier,
			Type: "Choice",
			Content: newQuestionContent,
			Options: questionOptions,
		}

		err = addQuestionFunction(newQuestionObject)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}

		afterCreatePage()
	}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), enterQuestionLabelWithEntry, widget.NewSeparator(), maximumAnswersAllowedRow, widget.NewSeparator(), choicesGrid, submitButton)

	setPageContent(page, window)
}


func setBuildMateProfilePage_AddQuestionnaireQuestion_Entry(window fyne.Window, addQuestionFunction func(mateQuestionnaire.QuestionObject)error, previousPage func(), afterCreatePage func()){

	title := getPageTitleCentered(translate("Add Questionnaire Question - Entry"))

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Entry questions allow responders to enter any text.")
	description2 := getLabelCentered("Select Numeric to restrict responses to only allow numbers.")
	description3 := getLabelCentered("Numeric allows you to filter users who respond within a desired range.")

	enterQuestionLabel := getBoldLabelCentered("Enter Question:")
	enterQuestionEntry := widget.NewMultiLineEntry()
	enterQuestionEntry.Wrapping = 3
	enterQuestionEntry.SetPlaceHolder("Enter question.")
	enterQuestionEntryBoxed := getWidgetBoxed(enterQuestionEntry)

	numericCheckbox := widget.NewCheck("Numeric Responses Only", nil)

	submitButton := getWidgetCentered(widget.NewButtonWithIcon("Add Question", theme.ConfirmIcon(), func(){

		newQuestionContent := enterQuestionEntry.Text

		if (newQuestionContent == ""){
			dialogTitle := translate("No question provided.")
			dialogMessage := translate("You must enter a question.")
			dialogContent := getLabelCentered(dialogMessage)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
			return
		}

		if (len(newQuestionContent) > 500){

			newQuestionBytesString := helpers.ConvertIntToString(len(newQuestionContent))

			dialogTitle := translate("Question Is Invalid.")
			dialogMessageA := getLabelCentered(translate("Question cannot be longer than 500 bytes."))
			dialogMessageB := getLabelCentered(translate("Provided question byte count: " + newQuestionBytesString))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
			return
		}

		isAllowed := allowedText.VerifyStringIsAllowed(newQuestionContent)
		if (isAllowed == false){
			dialogTitle := translate("Question Is Invalid.")
			dialogMessageA := getLabelCentered(translate("Question contains a prohibited character."))
			dialogMessageB := getLabelCentered(translate("It must be encoded in UTF-8."))
			dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
			dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
			return
		}

		// The question cannot contain these strings because we user them for encoding
		unallowedStringsList := []string{"+&", "%¢"}

		for _, unallowedString := range unallowedStringsList{

			isContained := strings.Contains(newQuestionContent, unallowedString)
			if (isContained == true){
				dialogTitle := translate("Question Is Invalid.")
				dialogMessageA := getLabelCentered(translate("Question contains prohibited string: ") + unallowedString)
				dialogMessageB := getLabelCentered(translate("Remove this string and resubmit."))
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}			
		}

		isNumeric := numericCheckbox.Checked

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

		getQuestionOptions := func()string{
			if (isNumeric == true){
				return "Numeric"
			}
			return "Any"
		}
		questionOptions := getQuestionOptions()

		newQuestionObject := mateQuestionnaire.QuestionObject{

			Identifier: newQuestionIdentifier,
			Type: "Entry",
			Content: newQuestionContent,
			Options: questionOptions,
		}

		err = addQuestionFunction(newQuestionObject)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}

		afterCreatePage()
	}))

	submitButtonWithCheckbox := container.NewVBox(numericCheckbox, submitButton)	

	entryWithCheckBoxAndSubmitButton := getContainerCentered(container.NewGridWithColumns(1, enterQuestionEntryBoxed, submitButtonWithCheckbox))

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




