package gui

// gui.go provides miscellaneous GUI functions

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

import "seekia/internal/appMemory"
import "seekia/internal/appUsers"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/mySettings"
import "seekia/internal/translation"

import "errors"

func getCustomFyneSize(shift int)fyne.Size{

	normalStyle := fyne.TextStyle{
		Bold: false,
		Italic: false,
		Monospace: false,
	}
	
	customSize := float32(30 + shift)

	size := fyne.MeasureText("Standard", customSize, normalStyle)
	
	return size
}

func getBoldLabel(text string) fyne.Widget{

	titleStyle := fyne.TextStyle{
		Bold: true,
		Italic: false,
		Monospace: false,
	}
	
	boldLabel := widget.NewLabelWithStyle(text, fyne.TextAlign(fyne.TextAlignCenter), titleStyle)

	return boldLabel
}

func getBoldItalicLabel(text string) fyne.Widget{

	titleStyle := fyne.TextStyle{
		Bold: true,
		Italic: true,
		Monospace: false,
	}
	
	boldItalicLabel := widget.NewLabelWithStyle(text, fyne.TextAlign(fyne.TextAlignCenter), titleStyle)
	
	return boldItalicLabel
}

func getItalicLabel(text string) fyne.Widget{
	
	italicTextStyle := fyne.TextStyle{
		Bold: false,
		Italic: true,
		Monospace: false,
	}
	
	italicLabel := widget.NewLabelWithStyle(text, fyne.TextAlign(fyne.TextAlignCenter), italicTextStyle)
	
	return italicLabel
}

func getFyneTextStyle_Standard()fyne.TextStyle{

	standardStyle := fyne.TextStyle{
		Bold: false,
		Italic: false,
		Monospace: false,
	}

	return standardStyle
}

func getFyneTextStyle_Bold()fyne.TextStyle{

	boldStyle := fyne.TextStyle{
		Bold: true,
		Italic: false,
		Monospace: false,
	}

	return boldStyle
}

func getFyneTextStyle_Italic()fyne.TextStyle{

	italicStyle := fyne.TextStyle{
		Bold: false,
		Italic: true,
		Monospace: false,
	}

	return italicStyle
}


func getLabelCentered(text string) *fyne.Container{

	label := widget.NewLabel(text)
	labelCentered := container.NewHBox(layout.NewSpacer(), label, layout.NewSpacer())

	return labelCentered
}


func getBoldLabelCentered(inputText string)*fyne.Container{

	boldLabel := getBoldLabel(inputText)

	boldLabelCentered := container.NewHBox(layout.NewSpacer(), boldLabel, layout.NewSpacer())

	return boldLabelCentered
}

func getItalicLabelCentered(inputText string)*fyne.Container{

	italicLabel := getItalicLabel(inputText)

	italicLabelCentered := container.NewHBox(layout.NewSpacer(), italicLabel, layout.NewSpacer())

	return italicLabelCentered
}

func getBoldItalicLabelCentered(inputText string)*fyne.Container{

	boldItalicLabel := getBoldItalicLabel(inputText)

	boldItalicLabelCentered := container.NewHBox(layout.NewSpacer(), boldItalicLabel, layout.NewSpacer())
	return boldItalicLabelCentered
}

func getWidgetCentered(widget fyne.Widget)*fyne.Container{
	
	widgetCentered := container.NewHBox(layout.NewSpacer(), widget, layout.NewSpacer())
	
	return widgetCentered
}


func getContainerCentered(inputContainer *fyne.Container)*fyne.Container{
	
	containerCentered := container.NewHBox(layout.NewSpacer(), inputContainer, layout.NewSpacer())

	return containerCentered
}


func getFyneImageCentered(inputImage *canvas.Image) *fyne.Container{
	
	imageCentered := container.NewHBox(layout.NewSpacer(), inputImage, layout.NewSpacer())

	return imageCentered
}

func getPageTitleCentered(title string)(*fyne.Container){

	textStyle := getFyneTextStyle_Bold()

	currentApp := fyne.CurrentApp()

	currentThemeVariant := currentApp.Settings().ThemeVariant()

	currentThemeObject := currentApp.Settings().Theme()

	// The .Color function is used to retrieve any specified color
	// In this case, we are retrieving the current foreground color of the theme
	// We have to include the theme variant to retrieve the color
	textColor := currentThemeObject.Color(theme.ColorNameForeground, currentThemeVariant)

	label := canvas.NewText(title, textColor)
	label.TextSize = 17
	label.TextStyle = textStyle

	increasedPadding := container.NewPadded(label)
	increasedPadding2 := container.NewPadded(increasedPadding)

	pageTitleCentered := container.NewHBox(layout.NewSpacer(), increasedPadding2, layout.NewSpacer())
	
	return pageTitleCentered
}

func getPageSubtitleCentered(subtitle string) *fyne.Container{

	textStyle := getFyneTextStyle_Bold()

	currentApp := fyne.CurrentApp()

	currentThemeVariant := currentApp.Settings().ThemeVariant()

	currentThemeObject := currentApp.Settings().Theme()

	// The .Color function is used to retrieve any specified color
	// In this case, we are retrieving the current foreground color of the theme
	// We have to include the theme variant to retrieve the color
	textColor := currentThemeObject.Color(theme.ColorNameForeground, currentThemeVariant)

	label := canvas.NewText(subtitle, textColor)
	label.TextSize = 16
	label.TextStyle = textStyle

	labelPadded1 := container.NewPadded(label)
	labelPadded2 := container.NewPadded(labelPadded1)

	pageSubtitleCentered := container.NewHBox(layout.NewSpacer(), labelPadded2, layout.NewSpacer())

	return pageSubtitleCentered
}

func getBackButtonCentered(previousPage func())*fyne.Container{

	backButton := getWidgetCentered(widget.NewButtonWithIcon(translate("Go Back"), theme.NavigateBackIcon(), previousPage))

	return backButton
}

func getFyneImageBoxed(inputImage *canvas.Image) *fyne.Container{
	
	boxedImage := container.NewBorder(widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), inputImage)

	return boxedImage
}

func getContainerBoxed(inputContainer *fyne.Container) *fyne.Container{
	
	boxedContainer := container.NewBorder(widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), inputContainer)

	return boxedContainer
}

func getAppTabsBoxed(inputTabs *container.AppTabs) *fyne.Container{
	
	boxedTabs := container.NewBorder(widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), inputTabs)

	return boxedTabs
}

func getScrollContainerBoxed(inputScrollContainer *container.Scroll) *fyne.Container{
	
	boxedContainer := container.NewBorder(widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), inputScrollContainer)

	return boxedContainer
}

func getWidgetBoxed(inputWidget fyne.Widget) *fyne.Container{
	
	boxedWidget := container.NewBorder(widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), widget.NewSeparator(), inputWidget)

	return boxedWidget
}

func translate(text string) string{

	result := translation.TranslateTextFromEnglishToMyLanguage(text)

	return result
}

func translateWithContext(text string, context string)string{

	//TODO: We will use this to translate text that belongs to a specific context
	// An example is 23andMe, which will have its own translations for text
	// For this reason, we may end up having 2 identical texts that have different translations

	return text
}

//TODO: Swap content and window parameters
func setPageContent(content *fyne.Container, window fyne.Window){

	navbar, navbarLocation, err := getNavigationBar(window)
	if (err != nil){
		title := getPageTitleCentered("Failed to read from filesystem.")
		description := getLabelCentered("Contact the Seekia developers to report this error.")

		errorString := err.Error()
		errorLabel := getLabelCentered(errorString)

		page := container.NewVBox(title, description, errorLabel)
		window.SetContent(page)
		return
	}

	contentScrollable := container.NewVScroll(content)

	getPageContentWithNavbar := func()*fyne.Container{
		if (navbarLocation == "Bottom"){
			content := container.NewBorder(nil, navbar, nil, nil, contentScrollable)
			return content
		}
		if (navbarLocation == "Left"){
			content := container.NewBorder(nil, nil, navbar, nil, contentScrollable)
			return content
		}
		if (navbarLocation == "Right"){
			content := container.NewBorder(nil, nil, nil, navbar, contentScrollable)
			return content
		}
		content := container.NewBorder(navbar, nil, nil, nil, contentScrollable)
		return content
	}
	
	pageContentWithNavbar := getPageContentWithNavbar()

	window.SetContent(pageContentWithNavbar)
}

//Outputs:
//	-*fyne.Container: Navigation Bar
//	-string: Navigation location ("Top"/"Bottom"/"Left"/"Right")
//	-error
func getNavigationBar(window fyne.Window) (*fyne.Container, string, error){

	geneticsIcon, err := getFyneImageIcon("Genome")
	if (err != nil) { return nil, "", err } 

	hostIcon, err := getFyneImageIcon("Host")
	if (err != nil) { return nil, "", err } 

	desiresIcon, err := getFyneImageIcon("Desires")
	if (err != nil) { return nil, "", err } 
	
	matchesIcon, err := getFyneImageIcon("Mate")
	if (err != nil) { return nil, "", err } 
	
	homeIcon, err := getFyneImageIcon("Home")
	if (err != nil) { return nil, "", err } 
	
	profileIcon, err := getFyneImageIcon("Profile")
	if (err != nil) { return nil, "", err } 
	
	moderateIcon, err := getFyneImageIcon("Moderate")
	if (err != nil) { return nil, "", err } 	

	chatIcon, err := getFyneImageIcon("Chat")
	if (err != nil) { return nil, "", err } 
	
	settingsIcon, err := getFyneImageIcon("Settings")	
	if (err != nil) { return nil, "", err } 

	geneticsButton := widget.NewButton(translate("Genetics"), func(){
			setGeneticsPage(window)
		})
	desiresButton := widget.NewButton(translate("Desires"), func(){
			setDesiresPage(window)
		})
	matchesButton := widget.NewButton(translate("Matches"), func(){
			setMatchesPage(window)
		})
	homeButton := widget.NewButton(translate("Home"), func(){
			setHomePage(window)
		})
	profileButton := widget.NewButton(translate("Profile"), func(){
			setProfilePage(window, false, "", false, nil)
		})
	hostButton := widget.NewButton(translate("Host"), func(){
			setHostPage(window, false, nil)
		})
	chatButton := widget.NewButton(translate("Chat"), func(){
			setChatPage(window)
		})
	moderateButton := widget.NewButton(translate("Moderate"), func(){
			setModeratePage(window, false, nil)
		})
	settingsButton := widget.NewButton(translate("Settings"), func(){
			setSettingsPage(window)
		})

	geneticsButtonWithIcon := container.NewGridWithRows(2, geneticsIcon, geneticsButton)	
	desiresButtonWithIcon := container.NewGridWithRows(2, desiresIcon, desiresButton)
	matchesButtonWithIcon := container.NewGridWithRows(2, matchesIcon, matchesButton)
	homeButtonWithIcon := container.NewGridWithRows(2, homeIcon, homeButton)
	profileButtonWithIcon := container.NewGridWithRows(2, profileIcon, profileButton)
	hostButtonWithIcon := container.NewGridWithRows(2, hostIcon, hostButton)
	chatButtonWithIcon := container.NewGridWithRows(2, chatIcon, chatButton)
	moderateButtonWithIcon := container.NewGridWithRows(2, moderateIcon, moderateButton)
	settingsButtonWithIcon := container.NewGridWithRows(2, settingsIcon, settingsButton)

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

		exists, navigationLocation, err := mySettings.GetSetting("NavigationBarLocation")
		if (err != nil) { return "", err }
		if (exists == false){
			return "Top", nil
		}
		if (navigationLocation != "Top" && navigationLocation != "Bottom" && navigationLocation != "Left" && navigationLocation != "Right"){
			return "", errors.New("Invalid NavigationBarLocation: " + navigationLocation)
		}
		return navigationLocation, nil
	}

	navigationLocation, err := getNavigationLocation()
	if (err != nil) { return nil, "", err }

	getNavbarButtonsList := func()([]*fyne.Container, error){

		_, showHostButton, err := mySettings.GetSetting("ShowHostButtonNavigation")
		if (err != nil) { return nil, err }

		_, showModerateButton, err := mySettings.GetSetting("ShowModerateButtonNavigation")
		if (err != nil) { return nil, err }

		if (showHostButton == "Yes" && showModerateButton == "Yes"){

			itemsList := []*fyne.Container{homeButtonWithIcon, geneticsButtonWithIcon, profileButtonWithIcon, desiresButtonWithIcon, matchesButtonWithIcon, chatButtonWithIcon, hostButtonWithIcon, moderateButtonWithIcon, settingsButtonWithIcon}

			return itemsList, nil
	
		}
		if (showHostButton != "Yes" && showModerateButton == "Yes"){

			itemsList := []*fyne.Container{homeButtonWithIcon, geneticsButtonWithIcon, profileButtonWithIcon, desiresButtonWithIcon, matchesButtonWithIcon, chatButtonWithIcon, moderateButtonWithIcon, settingsButtonWithIcon }

			return itemsList, nil
		}
		if (showHostButton == "Yes" && showModerateButton != "Yes"){

			itemsList := []*fyne.Container{homeButtonWithIcon, geneticsButtonWithIcon, profileButtonWithIcon, desiresButtonWithIcon, matchesButtonWithIcon, chatButtonWithIcon, hostButtonWithIcon, settingsButtonWithIcon }

			return itemsList, nil
		}
	
		// showHostButton != "Yes" && showModerateButton != "Yes"

		itemsList := []*fyne.Container{homeButtonWithIcon, geneticsButtonWithIcon, profileButtonWithIcon, desiresButtonWithIcon, matchesButtonWithIcon, chatButtonWithIcon, settingsButtonWithIcon }

		return itemsList, nil
	}

	navbarButtonsList, err := getNavbarButtonsList()
	if (err != nil) { return nil, "", err }

	if (navigationLocation == "Top" || navigationLocation == "Bottom"){

		navbar := container.NewHBox()
		navbar.Add(layout.NewSpacer())

		for _, element := range navbarButtonsList{
			navbar.Add(element)
		}
		navbar.Add(layout.NewSpacer())

		navbarScrollable := container.NewHScroll(navbar)

		if (navigationLocation == "Top"){
			navbarWithSeparator := container.NewVBox(navbarScrollable, widget.NewSeparator())

			return navbarWithSeparator, navigationLocation, nil
		}

		navbarWithSeparator := container.NewVBox(widget.NewSeparator(), navbarScrollable)

		return navbarWithSeparator, navigationLocation, nil
	}
	if (navigationLocation == "Left" || navigationLocation == "Right"){

		navbar := container.NewVBox()
		navbar.Add(layout.NewSpacer())

		for _, element := range navbarButtonsList{
			navbar.Add(element)
		}
		navbar.Add(layout.NewSpacer())

		navbarScrollable := container.NewVScroll(navbar)

		if (navigationLocation == "Left"){
			navbarWithSeparator := container.NewHBox(navbarScrollable, widget.NewSeparator())

			return navbarWithSeparator, navigationLocation, nil
		}

		navbarWithSeparator := container.NewHBox(widget.NewSeparator(), navbarScrollable)

		return navbarWithSeparator, navigationLocation, nil
	}

	return nil, "", errors.New("Invalid Navigation bar location: " + navigationLocation)
}

// This page is used when a new GUI page is being loaded, and concurrency is not being used
// When concurrency is being used, we give the user a GUI page where they can exit the page instead of waiting for the process to complete
// This screen should only be used when the time it takes to load will only be a few seconds
func setLoadingScreen(window fyne.Window, pageTitle string, loadingText string){

	title := getPageTitleCentered(pageTitle)

	loadingLabel := getWidgetCentered(getItalicLabel(loadingText))
	progressBar := getWidgetCentered(widget.NewProgressBarInfinite())

	pageContent := container.NewVBox(title, loadingLabel, progressBar)

	page := container.NewCenter(pageContent)

	window.SetContent(page)
}



// This will only be used within the startup gui
func setErrorEncounteredPage_NoNavBar(window fyne.Window, err error, showBackButton bool, previousPage func()){

	title := getPageTitleCentered("Error Encountered")

	header := container.NewVBox(title)

	if (showBackButton == true){
		backButton := getBackButtonCentered(previousPage)
		header.Add(backButton)
	}

	header.Add(widget.NewSeparator())

	description1 := getLabelCentered("Something went wrong. Report this error to Seekia developers.")
	description2 := getBoldLabelCentered("Be aware that this error may contain sensitive information.")
	description3 := getLabelCentered("Censor any sensitive information manually before sharing the error.")

	header.Add(description1)
	header.Add(description2)
	header.Add(description3)
	header.Add(widget.NewSeparator())

	getErrorString := func()string{
		if (err == nil){
			return "No nav bar error encountered page called with nil error."
		}
		errorString := err.Error()
		return errorString
	}

	errorString := getErrorString()

	errorLabel := widget.NewLabel(errorString)
	errorLabel.Wrapping = 3
	errorLabel.Alignment = 1
	errorLabel.Selectable = true
	errorLabel.TextStyle = getFyneTextStyle_Bold()

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

	window.SetContent(page)	
}

func setErrorEncounteredPage(window fyne.Window, err error, previousPage func()){

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

	currentPage := func(){setErrorEncounteredPage(window, err, previousPage)}	

	title := getPageTitleCentered("Error Encountered")

	backButton := getBackButtonCentered(previousPage)

	description1 := getLabelCentered("Something went wrong. Report this error to Seekia developers.")
	description2 := getBoldLabelCentered("Be aware that this error may contain sensitive information.")
	description3 := getLabelCentered("Censor any sensitive information manually before sharing the error.")

	getErrorString := func()string{
		if (err == nil){
			return "Error encountered page called with nil error."
		}
		errorString := err.Error()
		return errorString
	}

	errorString := getErrorString()

	errorStringTrimmed, _, _ := helpers.TrimAndFlattenString(errorString, 40)

	errorLabel := getBoldLabelCentered(errorStringTrimmed)

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

	errorRow := container.NewHBox(layout.NewSpacer(), errorLabel, viewTextButton, layout.NewSpacer())

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

	setPageContent(page, window)
}

func showUnderConstructionDialog(window fyne.Window){

	dialogTitle := translate("Under Construction")
	dialogMessageA := getLabelCentered(translate("Seekia is under construction."))
	dialogMessageB := getLabelCentered(translate("This page/feature needs to be built."))
	dialogContent := container.NewVBox(dialogMessageA, dialogMessageB)
	dialog.ShowCustom(dialogTitle, translate("Close"), dialogContent, window)
}

func setHomePage(window fyne.Window){

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

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

	title := getPageTitleCentered("Home")

	welcomeTitle := getBoldLabelCentered("Welcome to Seekia!")

	welcomeMessage := getLabelCentered("Seekia is a genetics aware mate discovery network.")

	exists, currentUserName := appUsers.GetCurrentAppUserName()
	if (exists == false){
		setErrorEncounteredPage(window, errors.New("setHomePage called with no signed-in user."), func(){setChooseAppUserPage(window)})
		return
	}
	currentUserLabel := getBoldItalicLabel("Current User:")

	changeUserButton := getWidgetCentered(widget.NewButtonWithIcon(currentUserName, theme.AccountIcon(), func(){

			setLoadingScreen(window, "Signing Out", "Signing out...")

			err := appUsers.SignOutOfAppUser()
			if (err != nil){
				setErrorEncounteredPage_NoNavBar(window, err, true, func(){setChooseAppUserPage(window)})
				return
			}

			setChooseAppUserPage(window)
		}))

	currentUserRow := container.NewHBox(layout.NewSpacer(), currentUserLabel, changeUserButton, layout.NewSpacer())

	helpIcon, err := getFyneImageIcon("Info")
	if (err != nil){
		setErrorEncounteredPage(window, err, currentPage)
		return
	}
	helpButton := widget.NewButton(translate("Help"), func(){
			setHelpPage(window, currentPage)
		})
	helpButtonWithIcon := container.NewGridWithColumns(1, helpIcon, helpButton)

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

	rulesButton := widget.NewButton("Rules", func(){
			setViewSeekiaRulesPage(window, currentPage)
		})
	rulesButtonWithIcon := container.NewGridWithColumns(1, rulesIcon, rulesButton)

	syncIcon, err := getFyneImageIcon("Sync")
	if (err != nil){
		setErrorEncounteredPage(window, err, currentPage)
		return
	}

	syncButton := widget.NewButton(translate("Sync"), func(){
			setSyncPage(window, currentPage)
		})
	syncButtonWithIcon := container.NewGridWithRows(2, syncIcon, syncButton)

	buttonsRow := getContainerCentered(container.NewGridWithRows(1, helpButtonWithIcon, rulesButtonWithIcon, syncButtonWithIcon))

	description1 := getBoldLabelCentered("Learn how to use Seekia on the Help page.")
	description2 := getLabelCentered("View the Seekia rules on the Rules page.")
	description3 := getLabelCentered("Manage your connection to the Seekia network on the Sync page.")

	seekiaLinkTitle := widget.NewLabel("Stay updated at:")

	//TODO: Retrieve URL from parameters
	// URL may need to be changed if it is lost or stolen
	// Also add a page that shows .eth URL
	seekiaLink := getBoldLabel("Seekia.net")

	seekiaVersion := getLabelCentered("Seekia Version 0.76")

	seekiaLinkWithTitle := container.NewHBox(layout.NewSpacer(), seekiaLinkTitle, seekiaLink, layout.NewSpacer())

	page := container.NewVBox(title, widget.NewSeparator(), welcomeTitle, welcomeMessage, widget.NewSeparator(), currentUserRow, widget.NewSeparator(), buttonsRow, widget.NewSeparator(), description1, description2, description3, widget.NewSeparator(), seekiaLinkWithTitle, seekiaVersion)
	
	setPageContent(page, window)
}

func setSelectLanguagePage(window fyne.Window, showNavigationBar bool, previousPage func()){

	title := getPageTitleCentered("Select Language")

	backButton := getBackButtonCentered(previousPage)

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

	currentLanguage := translation.GetMyLanguage()

	currentLanguageLabel := getBoldLabelCentered("Current Language:")
	currentLanguageTextLabel := getLabelCentered(currentLanguage)

	//TODO

	comingSoonLabel := getBoldLabelCentered("More languages coming eventually.")

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

	if (showNavigationBar == true){
		setPageContent(page, window)
	} else {
		window.SetContent(page)	
	}
}

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

	title := getPageTitleCentered("Seekia Rules")

	backButton := getBackButtonCentered(previousPage)
	
	description1 := getBoldLabelCentered("Unruleful content will be banned by the Seekia moderators.")
	description2 := getLabelCentered("You can report rulebreaking content within the app.")
	description3 := getLabelCentered("Identities, profiles and messages can be reported and banned.")

	//TODO: Add more rules, and improve these rules.
	// There should be a more descriptive page for moderators that has specific examples

	spamDescription := widget.NewLabel("Selling or trading. Advertising or soliciting anything except yourself.")
	spamAccordionItem := widget.NewAccordionItem("Spam", spamDescription)

	nudityDescription := widget.NewLabel("Nudity and pornography.")
	nudityAccordionItem := widget.NewAccordionItem("Nudity", nudityDescription)

	dangerousDescription := widget.NewLabel("Threats, harrasment, and other dangerous content.")
	dangerousAccordionItem := widget.NewAccordionItem("Dangerous Content", dangerousDescription)

	illegalDescription := widget.NewLabel("Prostitution, drug trading, copyrighted content, and other unlawful content.")
	illegalAccordionItem := widget.NewAccordionItem("Illegal Content", illegalDescription)

	rulesAccordion := getContainerCentered(getWidgetBoxed(widget.NewAccordion(spamAccordionItem, nudityAccordionItem, dangerousAccordionItem, illegalAccordionItem)))

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

	setPageContent(page, window)
}

// If profileTypeProvided == true, there is no ability to navigate between profile types
// If profileTypeProvided == false, you can navigate between profile types, and the result is saved in ProfilePageProfileType
func setProfilePage(window fyne.Window, profileTypeProvided bool, profileType string, previousPageProvided bool, previousPage func()){

	currentPage := func(){setProfilePage(window, profileTypeProvided, profileType, previousPageProvided, previousPage)}

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

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

		exists, moderatorModeStatus, err := mySettings.GetSetting("ModeratorModeOnOffStatus")
		if (err != nil) { return false, err }
		if (exists == true && moderatorModeStatus == "On"){
			return true, nil
		}
		return false, nil
	}

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

		exists, hostModeStatus, err := mySettings.GetSetting("HostModeOnOffStatus")
		if (err != nil) { return false, err }
		if (exists == true && hostModeStatus == "On"){
			return true, nil
		}
		return false, nil
	}

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

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

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

		if (profileTypeProvided == true){

			if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
				return "", errors.New("setProfilePage called with invalid profileType: " + profileType)
			}
			return profileType, nil
		}

		exists, myProfileType, err := mySettings.GetSetting("ProfilePageProfileType")
		if (err != nil) { return "", err }
		if (exists == false){
			return "Mate", nil
		}
		if (myProfileType != "Mate" && myProfileType != "Host" && myProfileType != "Moderator"){
			return "", errors.New("mySettings malformed: invalid ProfilePageProfileType: " + myProfileType)
		}

		if (myProfileType == "Moderator" && moderatorModeEnabled == false){
			return "Mate", nil
		}
		if (myProfileType == "Host" && hostModeEnabled == false){
			return "Mate", nil
		}

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

	title := getPageTitleCentered("My Profile")

	page := container.NewVBox(title)

	if (previousPageProvided == true){
		backButton := getBackButtonCentered(previousPage)
		page.Add(backButton)
	}

	profileTypeIcon, err := getIdentityTypeIcon(myProfileType, -10)
	if (err != nil){
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}

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

		if (profileTypeProvided == true){
			// The page does not offer a button to switch between profile types.
			profileTypeLabel := getBoldLabel(myProfileType + " Profile")
			return profileTypeLabel, nil
		}

		if (moderatorModeEnabled == false && hostModeEnabled == false){

			mateLabel := getBoldLabel("Mate Profile")
			return mateLabel, nil
		}

		changeProfileTypeButton := widget.NewButton(myProfileType + " Profile", func(){
			getNextProfileType := func()string{
				if (myProfileType == "Mate"){

					if (moderatorModeEnabled == true){
						return "Moderator"
					}
					
					return "Host"
				}
				if (myProfileType == "Moderator"){
					if (hostModeEnabled == true){
						return "Host"
					}
					return "Mate"
				}

				return "Mate"
			}
		
			nextProfileType := getNextProfileType()

			err := mySettings.SetSetting("ProfilePageProfileType", nextProfileType)
			if (err != nil){
				setErrorEncounteredPage(window, err, func(){setHomePage(window)})
				return
			}

			currentPage()
		})

		return changeProfileTypeButton, nil
	}

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

	changeProfileTypeButtonWithIcon := getContainerCentered(container.NewGridWithRows(2, profileTypeIcon, changeProfileTypeButtonOrText))

	getDescriptionSection := func()*fyne.Container{

		description1 := getLabelCentered("Manage your " + myProfileType + " profile.")

		if (myProfileType == "Mate"){
			description2 := getLabelCentered("Start by building your profile.")
			description3 := getLabelCentered("Broadcast your profile once it is ready.")
			description4 := getLabelCentered("You must rebroadcast your profile whenever you make changes.")

			descriptionSection := container.NewVBox(description1, description2, description3, description4)
			return descriptionSection
		}
		if (myProfileType == "Moderator"){

			description2 := getLabelCentered("Building your Moderator profile is optional.")
			description3 := getLabelCentered("You must rebroadcast your profile whenever you make changes.")

			descriptionSection := container.NewVBox(description1, description2, description3)
			return descriptionSection		
		}

		// myProfileType = "Host"
		description2 := getLabelCentered("Building your Host profile is optional.")
		description3 := getLabelCentered("Your Host profile will be broadcast automatically.")
		
		descriptionSection := container.NewVBox(description1, description2, description3)
		return descriptionSection
	}

	descriptionSection := getDescriptionSection()

	buildProfileIcon, err := getFyneImageIcon("Check")
	if (err != nil) { 
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}	
	
	profileIcon, err := getFyneImageIcon("Profile")
	if (err != nil) { 
		setErrorEncounteredPage(window, err, func(){setHomePage(window)})
		return
	}	

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

	buildProfileButton := widget.NewButton("Build", func(){
			if (myProfileType == "Mate"){
				setBuildMyMateProfilePage(window, currentPage)
				return		
			}
			if (myProfileType == "Moderator"){
				setBuildMyModeratorProfilePage(window, currentPage)
				return
			}
			setBuildMyHostProfilePage(window, currentPage)
		})
	buildProfileButtonWithIcon := container.NewGridWithRows(2, buildProfileIcon, buildProfileButton)

	broadcastButton := widget.NewButton(translate("Broadcast"), func(){			
			if (myProfileType == "Host"){
				//TODO: Add a custom broadcast page for Host where you can monitor the last time it was broadcast and deactivate
				showUnderConstructionDialog(window)
				return
			}

			setBroadcastPage(window, myProfileType, currentPage)
		})
	broadcastButtonWithIcon := container.NewGridWithRows(2, broadcastIcon, broadcastButton)

	viewProfileButton := widget.NewButton(translate("View"), func(){
			setViewMyLocalOrPublicProfilePage(window, myProfileType, currentPage)
		})
	viewProfileButtonWithIcon := container.NewGridWithRows(2, profileIcon, viewProfileButton)

	actionsGrid := container.NewGridWithRows(1, buildProfileButtonWithIcon, broadcastButtonWithIcon, viewProfileButtonWithIcon)
	actionsSection := getContainerCentered(actionsGrid)

	page.Add(widget.NewSeparator())
	page.Add(changeProfileTypeButtonWithIcon)
	page.Add(widget.NewSeparator())
	page.Add(descriptionSection)
	page.Add(widget.NewSeparator())
	page.Add(actionsSection)

	if (myProfileType == "Moderator"){

		page.Add(widget.NewSeparator())

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

		myScoreButton := widget.NewButton("My Score", func(){
				setViewMyModeratorScorePage(window, currentPage)
			})
		myScoreButtonWithIcon := container.NewGridWithColumns(1, scoreIcon, myScoreButton)

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

		myReviewsButton := widget.NewButton("My Reviews", func(){
				setViewMyReviewsPage(window, "Profile", currentPage)
			})
		myReviewsButtonWithIcon := container.NewGridWithColumns(1, reviewsIcon, myReviewsButton)

		moderatorButtonsRow := getContainerCentered(container.NewGridWithRows(1, myScoreButtonWithIcon, myReviewsButtonWithIcon))

		page.Add(moderatorButtonsRow)
		page.Add(widget.NewSeparator())
	}

	setPageContent(page, window)
}

// This is a page to view an identity hash in its entirety, and a text entry to copy it
func setViewIdentityHashPage(window fyne.Window, identityHash [16]byte, previousPage func()){

	title := getPageTitleCentered("Viewing Identity Hash")

	backButton := getBackButtonCentered(previousPage)

	identityHashString, _, err := identity.EncodeIdentityHashBytesToString(identityHash)
	if (err != nil){
		identityHashHex := encoding.EncodeBytesToHexString(identityHash[:])
		setErrorEncounteredPage(window, errors.New("setViewIdentityHashPage called with invalid identity hash: " + identityHashHex), previousPage)
	}

	identityHashLabel := getBoldLabel(identityHashString)
	identityHashLabelPadded := container.NewPadded(container.NewPadded(identityHashLabel))

	identityHashEntry := widget.NewMultiLineEntry()
	identityHashEntry.SetText(identityHashString)
	identityHashEntry.OnChanged = func(_ string){
			identityHashEntry.SetText(identityHashString)
		}
	identityHashEntryBoxed := getWidgetBoxed(identityHashEntry)

	widener := widget.NewLabel("                                                                           ")
	identityHashEntryWidened := getContainerCentered(container.NewGridWithColumns(1, identityHashEntryBoxed, widener))

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

// This is a page to view an message/profile hash in its entirety, and a text entry to copy it
func setViewContentHashPage(window fyne.Window, hashType string, contentHash []byte, previousPage func()){

	title := getPageTitleCentered("Viewing " + hashType + " Hash")

	backButton := getBackButtonCentered(previousPage)

	contentHashHex := encoding.EncodeBytesToHexString(contentHash)

	contentHashLabel := getBoldLabel(contentHashHex)
	contentHashLabelPadded := container.NewPadded(container.NewPadded(contentHashLabel))

	contentHashEntry := widget.NewMultiLineEntry()
	contentHashEntry.SetText(contentHashHex)
	contentHashEntry.OnChanged = func(_ string){
			contentHashEntry.SetText(contentHashHex)
		}
	contentHashEntry.Wrapping = 3

	contentHashEntryBoxed := getWidgetBoxed(contentHashEntry)

	widener := widget.NewLabel("                                                                           ")
	contentHashEntryWidened := getContainerCentered(container.NewGridWithColumns(1, contentHashEntryBoxed, widener))

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

//TODO: Make this page able to handle large amounts of text without crashing
// In the meantime, trim text that is too long to render
func setViewTextPage(window fyne.Window, pageTitle string, textToView string, viewCopyable bool, previousPage func()){

	title := getPageTitleCentered(pageTitle)

	backButton := getBackButtonCentered(previousPage)

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

	getTextViewWidget := func()fyne.Widget{

		if (viewCopyable == true){

			textEntry := widget.NewMultiLineEntry()
			textEntry.SetText(textToView)
			textEntry.OnChanged = func(_ string){
					textEntry.SetText(textToView)
				}
			textEntry.Wrapping = 3

			return textEntry
		}

		textViewWidget := widget.NewLabel(textToView)
		textViewWidget.Wrapping = 3
		textViewWidget.Alignment = 1
		textViewWidget.Selectable = true

		return textViewWidget
	}

	textViewWidget := getTextViewWidget()

	getChangeDisplayTypeButton := func()fyne.Widget{

		if (viewCopyable == false){

			viewCopyableButton := widget.NewButtonWithIcon("View Copyable", theme.ContentPasteIcon(), func(){
					setViewTextPage(window, pageTitle, textToView, true, previousPage)
				})
			return viewCopyableButton
		}
		viewUncopyableButton := widget.NewButtonWithIcon("View Uncopyable", theme.VisibilityIcon(), func(){
					setViewTextPage(window, pageTitle, textToView, false, previousPage)
				})
		return viewUncopyableButton
	}

	changeDisplayTypeButton := getChangeDisplayTypeButton()

	changeDisplayTypeButtonCentered := getWidgetCentered(changeDisplayTypeButton)

	page := container.NewBorder(header, changeDisplayTypeButtonCentered, nil, nil, textViewWidget)

	setPageContent(page, window)	
}

func setViewLinkPage(window fyne.Window, pageTitle string, textToView string, previousPage func()){

	title := getPageTitleCentered(pageTitle)

	backButton := getBackButtonCentered(previousPage)

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

	textEntry := widget.NewMultiLineEntry()
	textEntry.SetText(textToView)
	textEntry.OnChanged = func(_ string){
			textEntry.SetText(textToView)
		}
	textEntry.Wrapping = 3

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

	setPageContent(page, window)	
}


func getCryptocurrencyAddressLabelWithCopyAndQRButtons(window fyne.Window, cryptocurrency string, cryptoAddress string, currentPage func())(*fyne.Container, error){

	if (cryptocurrency != "Ethereum" && cryptocurrency != "Cardano"){
		return nil, errors.New("getCryptocurrencyAddressLabelWithCopyAndQRButtons called with invalid cryptocurrency: " + cryptocurrency)
	}

	addressLabel := widget.NewMultiLineEntry()
	addressLabel.SetText(cryptoAddress)
	addressLabel.OnChanged = func(_ string){
		addressLabel.SetText(cryptoAddress)
	}
	addressBox := getWidgetBoxed(addressLabel)

	qrCodeImage, err := getFyneImageIcon("QR")
	if (err != nil) { return nil, err }

	qrCodeButton := widget.NewButton("QR Code", func(){

			title := translate(cryptocurrency + " QR Code")

			//TODO: Add QR Code generation
			dialogMessage := getLabelCentered("Under Construction")

			dialog.ShowCustom(title, translate("Close"), dialogMessage, window)
		})

	qrCodeButtonWithIcon := getContainerBoxed(container.NewGridWithColumns(1, qrCodeImage, qrCodeButton))

	clipboardIcon, err := getFyneImageIcon("Clipboard")
	if (err != nil){ return nil, err }

	currentClipboard := window.Clipboard()

	getClipboardButtonText := func()string{
		
		currentContent := currentClipboard.Content()
		if (currentContent == cryptoAddress){
			return "Copied!"
		}
		return "Copy"
	}

	clipboardButtonText := getClipboardButtonText()
	copyToClipboardButton := widget.NewButton(clipboardButtonText, func(){
		
			currentClipboard.SetContent(cryptoAddress)
			currentPage()
		})

	clipboardButtonWithIcon := getContainerBoxed(container.NewGridWithColumns(1, clipboardIcon, copyToClipboardButton))

	addressBoxWithClipboardQRRow := getContainerCentered(container.NewGridWithRows(1, qrCodeButtonWithIcon, addressBox, clipboardButtonWithIcon))

	return addressBoxWithClipboardQRRow, nil
}


// This function creates a widget list from an input list of strings
// The onClickedFunction is called if any of the items are selected
//Inputs:
//	-[]string: A list of the items
//	-func(itemIndex int): The function that is called when an item is selected
// Outputs:
//	-fyne.Widget: Widget list
//	-error
func getFyneWidgetListFromStringList(inputList []string, onClickedFunction func(int))(fyne.Widget, error){

	listLength := len(inputList)

	if (listLength == 0){
		return nil, errors.New("getFyneWidgetListFromStringList called with empty list.")
	}

	getListLength := func()int{
		return listLength
	}
	
	createListItemFunction := func()fyne.CanvasObject{
		listItem := container.NewHBox(layout.NewSpacer(), widget.NewLabel(""), layout.NewSpacer())
		return listItem
	}
	updateListItemFunction := func(id widget.ListItemID, item fyne.CanvasObject){
		itemName := inputList[id]

		itemObjectsList := item.(*fyne.Container).Objects

		itemLabel := itemObjectsList[1].(*widget.Label)

		itemLabel.SetText(itemName)
	}

	widgetList := widget.NewList(getListLength, createListItemFunction, updateListItemFunction)
	
	widgetList.OnSelected = func(itemIndex widget.ListItemID) {
		onClickedFunction(itemIndex)
	}

	return widgetList, nil
}


