
package gui

// manageGeneticsGui.go implements the pages to manage genome people/couples, and to create genetic analyses
// viewAnalysisGui_Person.go and viewAnalysisGui_Couple.go implement the code to view genetic analyses

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

import "seekia/internal/appMemory"
import "seekia/internal/encoding"
import "seekia/internal/genetics/myGenomes"
import "seekia/internal/genetics/myPeople"
import "seekia/internal/genetics/myCouples"
import "seekia/internal/genetics/myAnalyses"
import "seekia/internal/genetics/readRawGenomes"
import "seekia/internal/genetics/sampleAnalyses"
import "seekia/internal/helpers"
import "seekia/internal/localFilesystem"

import "time"
import "strings"
import "errors"

func setGeneticsPage(window fyne.Window){

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

	title := getPageTitleCentered("Genetics")

	description1 := getLabelCentered("Seekia provides tools to analyze genomes.")
	description2 := getLabelCentered("You can import raw genome files from multiple sequencing companies.")
	description3 := getLabelCentered("You can share your genome information on your profile.")
	description4 := getLabelCentered("When an analysis is created, the genomes are never sent or shared anywhere.")

	managePeopleIcon, err := getFyneImageIcon("Person")
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}	
	managePeopleButton := widget.NewButton("Manage People", func(){
			setManageGenomePeoplePage(window, currentPage)
		})
	managePeopleButtonWithIcon := container.NewGridWithColumns(1, managePeopleIcon, managePeopleButton)	

	analyzeCoupleIcon, err := getFyneImageIcon("Couple")
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}
	analyzeCoupleButton := widget.NewButton("Analyze Couple", func(){
			setManageCouplesPage(window, currentPage)
		})
	analyzeCoupleButtonWithIcon := container.NewGridWithColumns(1, analyzeCoupleIcon, analyzeCoupleButton)

	buttonsRow := container.NewHBox(layout.NewSpacer(), managePeopleButtonWithIcon, analyzeCoupleButtonWithIcon, layout.NewSpacer())

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

	viewSampleAnalysesButton := widget.NewButton("View Sample Analyses", func(){setViewSampleGeneticAnalysesPage(window, currentPage)})
	viewSampleAnalysesButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, viewSampleAnalysesIcon, viewSampleAnalysesButton))

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

	setPageContent(page, window)
}

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

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

	title := getPageTitleCentered("Genetics - View Sample Analyses")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Seekia can create genetic analyses from imported genome files.")
	description2 := getLabelCentered("You can view example analyses on the pages below.")

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

	personAnalysisButton := widget.NewButton("Person Analysis", func(){

			personIdentifier := "111111111111111111111111111111"

			analysisObject, err := sampleAnalyses.GetSamplePerson1Analysis()
			if (err != nil) {
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisObject, 1, currentPage)
		})

	personAnalysisButtonWithIcon := container.NewGridWithColumns(1, personIcon, personAnalysisButton)	

	coupleIcon, err := getFyneImageIcon("Couple")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	coupleAnalysisButton := widget.NewButton("Couple Analysis", func(){

			person1Identifier := "111111111111111111111111111111"
			person2Identifier := "222222222222222222222222222222"

			person1AnalysisObject, err := sampleAnalyses.GetSamplePerson1Analysis()
			if (err != nil) {
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			person2AnalysisObject, err := sampleAnalyses.GetSamplePerson2Analysis()
			if (err != nil) {
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			coupleAnalysisObject, err := sampleAnalyses.GetSampleCoupleAnalysis()
			if (err != nil) {
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			setViewCoupleGeneticAnalysisPage(window, person1Identifier, person2Identifier, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, 1, 1, currentPage) 
		})

	coupleAnalysisButtonWithIcon := container.NewGridWithColumns(1, coupleIcon, coupleAnalysisButton)

	buttonsRow := container.NewHBox(layout.NewSpacer(), personAnalysisButtonWithIcon, coupleAnalysisButtonWithIcon, layout.NewSpacer())

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

	setPageContent(page, window)
}

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

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

	title := getPageTitleCentered("Genetics - Manage People")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("A Person is used to organize genome files.")
	description2 := getLabelCentered("You can import multiple genome files for the same person.")

	createPersonButton := getWidgetCentered(widget.NewButtonWithIcon("Create Person", theme.ContentAddIcon(), func(){
			setCreateGenomePersonPage(window, currentPage, currentPage)
		}))

	getPeopleContainer := func()(*fyne.Container, error){
		
		peopleMapList, err := myPeople.GetMyGenomePeopleMapList()
		if (err != nil) { return nil, err }

		if (len(peopleMapList) == 0){

			noPeopleExistDescription := getBoldLabelCentered("No people found.")
			return noPeopleExistDescription, nil
		}

		myPeopleLabel := getItalicLabelCentered("My People:")

		manageButtonsGrid := container.NewGridWithColumns(1)

		for _, personMap := range peopleMapList{

			personNameString, exists := personMap["PersonName"]
			if (exists == false) {
				return nil, errors.New("Malformed GenomePeople map list: Item missing PersonName.")
			}

			personIdentifier, exists := personMap["PersonIdentifier"]
			if (exists == false) {
				return nil, errors.New("Malformed GenomePeople map list: Item missing PersonIdentifier.")
			}

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

			managePersonButton := widget.NewButton(personNameTrimmed, func(){
					setManageGenomePersonPage(window, personIdentifier, currentPage)
				})

			manageButtonsGrid.Add(managePersonButton)
		}

		buttonsGridCentered := getContainerCentered(manageButtonsGrid)
		peopleContainer := container.NewVBox(myPeopleLabel, buttonsGridCentered)
	
		return peopleContainer, nil
	}

	peopleContainer, err := getPeopleContainer()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	setPageContent(page, window)
}



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

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

	title := getPageTitleCentered(translate("Genetics - Create Person"))

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Create a Person to manage genome files.")

	enterNameDescription := getBoldLabelCentered("Enter Person Name:")

	enterNameEntry := widget.NewEntry()
	enterNameEntry.SetPlaceHolder(translate("Enter Name..."))
	enterNameEntryBoxed := getWidgetBoxed(enterNameEntry)

	enterNameDescriptionWithEntry := getContainerCentered(container.NewGridWithColumns(1, enterNameDescription, enterNameEntryBoxed))

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

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

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

	selectSexLabel := getBoldLabelCentered(translate("Select Sex:"))
	sexSelector := widget.NewSelect(sexSelectorOptions, nil)
	sexSelector.SetSelectedIndex(0)
	sexSelectorCentered := getWidgetCentered(sexSelector)

	createPersonButton := getWidgetCentered(widget.NewButtonWithIcon("Create Person", theme.ConfirmIcon(), func(){

			newPersonName := enterNameEntry.Text
			if (newPersonName == ""){
				dialogTitle := translate("Missing Person Name.")
				dialogMessageA := getLabelCentered("You must enter a name for this person.")
				dialogContent := container.NewVBox(dialogMessageA)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}
			if (len(newPersonName) > 40){
				dialogTitle := translate("Name Is Too Long.")
				dialogMessageA := getLabelCentered("You must enter a name that is less than 40 characters.")
				dialogContent := container.NewVBox(dialogMessageA)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			personSexTranslated := sexSelector.Selected

			personSex, exists := untranslatedOptionsMap[personSexTranslated]
			if (exists == false) {
				setErrorEncounteredPage(window, errors.New("untranslatedOptionsMap missing personSexTranslated: " + personSexTranslated), currentPage)
				return
			}

			duplicateNameExists, err := myPeople.AddPerson(newPersonName, personSex)
			if (err != nil) {
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			if (duplicateNameExists == true){
				dialogTitle := translate("Invalid Person Name.")
				dialogMessageA := getLabelCentered("A Person with this name already exists.")
				dialogMessageB := getLabelCentered("Enter a different name for the Person.")
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			nextPage()
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, widget.NewSeparator(), enterNameDescriptionWithEntry, selectSexLabel, sexSelectorCentered, createPersonButton)

	setPageContent(page, window)
}

// This is a page to manage a genome Person
func setManageGenomePersonPage(window fyne.Window, personIdentifier string, previousPage func()){

	currentPage := func(){setManageGenomePersonPage(window, personIdentifier, previousPage)}

	title := getPageTitleCentered("Genetics - Manage Person")

	backButton := getBackButtonCentered(previousPage)

	personFound, personName, _, personSex, err := myPeople.GetPersonInfo(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (personFound == false){
		setErrorEncounteredPage(window, errors.New("setManageGenomePersonPage called with missing personIdentifier"), previousPage)
		return
	}

	personNameLabel := getLabelCentered("Person Name:")
	personNameText := getBoldLabelCentered(personName)

	personSexLabel := getLabelCentered("Person Sex:")
	personSexText := getBoldLabelCentered(personSex)
	
	analyzeIcon, err := getFyneImageIcon("Stats")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	analyzeGeneticsButton := widget.NewButton("Analyze Genetics", func(){
			setAnalyzePersonGeneticsPage(window, personIdentifier, currentPage)
		})
	analyzeGeneticsButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, analyzeIcon, analyzeGeneticsButton))
	
	genomesIcon, err := getFyneImageIcon("Genome")
	manageGenomesButton := widget.NewButton("Manage Genomes", func(){
			setManagePersonGenomesPage(window, personIdentifier, currentPage)
		})
	manageGenomesButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, genomesIcon, manageGenomesButton))

	renamePersonButton := widget.NewButtonWithIcon("Rename", theme.DocumentCreateIcon(), func(){
			setRenameGenomePersonPage(window, personIdentifier, currentPage, currentPage)
		})
	changeSexButton := widget.NewButtonWithIcon("Change Sex", theme.DocumentCreateIcon(), func(){
			//TODO
			showUnderConstructionDialog(window)
		})
	deletePersonButton := widget.NewButtonWithIcon("Delete", theme.DeleteIcon(), func(){
			setDeleteGenomePersonPage(window, personIdentifier, currentPage, previousPage)
		})

	buttonsGrid := getContainerCentered(container.NewGridWithColumns(1, renamePersonButton, changeSexButton, deletePersonButton))
	
	page := container.NewVBox(title, backButton, widget.NewSeparator(), personNameLabel, personNameText, widget.NewSeparator(), personSexLabel,  personSexText, widget.NewSeparator(), analyzeGeneticsButtonWithIcon, manageGenomesButtonWithIcon, widget.NewSeparator(), buttonsGrid)

	setPageContent(page, window)
}

func setRenameGenomePersonPage(window fyne.Window, personIdentifier string, previousPage func(), nextPage func()){

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

	title := getPageTitleCentered("Genetics - Rename Person")

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered("Rename Person")

	personFound, personName, _, personSex, err := myPeople.GetPersonInfo(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (personFound == false){
		setErrorEncounteredPage(window, errors.New("setRenameGenomePersonPage called with person who does not exist."), previousPage)
		return
	}

	currentNameLabel := widget.NewLabel("Current Name:")
	currentNameText := getBoldLabel(personName)

	currentNameRow := container.NewHBox(layout.NewSpacer(), currentNameLabel, currentNameText, layout.NewSpacer())

	enterNameDescription := widget.NewLabel("Enter new name:")

	newNameEntry := widget.NewEntry()
	newNameEntry.SetPlaceHolder("Enter name...")

	enterNameEntryWithDescription := getContainerCentered(container.NewGridWithColumns(1, enterNameDescription, newNameEntry))
	
	renameButton := getWidgetCentered(widget.NewButtonWithIcon("Rename", theme.ConfirmIcon(), func(){
			newName := newNameEntry.Text
			if (newName == ""){
				dialogTitle := translate("No Name Provided.")
				dialogMessageA := getLabelCentered("You must enter a new name.")
				dialogContent := container.NewVBox(dialogMessageA)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}
			if (len(newName) > 40){
				dialogTitle := translate("Name Is Too Long.")
				dialogMessageA := getLabelCentered("You must enter a name that is less than 40 characters.")
				dialogContent := container.NewVBox(dialogMessageA)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			nameIsDuplicate, err := myPeople.EditPerson(personIdentifier, newName, personSex)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			if (nameIsDuplicate == true){
				dialogTitle := translate("Name Already Exists.")
				dialogMessageA := getLabelCentered("Another person already has this name.")
				dialogMessageB := getLabelCentered("You must enter a unique name.")
				dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}
			nextPage()
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), subtitle, widget.NewSeparator(), currentNameRow, widget.NewSeparator(), enterNameEntryWithDescription, renameButton) 

	setPageContent(page, window)
}

func setDeleteGenomePersonPage(window fyne.Window, personIdentifier string, previousPage func(), nextPage func()){

	currentPage := func(){setDeleteGenomePersonPage(window, personIdentifier, previousPage, nextPage)}
	
	title := getPageTitleCentered("Genetics - Delete Person")

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered("Delete Person")

	personFound, personName, personCreatedTime, _, err := myPeople.GetPersonInfo(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (personFound == false){
		setErrorEncounteredPage(window, errors.New("setDeleteGenomePersonPage called with unknown person."), previousPage)
		return
	}

	description1 := getLabelCentered("Confirm to delete " + personName + "?")

	createdTimeAgoText, err := helpers.ConvertUnixTimeToTimeAgoTranslated(personCreatedTime, false)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	
	description2 := getItalicLabelCentered("Person created " + createdTimeAgoText + ".")

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

	//Outputs:
	//	-bool: Any couples/genomes exist for this person
	//	-*fyne.Container: label describing the couples and genomes that will be deleted
	//	-error
	getPersonGenomesAndCouplesLabel := func()(bool, *fyne.Container, error){

		allPersonGenomesList, err := myGenomes.GetAllPersonGenomesMapList(personIdentifier)
		if (err != nil) { return false, nil, err }

		numberOfPersonGenomes := len(allPersonGenomesList)

		numberOfPersonCouples, err := myCouples.GetNumberOfCouplesForPerson(personIdentifier)
		if (err != nil){ return false, nil, err }

		if (numberOfPersonGenomes == 0 && numberOfPersonCouples == 0){
			return false, nil, nil
		}

		numberOfPersonGenomesString := helpers.ConvertIntToString(numberOfPersonGenomes)
		numberOfPersonCouplesString := helpers.ConvertIntToString(numberOfPersonCouples)

		getGenomeOrGenomesText := func()string{
			if (numberOfPersonGenomes == 1){
				result := translate("genome")
				return result
			}
			result := translate("genomes")
			return result		
		}

		genomeOrGenomesText := getGenomeOrGenomesText()

		getCoupleOrCouplesText := func()string{

			if (numberOfPersonCouples == 1){
				result := translate("couple")
				return result			
			}
	
			result := translate("couples")
			return result		
		}

		coupleOrCouplesText := getCoupleOrCouplesText()

		if (numberOfPersonGenomes != 0 && numberOfPersonCouples != 0){

			genomesAndCouplesText := numberOfPersonGenomesString + " " + genomeOrGenomesText + " & " + numberOfPersonCouplesString + " " + coupleOrCouplesText + "."
			genomesAndCouplesLabel := getBoldLabelCentered(genomesAndCouplesText)
			return true, genomesAndCouplesLabel, nil
		}		
		
		if (numberOfPersonGenomes != 0){

			genomesText := numberOfPersonGenomesString + " " + genomeOrGenomesText + "."
			genomesLabel := getBoldLabelCentered(genomesText)
			return true, genomesLabel, nil
		}

		couplesText := numberOfPersonCouplesString + " " + coupleOrCouplesText + "."
		couplesLabel := getBoldLabelCentered(couplesText)
		return true, couplesLabel, nil
	}

	anyGenomesOrCouplesExist, personGenomesAndCouplesLabel, err := getPersonGenomesAndCouplesLabel()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	if (anyGenomesOrCouplesExist == true){

		deletionDescription := getLabelCentered("Deleting this person will delete:")

		page.Add(widget.NewSeparator())
		page.Add(deletionDescription) 
		page.Add(personGenomesAndCouplesLabel)
		page.Add(widget.NewSeparator())
	}

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

			err := myPeople.DeletePerson(personIdentifier)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
		
			nextPage()
		}))

	page.Add(deleteButton)

	setPageContent(page, window)
}

// This function provides a page to manage a Person's genomes
func setManagePersonGenomesPage(window fyne.Window, personIdentifier string, previousPage func()){

	currentPage := func(){setManagePersonGenomesPage(window, personIdentifier, previousPage)}

	title := getPageTitleCentered("Genetics - Manage Person Genomes")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Manage the person's genome files below.")

	importGenomeButton := getWidgetCentered(widget.NewButtonWithIcon("Import Genome", theme.ContentAddIcon(), func(){
			setImportRawGenomePage(window, personIdentifier, currentPage, currentPage)
		}))

	getPersonGenomesContainer := func()(*fyne.Container, error){
		
		allGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(personIdentifier)
		if (err != nil){ return nil, err }

		if (len(allGenomesMapList) == 0){
			noGenomesExistLabel := getBoldLabelCentered("No genomes exist.")
			return noGenomesExistLabel, nil
		}

		indexColumn := container.NewVBox()
		genomeNameColumn := container.NewVBox()
		manageButtonColumn := container.NewVBox()

		for index, genomeMap := range allGenomesMapList{

			indexString := helpers.ConvertIntToString(index+1)
			indexLabel := getBoldLabel(indexString + ".")
	
			companyName, exists := genomeMap["CompanyName"]
			if (exists == false){
				return nil, errors.New("Malformed myGenomesMapList: Item missing CompanyName")
			}

			companyNameLabel := widget.NewLabel(companyName)

			genomeIdentifierHex, exists := genomeMap["GenomeIdentifier"]
			if (exists == false){
				return nil, errors.New("Malformed myGenomesMapList: Item missing GenomeIdentifier")
			}

			genomeIdentifier, err := encoding.DecodeHexStringTo16ByteArray(genomeIdentifierHex)
			if (err != nil){
				return nil, errors.New("Malformed myGenomesMapList: Item contains invalid GenomeIdentifier: " + genomeIdentifierHex)
			}

			manageButton := widget.NewButtonWithIcon("Manage", theme.VisibilityIcon(), func(){
					setManageGenomePage(window, genomeIdentifier, currentPage)
				})

			indexColumn.Add(indexLabel)	
			genomeNameColumn.Add(companyNameLabel)
			manageButtonColumn.Add(manageButton)

			if (index != len(allGenomesMapList)-1){
				indexColumn.Add(widget.NewSeparator())
				genomeNameColumn.Add(widget.NewSeparator())
				manageButtonColumn.Add(widget.NewSeparator())
			}
		}
		
		genomesContainer := container.NewHBox(layout.NewSpacer(), indexColumn, genomeNameColumn, manageButtonColumn, layout.NewSpacer())
		
		return genomesContainer, nil
	}

	personGenomesContainer, err := getPersonGenomesContainer()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	setPageContent(page, window)
}

func setImportRawGenomePage(window fyne.Window, personIdentifier string, previousPage func(), nextPage func()){

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

	title := getPageTitleCentered("Import Raw Genome")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Import a raw genome file below.")
	description2 := getLabelCentered("You must export your raw data from a sequencing company.")
	description3 := getLabelCentered("Supported Companies: 23andMe, AncestryDNA")

	selectFileButton := getWidgetCentered(widget.NewButtonWithIcon("Select File", theme.FileIcon(), func(){

			openFileCallbackFunction := func(file fyne.URIReadCloser, err error){
				if (err != nil){
					setErrorEncounteredPage(window, err, currentPage)
					return
				}
				if (file == nil){
					return
				}

				uriFilePath := file.URI().String()

				filePath := strings.TrimPrefix(uriFilePath, "file://")

				setLoadingScreen(window, "Importing Genome", "Importing genome file...")

				fileExists, fileBytes, err := localFilesystem.GetFileContents(filePath)
				if (err != nil){
					setErrorEncounteredPage(window, err, currentPage)
					return				
				}
				if (fileExists == false){
					setErrorEncounteredPage(window, errors.New("Unable to read chosen file: File not found."), currentPage)
					return
				}

				fileString := string(fileBytes)

				fileIsValid, fileAlreadyExists, err := myGenomes.AddRawGenome(personIdentifier, fileString)
				if (err != nil){
					setErrorEncounteredPage(window, err, currentPage)
					return
				}
				if (fileIsValid == false){
					currentPage()
					dialogTitle := translate("Unable To Read File.")
					dialogMessageA := getLabelCentered("Seekia was unable to read the selected genome file.")
					dialogMessageB := getLabelCentered("Only 23andMe and AncestryDNA files are supported.")
					dialogMessageC := getLabelCentered("More companies will be supported in the future.")
					dialogContent := container.NewVBox(dialogMessageA, dialogMessageB, dialogMessageC)
					dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
					return
				}
				if (fileAlreadyExists == true){
					currentPage()
					dialogTitle := translate("Genome Already Exists.")
					dialogMessageA := getLabelCentered("The genome file you selected is already imported.")
					dialogMessageB := getLabelCentered("You can only import the same file once for each Person.")
					dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
					dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
					return
				}
				nextPage()
			}
			dialog.ShowFileOpen(openFileCallbackFunction, window)
		}))

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

	setPageContent(page, window)
}

// This provides a page to manage a person's Genome
func setManageGenomePage(window fyne.Window, genomeIdentifier [16]byte, previousPage func()){

	currentPage := func(){setManageGenomePage(window, genomeIdentifier, previousPage)}

	setLoadingScreen(window, "Loading Genome", "Loading genome...")

	title := getPageTitleCentered("Genetics - Manage Genome")
	
	backButton := getBackButtonCentered(previousPage)

	genomeFound, personIdentifier, exportTime, importTime, isPhased, snpCount, companyName, importVersion, fileHash, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (genomeFound == false){
		setErrorEncounteredPage(window, errors.New("setManageGenomePage called with missing genomeIdentifier"), previousPage)
		return
	}
	currentImportVersion, err := readRawGenomes.GetCurrentCompanyImportVersion(companyName)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (importVersion != currentImportVersion){
		// We will refresh the metadata for the genome
		err := myGenomes.RefreshRawGenomeMetadata(genomeIdentifier)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}
		currentPage()
		return
	}

	isPhasedString := helpers.ConvertBoolToYesOrNoString(isPhased)
	snpCountString := helpers.ConvertInt64ToString(snpCount)

	companyLabel := widget.NewLabel("Company:")
	companyNameLabel := getBoldLabel(companyName)

	companyNameRow := container.NewHBox(layout.NewSpacer(), companyLabel, companyNameLabel, layout.NewSpacer())

	snpCountLabel := widget.NewLabel("SNP Count:")
	snpCountText := getBoldLabel(snpCountString)
	snpCountHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
			setGenomeSNPCountExplainerPage(window, currentPage)
		})

	snpCountRow := container.NewHBox(layout.NewSpacer(), snpCountLabel, snpCountText, snpCountHelpButton, layout.NewSpacer())

	isPhasedLabel := widget.NewLabel("Is Phased:")
	isPhasedText := getBoldLabel(isPhasedString)
	isPhasedHelpButton := widget.NewButtonWithIcon("", theme.QuestionIcon(), func(){
			setGenomePhasingExplainerPage(window, currentPage)
		})

	isPhasedRow := container.NewHBox(layout.NewSpacer(), isPhasedLabel, isPhasedText, isPhasedHelpButton, layout.NewSpacer())

	fileHashTrimmed, _, err := helpers.TrimAndFlattenString(fileHash, 6)
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	fileHashLabel := widget.NewLabel("File Hash: ")
	fileHashText := getBoldLabel(fileHashTrimmed)
	fileHashRow := container.NewHBox(layout.NewSpacer(), fileHashLabel, fileHashText, layout.NewSpacer())

	exportTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(exportTime, false)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	exportedTimeLabel := getItalicLabelCentered("Exported from " + companyName + " " + exportTimeAgo + ".")

	importedTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(importTime, false)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	importedTimeLabel := getItalicLabelCentered("Imported " + importedTimeAgo + ".")

	deleteGenomeButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Genome", theme.DeleteIcon(), func(){
			setConfirmDeletePersonGenomePage(window, personIdentifier, genomeIdentifier, currentPage, previousPage)
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), companyNameRow, snpCountRow, isPhasedRow, fileHashRow, widget.NewSeparator(), exportedTimeLabel, importedTimeLabel, widget.NewSeparator(), deleteGenomeButton)

	setPageContent(page, window)
}

func setConfirmDeletePersonGenomePage(window fyne.Window, personIdentifier string, genomeIdentifier [16]byte, previousPage func(), nextPage func()){

	currentPage := func(){setConfirmDeletePersonGenomePage(window, personIdentifier, genomeIdentifier, previousPage, nextPage)}

	title := getPageTitleCentered("Confirm Delete Genome")

	backButton := getBackButtonCentered(previousPage)

	description1 := getBoldLabelCentered("Delete Genome?")

	description2 := getLabelCentered("This will delete this person's raw genome.")
	description3 := getLabelCentered("You must run a new genetic analysis afterwards.")
	
	genomeFound, genomePersonIdentifier, timeGenomeWasExported, timeGenomeWasImported, _, _, companyName, _, _, err := myGenomes.GetMyRawGenomeMetadata(genomeIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (genomeFound == false){
		setErrorEncounteredPage(window, errors.New("setDeletePersonGenomePage called with missing genome."), previousPage)
		return
	}
	if (genomePersonIdentifier != personIdentifier){
		setErrorEncounteredPage(window, errors.New("Cannot delete genome: Genome person identifier does not match."), previousPage)
		return
	}

	exportTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeGenomeWasExported, false)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	exportedTimeLabel := getItalicLabelCentered("Exported from " + companyName + " " + exportTimeAgo + ".")

	importedTimeAgo, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeGenomeWasImported, false)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	importedTimeLabel := getItalicLabelCentered("Imported " + importedTimeAgo + ".")

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

			err := myGenomes.DeleteMyRawGenome(genomeIdentifier)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			nextPage()
		}))

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

	setPageContent(page, window)
}

func setAnalyzePersonGeneticsPage(window fyne.Window, personIdentifier string, previousPage func()){

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

	currentPage := func(){setAnalyzePersonGeneticsPage(window, personIdentifier, previousPage)}

	title := getPageTitleCentered(translate("Genetics - Analyze Person Genetics"))

	backButton := getBackButtonCentered(previousPage)

	description := getLabelCentered(translate("Analyze this person's genetics."))

	personFound, personName, _, _, err := myPeople.GetPersonInfo(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (personFound == false){
		setErrorEncounteredPage(window, errors.New("setAnalyzePersonGeneticsPage called with missing person"), previousPage)
		return
	}

	allPersonRawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (len(allPersonRawGenomeIdentifiersList) == 0){
		noGenomesExistLabel := getBoldLabelCentered("No genomes found.")
		description2 := getLabelCentered("You must import a genome to perform a genetic analysis.")
		description3 := getLabelCentered("Go to the previous page and select Manage Genomes.")
		page := container.NewVBox(title, backButton, widget.NewSeparator(), noGenomesExistLabel, description2, description3)
		setPageContent(page, window)
		return
	}

	anyAnalysisFound, newestAnalysisIdentifier, timeOfNewestAnalysis, newestAnalysisListOfGenomesAnalyzed, newerAnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (anyAnalysisFound == false){
		// We will skip to the ConfirmPerformAnalysis page
		setConfirmPerformPersonAnalysisPage(window, personIdentifier, previousPage, currentPage)
		return
	}
	if (newerAnalysisVersionAvailable == true){

		description1 := getLabelCentered("A new analysis method is available!")
		description2 := getLabelCentered("You must perform a new analysis to see your new results.")
		
		performNewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform New Analysis", theme.NavigateNextIcon(), func(){
				setConfirmPerformPersonAnalysisPage(window, personIdentifier, currentPage, currentPage)
			}))

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

		setPageContent(page, window)
		return
	}

	// This will return true if a newer analysis can be performed using newly imported genomes
	getAnalysisIsMissingGenomesBool := func()(bool, error){

		genomesAreIdentical := helpers.CheckIfTwoListsContainIdenticalItems(allPersonRawGenomeIdentifiersList, newestAnalysisListOfGenomesAnalyzed)
		if (genomesAreIdentical == false){
			return true, nil	
		}
		return false, nil
	}

	analysisIsMissingGenomes, err := getAnalysisIsMissingGenomesBool()
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (analysisIsMissingGenomes == true){

		description1 := getLabelCentered("You have imported/deleted a genome.")
		description2 := getLabelCentered("You must perform a new analysis.")
		performAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform Analysis", theme.NavigateNextIcon(), func(){
				setConfirmPerformPersonAnalysisPage(window, personIdentifier, currentPage, currentPage)
			}))

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

	analysisReadyLabel := getBoldLabelCentered("Genetic Analysis Complete!")

	personAnalyzedLabel := widget.NewLabel("Person Analyzed:")
	personNameLabel := getBoldLabel(personName)
	personAnalyzedRow := container.NewHBox(layout.NewSpacer(), personAnalyzedLabel, personNameLabel, layout.NewSpacer())

	numberOfGenomesInAnalysis := len(newestAnalysisListOfGenomesAnalyzed)

	numberOfGenomesInAnalysisString := helpers.ConvertIntToString(numberOfGenomesInAnalysis)

	numberOfGenomesInAnalysisLabel := widget.NewLabel("Number of genomes analyzed:")
	numberOfGenomesInAnalysisText := getBoldLabel(numberOfGenomesInAnalysisString)
	numberOfGenomesInAnalysisRow := container.NewHBox(layout.NewSpacer(), numberOfGenomesInAnalysisLabel, numberOfGenomesInAnalysisText, layout.NewSpacer())

	timeAgoPerformed, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeOfNewestAnalysis, false)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	timeAgoPerformedLabel := getItalicLabelCentered("Analysis performed " + timeAgoPerformed + ".") 

	viewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("View Analysis", theme.VisibilityIcon(), func(){

			analysisFound, analysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestAnalysisIdentifier)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			if (analysisFound == false){
				setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage)
				return
			}

			setViewPersonGeneticAnalysisPage(window, personIdentifier, analysisObject, numberOfGenomesInAnalysis, currentPage)
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), analysisReadyLabel, personAnalyzedRow, numberOfGenomesInAnalysisRow, timeAgoPerformedLabel, viewAnalysisButton)

	setPageContent(page, window)
}


func setConfirmPerformPersonAnalysisPage(window fyne.Window, personIdentifier string, previousPage func(), pageToVisitAfter func()){

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

	currentPage := func(){setConfirmPerformPersonAnalysisPage(window, personIdentifier, previousPage, pageToVisitAfter)}

	title := getPageTitleCentered("Genetics - Perform Analysis")

	backButton := getBackButtonCentered(previousPage)

	allPersonGenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	numberOfPersonGenomes := len(allPersonGenomesMapList)

	if (numberOfPersonGenomes == 0){
		setErrorEncounteredPage(window, errors.New("setConfirmPerformPersonAnalysisPage called with person who has no genomes."), previousPage)
		return
	}

	// Now we check if there is an analysis running

	anyProcessFound, processIdentifier, err := myAnalyses.GetPersonGeneticAnalysisProcessIdentifier(personIdentifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (anyProcessFound == true){

		processFound, processIsComplete, _, _, _, err := myAnalyses.GetAnalysisProcessInfo(processIdentifier)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}
		if (processFound == false){
			setErrorEncounteredPage(window, errors.New("Genetic analysis process not found after being found already."), previousPage)
			return
		}
		if (processIsComplete == false){

			// An analysis is already running.

			description1 := getLabelCentered("A genetic analysis is already being generated.")
			description2 := getLabelCentered("View the status of the analysis?")

			viewStatusButton := getWidgetCentered(widget.NewButtonWithIcon("View Status", theme.VisibilityIcon(), func(){
					setMonitorGeneticAnalysisGenerationPage(window, processIdentifier, currentPage, pageToVisitAfter)
				}))

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

		// Existing analysis probably encountered error.
		// We will just show a retry option.
	}

	description1 := getBoldLabelCentered("Perform genetic analysis?")
	description2 := getLabelCentered("This will create an analysis of all of this person's genomes.")
	description3 := getLabelCentered("It is performed offline and is not shared without your approval.")

	startAnalysisFunction := func(){

		newProcessIdentifier, err := myAnalyses.StartCreateNewPersonGeneticAnalysis(personIdentifier)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}

		setMonitorGeneticAnalysisGenerationPage(window, newProcessIdentifier, currentPage, pageToVisitAfter)
	}

	startAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Start Analysis", theme.ConfirmIcon(), startAnalysisFunction))

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

	setPageContent(page, window)
}

// This function provides a page to monitor an active analysis generation
func setMonitorGeneticAnalysisGenerationPage(window fyne.Window, processIdentifier string, previousPage func(), pageToVisitAfter func()){

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

	appMemory.SetMemoryEntry("CurrentViewedPage", pageIdentifier)

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

	title := getPageTitleCentered("Monitor Genetic Analysis Generation")

	backButton := getBackButtonCentered(previousPage)

	processDetailsABinding := binding.NewString()
	processDetailsBBinding := binding.NewString()
	progressPercentageBinding := binding.NewFloat()

	processDetailsABinding.Set("Genetic analysis is being generated...")
	processDetailsBBinding.Set("You can leave this page.")

	processDetailsALabel := widget.NewLabelWithData(processDetailsABinding)
	processDetailsALabel.TextStyle = getFyneTextStyle_Bold()
	processDetailsBLabel := widget.NewLabelWithData(processDetailsBBinding)

	processDetailsALabelCentered := getWidgetCentered(processDetailsALabel)
	processDetailsBLabelCentered := getWidgetCentered(processDetailsBLabel)

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

	updateBindingsFunction := func(){

		for{

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

			processFound, processIsComplete, processEncounteredError, errorEncounteredByProcess, processPercentageComplete, err := myAnalyses.GetAnalysisProcessInfo(processIdentifier)
			if (err != nil){
				fyne.Do(func(){setErrorEncounteredPage(window, err, previousPage)})
				return
			}
			if (processFound == false){
				fyne.Do(func(){setErrorEncounteredPage(window, errors.New("setMonitorGeneticAnalysisGenerationPage called with missing process"), previousPage)})
				return
			}
			if (processIsComplete == false){
				percentageCompleteFloat := float64(processPercentageComplete)/100
				progressPercentageBinding.Set(percentageCompleteFloat)
				time.Sleep(time.Millisecond * 50)
				continue
			}

			// Process is complete

			progressPercentageBinding.Set(1)

			if (processEncounteredError == true){
				processDetailsABinding.Set("Process Encountered Error!")
				processDetailsBBinding.Set("ERROR: " + errorEncounteredByProcess.Error())
				return
			}

			fyne.Do(pageToVisitAfter)
			return
		}
	}

	page := container.NewVBox(title, backButton, widget.NewSeparator(), processDetailsALabelCentered, processDetailsBLabelCentered, loadingBar)

	setPageContent(page, window)

	go updateBindingsFunction()
}


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

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

	title := getPageTitleCentered("Genetics - Manage Couples")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Perform a genetic analysis of a couple.")

	createACoupleButton := getWidgetCentered(widget.NewButtonWithIcon("Create Couple", theme.ContentAddIcon(), func(){
			setCreateCouplePage(window, currentPage)
		}))

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

		myCouplesMapList, err := myCouples.GetMyGenomeCouplesMapList()
		if (err != nil){ return nil, err }

		if (len(myCouplesMapList) == 0){

			noCouplesExistLabel := getBoldLabelCentered("No couples exist.")

			return noCouplesExistLabel, nil
		}

		myCouplesText := getItalicLabelCentered("My Couples:")

		manageButtonsGrid := container.NewGridWithColumns(1)

		for _, coupleMap := range myCouplesMapList{

			person1Identifier, exists := coupleMap["Person1Identifier"]
			if (exists == false) {
				return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing Person1Identifier")
			}

			person2Identifier, exists := coupleMap["Person2Identifier"]
			if (exists == false) {
				return nil, errors.New("Malformed myCoupleAnalysesMapList: Item missing Person2Identifier")
			}

			personFound, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
			if (err != nil) { return nil, err }
			if (personFound == false){
				return nil, errors.New("Couple person not found.")
			} 

			personFound, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
			if (err != nil) { return nil, err }
			if (personFound == false){
				return nil, errors.New("Couple person not found.")
			} 

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

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

			coupleName := person1NameTrimmed + " + " + person2NameTrimmed

			manageCoupleButton := widget.NewButton(coupleName, func(){
					setManageCouplePage(window, person1Identifier, person2Identifier, currentPage)
				})

			manageButtonsGrid.Add(manageCoupleButton)
		}

		manageButtonsGridCentered := getContainerCentered(manageButtonsGrid)

		manageMyCouplesContainer := container.NewVBox(myCouplesText, manageButtonsGridCentered)

		return manageMyCouplesContainer, nil
	}

	manageMyCouplesContainer, err := getManageMyCouplesContainer()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

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

	setPageContent(page, window)
}


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

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

	title := getPageTitleCentered("Genetics - Create Couple")

	backButton := getBackButtonCentered(previousPage)

	myGenomePeopleMapList, err := myPeople.GetMyGenomePeopleMapList()
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	if (len(myGenomePeopleMapList) < 2){

		description1 := getLabelCentered("You have created fewer than 2 people.")
		description2 := getLabelCentered("You must create at least 2 people to create a couple.")
		description3 := getLabelCentered("Create a new person on the Genetics - Manage People page.")

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

		setPageContent(page, window)
		return		
	}

	description1 := getLabelCentered("Choose 2 people to pair below.")
	description2 := getLabelCentered("You can create new people on the Manage People page.")

	selectedPeopleListBinding := binding.NewStringList()

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

		getNumberOfGridColumns := func()int{
			if (len(myGenomePeopleMapList) == 2){
				return 2
			}
			return 3
		}

		numberOfGridColumns := getNumberOfGridColumns()

		choosePeopleGrid := container.NewGridWithColumns(numberOfGridColumns)

		for _, personMap := range myGenomePeopleMapList{

			personIdentifier, exists := personMap["PersonIdentifier"]
			if (exists == false){
				return nil, errors.New("myGenomePeopleMapList contains item missing PersonIdentifier")
			}

			personName, exists := personMap["PersonName"]
			if (exists == false){
				return nil, errors.New("myGenomePeopleMapList contains item missing PersonName")
			}

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

			personCheck := widget.NewCheck(personNameTrimmed, func(response bool){
					if (response == true){
						err := selectedPeopleListBinding.Append(personIdentifier)
						if (err != nil){
							setErrorEncounteredPage(window, err, currentPage)
						}			
						return
					}

					existingList, err := selectedPeopleListBinding.Get()
					if (err != nil){
						setErrorEncounteredPage(window, err, currentPage)
						return
					}

					newList, deletedAny := helpers.DeleteAllMatchingItemsFromList(existingList, personIdentifier)
					if (deletedAny == false){
						setErrorEncounteredPage(window, errors.New("Person not found when trying to delete person from chosen people list."), currentPage)
						return
					}
					err = selectedPeopleListBinding.Set(newList)
					if (err != nil){
						setErrorEncounteredPage(window, err, currentPage)
					}
				})

			personCheckBoxed := getWidgetBoxed(personCheck)

			choosePeopleGrid.Add(personCheckBoxed)
		}

		choosePeopleGridCentered := getContainerCentered(choosePeopleGrid)

		return choosePeopleGridCentered, nil
	}

	choosePeopleGrid, err := getChoosePeopleGrid()
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	createCoupleButton := getWidgetCentered(widget.NewButtonWithIcon("Create Couple", theme.ConfirmIcon(), func(){

			selectedPeopleList, err := selectedPeopleListBinding.Get()
			if (err != nil){
				setErrorEncounteredPage(window, err, previousPage)
				return
			}
			if (len(selectedPeopleList) < 2){

				dialogTitle := translate("Not Enough People Selected.")
				dialogMessageA := getLabelCentered("You must select two people to create a couple.")
				dialogContent := container.NewVBox(dialogMessageA)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}
			if (len(selectedPeopleList) > 2){

				dialogTitle := translate("Too Many People Selected.")
				dialogMessageA := getLabelCentered("You must select two people to create a couple.")
				dialogContent := container.NewVBox(dialogMessageA)
				dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
				return
			}

			person1Identifier := selectedPeopleList[0]
			person2Identifier := selectedPeopleList[1]

			_, err = myCouples.AddCouple(person1Identifier, person2Identifier)
			if (err != nil) {
				setErrorEncounteredPage(window, err, previousPage)
				return
			}

			setManageCouplePage(window, person1Identifier, person2Identifier, previousPage)
		}))

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

	setPageContent(page, window)
}


func setManageCouplePage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func()){

	currentPage := func(){setManageCouplePage(window, person1Identifier, person2Identifier, previousPage)}

	title := getPageTitleCentered("Genetics - Manage Couple")

	backButton := getBackButtonCentered(previousPage)

	person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (person1Found == false){
		setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown person1Identifier"), previousPage)
		return
	}

	person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (person2Found == false){
		setErrorEncounteredPage(window, errors.New("setManageCouplePage called with unknown person2Identifier"), previousPage)
		return
	}

	coupleNameLabel := getLabelCentered("Couple Name:")
	coupleNameText := getBoldLabelCentered(person1Name + " + " + person2Name)

	analyzeIcon, err := getFyneImageIcon("Stats")
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	analyzeGeneticsButton := widget.NewButton("Analyze Genetics", func(){
			setAnalyzeCoupleGeneticsPage(window, person1Identifier, person2Identifier, currentPage)
		})
	analyzeGeneticsButtonWithIcon := getContainerCentered(container.NewGridWithColumns(1, analyzeIcon, analyzeGeneticsButton))

	deleteCoupleButton := getWidgetCentered(widget.NewButtonWithIcon("Delete Couple", theme.DeleteIcon(), func(){
			setDeleteCouplePage(window, person1Identifier, person2Identifier, currentPage, previousPage)
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), coupleNameLabel, coupleNameText, widget.NewSeparator(), analyzeGeneticsButtonWithIcon, widget.NewSeparator(), deleteCoupleButton)

	setPageContent(page, window)
}

func setDeleteCouplePage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func(), nextPage func()){

	currentPage := func(){setDeleteCouplePage(window, person1Identifier, person2Identifier, previousPage, nextPage)}

	title := getPageTitleCentered("Genetics - Delete Couple")

	backButton := getBackButtonCentered(previousPage)

	subtitle := getPageSubtitleCentered("Delete Couple")

	person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (person1Found == false){
		setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown person1Identifier"), previousPage)
		return
	}

	person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (person2Found == false){
		setErrorEncounteredPage(window, errors.New("setDeleteGenomeCouplePage called with unknown person2Identifier"), previousPage)
		return
	}

	description := getLabelCentered("Confirm to delete this couple?")

	coupleName := person1Name + " + " + person2Name	

	coupleNameLabel := widget.NewLabel("Couple Name:")
	coupleNameText := getBoldLabel(coupleName)

	coupleNameRow := container.NewHBox(layout.NewSpacer(), coupleNameLabel, coupleNameText, layout.NewSpacer())

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

			err := myCouples.DeleteCouple(person1Identifier, person2Identifier)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}

			nextPage()
		}))

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

	setPageContent(page, window)
}


func setAnalyzeCoupleGeneticsPage(window fyne.Window, inputPerson1Identifier string, inputPerson2Identifier string, previousPage func()){

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

	currentPage := func(){setAnalyzeCoupleGeneticsPage(window, inputPerson1Identifier, inputPerson2Identifier, previousPage)}

	// We have to sort the person identifiers so they are in the same order as they exist in the couple analysis

	person1Identifier, person2Identifier, err := myAnalyses.GetPeopleIdentifiersSortedForCouple(inputPerson1Identifier, inputPerson2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	title := getPageTitleCentered("Genetics - Analyze Couple Genetics")

	backButton := getBackButtonCentered(previousPage)

	description := getLabelCentered("Analyze this couple's genetics.")

	person1Found, person1Name, _, _, err := myPeople.GetPersonInfo(person1Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (person1Found == false){
		setErrorEncounteredPage(window, errors.New("setAnalyzeCoupleGeneticsPage called with missing person"), previousPage)
		return
	}

	person2Found, person2Name, _, _, err := myPeople.GetPersonInfo(person2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (person2Found == false){
		setErrorEncounteredPage(window, errors.New("setAnalyzeCoupleGeneticsPage called with missing person"), previousPage)
		return
	}

	allPerson1RawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(person1Identifier)
	if (err != nil) { 
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	allPerson2RawGenomeIdentifiersList, err := myGenomes.GetAllPersonRawGenomeIdentifiersList(person2Identifier)
	if (err != nil) { 
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	if (len(allPerson1RawGenomeIdentifiersList) == 0 || len(allPerson2RawGenomeIdentifiersList) == 0){
		// At least one of the people is missing genomes
		
		getGenomesMissingText := func()string{
			if (len(allPerson1RawGenomeIdentifiersList) == 0 && len(allPerson2RawGenomeIdentifiersList) == 0){
				missingLabelText := person1Name + " and " + person2Name + " have no genomes."
				return missingLabelText
			}
			if (len(allPerson1RawGenomeIdentifiersList) == 0 && len(allPerson2RawGenomeIdentifiersList) != 0){

				missingLabelText := person1Name + " has no genomes."
				return missingLabelText
			}
			// len(allPerson1RawGenomeIdentifiersList) != 0 && len(allPerson2RawGenomeIdentifiersList) == 0){

			missingLabelText := person2Name + " has no genomes."
			return missingLabelText
		}

		genomesMissingText := getGenomesMissingText()

		noGenomesExistLabel := getBoldLabelCentered(genomesMissingText)
		description2 := getLabelCentered("Each person must have at least 1 genome to perform a couple analysis.")
		description3 := getLabelCentered("Go to the Manage Person page to import a genome for a person.")
		page := container.NewVBox(title, backButton, widget.NewSeparator(), noGenomesExistLabel, description2, description3)
		setPageContent(page, window)
		return
	}

	anyPerson1AnalysisFound, newestPerson1AnalysisIdentifier, _, newestPerson1AnalysisListOfGenomesAnalyzed, newerPerson1AnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(person1Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	anyPerson2AnalysisFound, newestPerson2AnalysisIdentifier, _, newestPerson2AnalysisListOfGenomesAnalyzed, newerPerson2AnalysisVersionAvailable, err := myAnalyses.GetPersonNewestGeneticAnalysisInfo(person2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	anyCoupleAnalysisFound, newestCoupleAnalysisIdentifier, timeOfNewestAnalysis, newestCoupleAnalysisListOfGenomesAnalyzed_Person1, newestCoupleAnalysisListOfGenomesAnalyzed_Person2, newerCoupleAnalysisVersionAvailable, err := myAnalyses.GetCoupleNewestGeneticAnalysisInfo(person1Identifier, person2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (anyCoupleAnalysisFound == false || anyPerson1AnalysisFound == false || anyPerson2AnalysisFound == false){
		// We will skip to the ConfirmPerformAnalysis page
		setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, previousPage, currentPage)
		return
	}
	if (newerPerson1AnalysisVersionAvailable == true || newerPerson2AnalysisVersionAvailable == true || newerCoupleAnalysisVersionAvailable == true){
	
		description1 := getLabelCentered("A new analysis method is available!")
		description2 := getLabelCentered("You must perform a new analysis to see the couple's new results.")
		
		performNewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform New Analysis", theme.NavigateNextIcon(), func(){
				setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, currentPage, currentPage)
			}))

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

		setPageContent(page, window)
		return
	}

	// This will return true if a newer analysis can be performed using newly imported genomes
	getAnalysisIsMissingGenomesBool := func()(bool, error){
	
		genomesAreIdentical := helpers.CheckIfTwoListsContainIdenticalItems(allPerson1RawGenomeIdentifiersList, newestPerson1AnalysisListOfGenomesAnalyzed)
		if (genomesAreIdentical == false){
			return true, nil
		}

		genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson2RawGenomeIdentifiersList, newestPerson2AnalysisListOfGenomesAnalyzed)
		if (genomesAreIdentical == false){
			return true, nil
		}

		genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson1RawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_Person1)
		if (genomesAreIdentical == false){
			return true, nil
		}

		genomesAreIdentical = helpers.CheckIfTwoListsContainIdenticalItems(allPerson2RawGenomeIdentifiersList, newestCoupleAnalysisListOfGenomesAnalyzed_Person2)
		if (genomesAreIdentical == false){
			return true, nil
		}

		return false, nil
	}

	analysisIsMissingGenomes, err := getAnalysisIsMissingGenomesBool()
	if (err != nil) {
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (analysisIsMissingGenomes == true){

		description1 := getLabelCentered("At least 1 person in the couple has imported/deleted a genome.")
		description2 := getLabelCentered("A new analysis must be performed.")
		performAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Perform Analysis", theme.NavigateNextIcon(), func(){
				setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, currentPage, currentPage)
			}))

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

		setPageContent(page, window)
		return
	}

	analysisReadyLabel := getBoldLabelCentered("Genetic Analysis Complete!")

	coupleAnalyzedLabel := widget.NewLabel("Couple Analyzed:")
	coupleName := person1Name + " + " + person2Name
	coupleNameLabel := getBoldLabel(coupleName)
	personAnalyzedRow := container.NewHBox(layout.NewSpacer(), coupleAnalyzedLabel, coupleNameLabel, layout.NewSpacer())

	numberOfPerson1GenomesInAnalysis := len(newestPerson1AnalysisListOfGenomesAnalyzed)
	numberOfPerson2GenomesInAnalysis := len(newestPerson2AnalysisListOfGenomesAnalyzed)

	timeAgoPerformed, err := helpers.ConvertUnixTimeToTimeAgoTranslated(timeOfNewestAnalysis, false)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	timeAgoPerformedLabel := getItalicLabelCentered("Analysis performed " + timeAgoPerformed + ".") 

	viewAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("View Analysis", theme.VisibilityIcon(), func(){

			person1AnalysisFound, person1AnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestPerson1AnalysisIdentifier)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			if (person1AnalysisFound == false){
				setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage)
				return
			}

			person2AnalysisFound, person2AnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(newestPerson2AnalysisIdentifier)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			if (person2AnalysisFound == false){
				setErrorEncounteredPage(window, errors.New("Person analysis not found after being found already."), currentPage)
				return
			}

			coupleAnalysisFound, coupleAnalysisObject, err := myAnalyses.GetCoupleGeneticAnalysis(newestCoupleAnalysisIdentifier)
			if (err != nil){
				setErrorEncounteredPage(window, err, currentPage)
				return
			}
			if (coupleAnalysisFound == false){
				setErrorEncounteredPage(window, errors.New("Couple analysis not found after being found already."), currentPage)
				return
			}

			setViewCoupleGeneticAnalysisPage(window, person1Identifier, person2Identifier, person1AnalysisObject, person2AnalysisObject, coupleAnalysisObject, numberOfPerson1GenomesInAnalysis, numberOfPerson2GenomesInAnalysis, currentPage)
			
		}))

	page := container.NewVBox(title, backButton, widget.NewSeparator(), description, widget.NewSeparator(), analysisReadyLabel, personAnalyzedRow, timeAgoPerformedLabel, viewAnalysisButton)

	setPageContent(page, window)
}


func setConfirmPerformCoupleAnalysisPage(window fyne.Window, person1Identifier string, person2Identifier string, previousPage func(), pageToVisitAfter func()){

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

	currentPage := func(){setConfirmPerformCoupleAnalysisPage(window, person1Identifier, person2Identifier, previousPage, pageToVisitAfter)}

	title := getPageTitleCentered("Genetics - Perform Couple Analysis")

	backButton := getBackButtonCentered(previousPage)

	allPerson1GenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(person1Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	numberOfPerson1Genomes := len(allPerson1GenomesMapList)

	if (numberOfPerson1Genomes == 0){
		setErrorEncounteredPage(window, errors.New("setConfirmPerformCoupleAnalysisPage called with person who has no genomes."), previousPage)
		return
	}

	allPerson2GenomesMapList, err := myGenomes.GetAllPersonGenomesMapList(person2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}

	numberOfPerson2Genomes := len(allPerson2GenomesMapList)

	if (numberOfPerson2Genomes == 0){
		setErrorEncounteredPage(window, errors.New("setConfirmPerformCoupleAnalysisPage called with person who has no genomes."), previousPage)
		return
	}

	// Now we check if there is an analysis running

	anyProcessFound, processIdentifier, err := myAnalyses.GetCoupleGeneticAnalysisProcessIdentifier(person1Identifier, person2Identifier)
	if (err != nil){
		setErrorEncounteredPage(window, err, previousPage)
		return
	}
	if (anyProcessFound == true){

		processFound, processIsComplete, _, _, _, err := myAnalyses.GetAnalysisProcessInfo(processIdentifier)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}
		if (processFound == false){
			setErrorEncounteredPage(window, errors.New("Couple genetic analysis process not found after being found already."), previousPage)
			return
		}
		if (processIsComplete == false){

			// An analysis is runnning. We will display that on the page

			description1 := getLabelCentered("A genetic analysis is already being generated.")
			description2 := getLabelCentered("View the status of the analysis?")

			viewStatusButton := getWidgetCentered(widget.NewButtonWithIcon("View Status", theme.VisibilityIcon(), func(){
					setMonitorGeneticAnalysisGenerationPage(window, processIdentifier, currentPage, pageToVisitAfter)
				}))

			page := container.NewVBox(title, backButton, widget.NewSeparator(), description1, description2, viewStatusButton)
			setPageContent(page, window)
			return
		}
	
		// Process probably encountered error
		// We will show retry page
	}

	description1 := getBoldLabelCentered("Perform genetic analysis?")
	description2 := getLabelCentered("This will create an analysis of the couple's genomes.")

	startAnalysisFunction := func(){

		newProcessIdentifier, err := myAnalyses.StartCreateNewCoupleGeneticAnalysis(person1Identifier, person2Identifier)
		if (err != nil){
			setErrorEncounteredPage(window, err, previousPage)
			return
		}

		setMonitorGeneticAnalysisGenerationPage(window, newProcessIdentifier, currentPage, pageToVisitAfter)
	}

	startAnalysisButton := getWidgetCentered(widget.NewButtonWithIcon("Start Analysis", theme.ConfirmIcon(), startAnalysisFunction))

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

	setPageContent(page, window)
}




