
// monogenicDiseases provides information about monogenic genetic diseases

package monogenicDiseases

import "errors"

// D == Deletion, I == Insertion

// All bases reference the DNA plus strand

// A disease variant is specific change on a specific location on the genome that causes the disease if mutated
type DiseaseVariant struct{

	// 3 byte hex identifier. We must ensure no variants have the same identifier.
	VariantIdentifier string

	VariantNames []string

	// Example: c.1657C>T
	NucleotideChange string

	// Example: Gly542Ter
	AminoAcidChange string

	// RSID that represents the location of this variant
	// If multiple RSIDs represent the same variant location, use the first rsID for the locus in the locusMetadata package
	VariantRSID int64

	// Base which indicate allele does not have the disease (wild-type, reference, normal, functional)
	HealthyBase string

	// Base which mean allele tests positive for the disease (mutated)
	DefectiveBase string

	// Is true if the variant is known to have a mild effect
	EffectIsMild bool

	// Reference name -> Reference link
	References map[string]string
}

type MonogenicDisease struct{

	DiseaseName string

	// Name of gene that if mutated causes disease
	GeneName string

	DominantOrRecessive string

	DiseaseDescription string

	VariantsList []DiseaseVariant

	// Reference name -> Reference link
	References map[string]string
}

var monogenicDiseaseNamesList []string
var monogenicDiseaseObjectsList []MonogenicDisease

// This must be called once during application startup
func InitializeMonogenicDiseaseVariables(){

	cysticFibrosisObject := getCysticFibrosisDiseaseObject()
	sickleCellAnemiaObject := getSickleCellAnemiaDiseaseObject()

	monogenicDiseaseObjectsList = []MonogenicDisease{cysticFibrosisObject, sickleCellAnemiaObject}

	monogenicDiseaseNamesList = make([]string, 0, len(monogenicDiseaseObjectsList))

	for _, diseaseObject := range monogenicDiseaseObjectsList{

		diseaseName := diseaseObject.DiseaseName
		monogenicDiseaseNamesList = append(monogenicDiseaseNamesList, diseaseName)
	}
}

// Be aware that all of these functions are returning original objects/slices, not copies
// Thus, we cannot edit the objects/slices that are returned. We must copy the fields first if we want to edit them.

func GetMonogenicDiseaseNamesList()([]string, error){

	if (monogenicDiseaseNamesList == nil){
		return nil, errors.New("GetMonogenicDiseaseNamesList called when list is not initialized.")
	}
	
	return monogenicDiseaseNamesList, nil
}

func GetMonogenicDiseaseObjectsList()([]MonogenicDisease, error){

	if (monogenicDiseaseObjectsList == nil){
		return nil, errors.New("GetMonogenicDiseaseObjectsList called when list is not initialized.")
	}

	return monogenicDiseaseObjectsList, nil
}

func GetMonogenicDiseaseObject(diseaseName string)(MonogenicDisease, error){

	monogenicDiseasesList, err := GetMonogenicDiseaseObjectsList()
	if (err != nil) { return MonogenicDisease{}, err }

	for _, diseaseObject := range monogenicDiseasesList{

		currentDiseaseName := diseaseObject.DiseaseName
		if (currentDiseaseName == diseaseName){
			return diseaseObject, nil
		}
	}

	return MonogenicDisease{}, errors.New("GetMonogenicDiseaseObject called with unknown disease: " + diseaseName)
}

//Outputs:
//	-map[string]DiseaseVariant: Variant Identifier -> Disease Variant
//	-error
func GetMonogenicDiseaseVariantsMap(diseaseName string)(map[string]DiseaseVariant, error){

	diseaseObject, err := GetMonogenicDiseaseObject(diseaseName)
	if (err != nil){
		return nil, errors.New("GetMonogenicDiseaseVariantsMap failed: " + err.Error())
	}

	diseaseVariantsMap := make(map[string]DiseaseVariant)

	diseaseVariantsList := diseaseObject.VariantsList

	for _, variantObject := range diseaseVariantsList{

		variantIdentifier := variantObject.VariantIdentifier

		diseaseVariantsMap[variantIdentifier] = variantObject
	}

	return diseaseVariantsMap, nil
}

func GetMonogenicDiseaseVariantObject(diseaseName string, variantIdentifier string)(DiseaseVariant, error){

	diseaseVariantsMap, err := GetMonogenicDiseaseVariantsMap(diseaseName)
	if (err != nil) { return DiseaseVariant{}, err }

	variantObject, exists := diseaseVariantsMap[variantIdentifier]
	if (exists == false) {
		return DiseaseVariant{}, errors.New("GetMonogenicDiseaseVariantObject called with unknown variant. Disease: " + diseaseName + ", Variant: " + variantIdentifier)
	}

	return variantObject, nil
}


