
// myChosenAnalysis provides a function to get a mate user's chosen genetic analysis
// Mate users choose a genome person and genome in the Build Profile gui.
// The genome person's analysis is used for calculating genetic attributes with other users and for sharing on their profile
// The user can choose to not share any of the genetic analysis information on their profile, in which case
//		the analysis is only used when calculating attributes with other users
// An example of one of these calculated attributes is "OffspringProbabilityOfAnyMonogenicDisease"

package myChosenAnalysis

import "seekia/internal/genetics/geneticAnalysis"
import "seekia/internal/genetics/myAnalyses"
import "seekia/internal/genetics/myPeople"
import "seekia/internal/genetics/readGeneticAnalysis"
import "seekia/internal/profiles/myLocalProfiles"

import "errors"
import "sync"


// We use this mutex whenever we update the cacheChosenGeneticAnalysis global variables
var myCacheChosenGeneticAnalysisMutex sync.RWMutex

// We use this identifier to keep track of which analysis we have stored in the cache
// Whenever we update our genome person's analysis, we will update our cache analysis
var myCacheChosenGeneticAnalysisIdentifier string

// We use this variable to store the analysis in memory
// This prevents us from having to read and unmarshal the messagepack file each time we want to retrieve the analysis
var myCacheChosenGeneticAnalysis geneticAnalysis.PersonAnalysis

// These variables store metadata about the cache genetic analysis
var myCacheChosenGeneticAnalysis_GenomeIdentifierToUse [16]byte

// This variable tells us if the current cache chosen genetic analysis contains multiple genomes
var myCacheChosenGeneticAnalysis_MultipleGenomesExist bool


// This function is used to retrieve the user's chosen person genetic analysis
// The user must choose a person to link to their mate profile/identity
// This genetic analysis is not shared on the person's profile publicly.
// Portions of the analysis may be shared if the user opts in.
// The analysis returned by this function is not a copy, so fields must not be edited
//Outputs:
//	-bool: Person identifier chosen
//	-bool: Any genomes exist
//	-bool: Person analysis is ready
//	-geneticAnalysis.PersonAnalysis: My Chosen Genetic analysis
//	-[16]byte: Genome identifier to use
//	-bool: Multiple genomes exist
//	-error
func GetMyChosenMateGeneticAnalysis()(bool, bool, bool, geneticAnalysis.PersonAnalysis, [16]byte, bool, error){

	// We use this when returning errors
	emptyPersonAnalysis := geneticAnalysis.PersonAnalysis{}

	// We check if the user has added a genome person, and add their genome info if necessary

	myGenomePersonIdentifierExists, myGenomePersonIdentifier, err := myLocalProfiles.GetProfileData("Mate", "GenomePersonIdentifier")
	if (err != nil){ return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err }
	if (myGenomePersonIdentifierExists == false){
		return false, false, false, emptyPersonAnalysis, [16]byte{}, false, nil
	}

	anyGenomesExist, personAnalysisIsReady, personAnalysisIdentifier, err := myPeople.CheckIfPersonAnalysisIsReady(myGenomePersonIdentifier)
	if (err != nil){ return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err }
	if (anyGenomesExist == false){
		return true, false, false, emptyPersonAnalysis, [16]byte{}, false, nil
	}
	if (personAnalysisIsReady == false){
		return true, true, false, emptyPersonAnalysis, [16]byte{}, false, nil
	}

	myCacheChosenGeneticAnalysisMutex.RLock()
	myCacheChosenGeneticAnalysisIdentifierCopy := myCacheChosenGeneticAnalysisIdentifier
	myCacheChosenGeneticAnalysisMutex.RUnlock()

	if (myCacheChosenGeneticAnalysisIdentifierCopy == personAnalysisIdentifier){
		// The analysis exists in cache
		// We copy it and return it

		myCacheChosenGeneticAnalysisMutex.RLock()

		// This is a shallow copy, but it is all we need, because we never directly edit attributes of the returned object
		myCacheChosenGeneticAnalysisCopy := myCacheChosenGeneticAnalysis

		myCacheChosenGeneticAnalysis_GenomeIdentifierToUseCopy := myCacheChosenGeneticAnalysis_GenomeIdentifierToUse
		myCacheChosenGeneticAnalysis_MultipleGenomesExistCopy := myCacheChosenGeneticAnalysis_MultipleGenomesExist

		myCacheChosenGeneticAnalysisMutex.RUnlock()

		return true, true, true, myCacheChosenGeneticAnalysisCopy, myCacheChosenGeneticAnalysis_GenomeIdentifierToUseCopy, myCacheChosenGeneticAnalysis_MultipleGenomesExistCopy, nil
	}

	// The analysis and its metadata have not been read into the cache yet
	// We will retrieve it from its .messagepack file

	myAnalysisFound, myGeneticAnalysisObject, err := myAnalyses.GetPersonGeneticAnalysis(personAnalysisIdentifier)
	if (err != nil){ return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err }
	if (myAnalysisFound == false){
		return false, false, false, emptyPersonAnalysis, [16]byte{}, false, errors.New("CheckIfPersonAnalysisIsReady returning missing genetic analysis.")
	}

	allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, _, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
	if (err != nil) { return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err }

	getGenomeIdentifierToUse := func()([16]byte, error){

		if (multipleGenomesExist == false){

			// The analysis only has 1 genome identifier.
			analysisOnlyRawGenomeIdentifier := allRawGenomeIdentifiersList[0]

			return analysisOnlyRawGenomeIdentifier, nil
		}

		currentCombinedGenomeToUse, err := GetMyCombinedGenomeToUse()
		if (err != nil){ return [16]byte{}, err }

		if (currentCombinedGenomeToUse == "Only Exclude Conflicts"){
			return onlyExcludeConflictsGenomeIdentifier, nil
		}
		// currentCombinedGenomeToUse == "Only Include Shared"
		return onlyIncludeSharedGenomeIdentifier, nil
	}

	genomeIdentifierToUse, err := getGenomeIdentifierToUse()
	if (err != nil) { return false, false, false, emptyPersonAnalysis, [16]byte{}, false, err }

	myCacheChosenGeneticAnalysisMutex.Lock()

	myCacheChosenGeneticAnalysisIdentifier = personAnalysisIdentifier
	myCacheChosenGeneticAnalysis = myGeneticAnalysisObject
	myCacheChosenGeneticAnalysis_GenomeIdentifierToUse = genomeIdentifierToUse
	myCacheChosenGeneticAnalysis_MultipleGenomesExist = multipleGenomesExist

	myChosenGeneticAnalysisCopy := myGeneticAnalysisObject

	myCacheChosenGeneticAnalysisMutex.Unlock()

	return true, true, true, myChosenGeneticAnalysisCopy, genomeIdentifierToUse, multipleGenomesExist, nil
}


//Outputs:
//	-string: "Only Include Shared", "Only Exclude Conflicts"
//	-error
func GetMyCombinedGenomeToUse()(string, error){

	combinedGenomeToUseExists, combinedGenomeToUse, err := myLocalProfiles.GetProfileData("Mate", "CombinedGenomeToUse")
	if (err != nil){ return "", err }
	if (combinedGenomeToUseExists == false){
		return "Only Exclude Conflicts", nil
	}

	if (combinedGenomeToUse != "Only Include Shared" && combinedGenomeToUse != "Only Exclude Conflicts"){
		return "", errors.New("myLocalProfiles is malformed: Contains invalid combinedGenomeToUse: " + combinedGenomeToUse)
	}

	return combinedGenomeToUse, nil
}


func SetMyCombinedGenomeToUse(combinedGenomeToUse string)error{

	if (combinedGenomeToUse != "Only Include Shared" && combinedGenomeToUse != "Only Exclude Conflicts"){
		return errors.New("SetCombinedGenomeToUse called with invalid combinedGenomeToUse: " + combinedGenomeToUse)
	}

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

	// We reset the global variables so the new analysis GenomeIdentifierToUse global variable will be updated
	myCacheChosenGeneticAnalysisMutex.Lock()

	myCacheChosenGeneticAnalysisIdentifier = ""
	myCacheChosenGeneticAnalysis = geneticAnalysis.PersonAnalysis{}
	myCacheChosenGeneticAnalysis_GenomeIdentifierToUse = [16]byte{}
	myCacheChosenGeneticAnalysis_MultipleGenomesExist = false

	myCacheChosenGeneticAnalysisMutex.Unlock()

	return nil
}



