package comsoc

import "fmt"

func TieBreakFactory(orderedAlts []Alternative) func([]Alternative) (Alternative, error) {
	return func(bestAlts []Alternative) (Alternative, error) {
		bestAlt := bestAlts[0]
		for _, alt := range bestAlts[1:] {
			if isPref(alt, bestAlt, orderedAlts) {
				bestAlt = alt
			}
		}
		return bestAlt, nil
	}
}

func Test_tieBreakFactory() {
	orderedAlts := []Alternative{8, 9, 6, 1, 3, 2}
	fmt.Println("Ordre strict :", orderedAlts)
	lambda := TieBreakFactory(orderedAlts)
	bestAlts := []Alternative{3, 6}
	fmt.Println("Premières alternatives, à départager :", bestAlts)
	bestAlt, _ := lambda(bestAlts)
	fmt.Println("Première alternative :", bestAlt)
}

func SWFFactory(swf func(Profile) (Count, error), tb func([]Alternative) (Alternative, error)) func(Profile) ([]Alternative, error) {

	return func(p Profile) ([]Alternative, error) {
		//récupération du décompte
		count, errSWF := swf(p)
		if errSWF != nil {
			return nil, errSWF
		}
		//préparation de la sortie
		var sortedAlts []Alternative

		//PARCOURS DU DECOMPTE
		for len(count) > 0 {
			//On prend les meilleures alternatives (avant tie break)
			bestAlts := maxCount(count)
			//On supprime les meilleures alternatives du décompte
			for alt := range bestAlts {
				delete(count, Alternative(alt))
			}
			//Départage
			for len(bestAlts) > 0 {
				bestAlt, errTB := tb(bestAlts)
				if errTB != nil {
					return nil, errTB
				}
				//ajout de la meilleure alternative post-tie break
				sortedAlts = append(sortedAlts, bestAlt)
				//suppression de l'alternative dans bestAlts
				indice := rank(bestAlt, bestAlts)
				bestAlts = append(bestAlts[:indice], bestAlts[indice+1:]...)
				//suppression de l'alternativ dans le compte
				delete(count, Alternative(bestAlt))
			}
		}
		return sortedAlts, nil
	}
}

func Test_sWFFactory() {
	//Définition de l'Ordre strict
	orderedAlts := []Alternative{8, 9, 6, 1, 3, 2}
	fmt.Println("Ordre strict :", orderedAlts)

	//Construction d'un profil avec alternatives ex aequo
	profil := make([][]Alternative, 2)
	profil[0] = []Alternative{1, 2, 3, 4, 5, 6}
	profil[1] = []Alternative{3, 2, 1, 4, 5, 6}
	fmt.Println("Profil :", profil)

	//Construction de la fonction Tie Break
	lambda := TieBreakFactory(orderedAlts)
	//Construction de la Social Welfare Factory à partir de la fonction swf + la fonction de TieBreak
	mu := SWFFactory(MajoritySWF, lambda)
	// mu := SWFFactory(BordaSWF, lambda)
	//Construction d'une fonction
	sorted_alts, _ := mu(profil)
	fmt.Println("Alternatives strictement ordonnées selon la méthode de Borda :", sorted_alts)
}


func SWFFactoryWithOptions(
	swf func(Profile, []int) (Count, error),
	tb func([]Alternative) (Alternative, error),
) func(Profile, []int) ([]Alternative, error) {

	return func(p Profile, o []int) ([]Alternative, error) {
		//récupération du décompte
		count, errSWF := swf(p, o)
		if errSWF != nil {
			return nil, errSWF
		}
		//préparation de la sortie
		var sortedAlts []Alternative

		//PARCOURS DU DECOMPTE
		for len(count) > 0 {
			//On prend les meilleures alternatives (avant tie break)
			bestAlts := maxCount(count)
			//On supprime les meilleures alternatives du décompte
			for alt := range bestAlts {
				delete(count, Alternative(alt))
			}
			//Départage
			for len(bestAlts) > 0 {
				bestAlt, errTB := tb(bestAlts)
				if errTB != nil {
					return nil, errTB
				}
				//ajout de la meilleure alternative post-tie break
				sortedAlts = append(sortedAlts, bestAlt)
				//suppression de l'alternative dans bestAlts
				indice := rank(bestAlt, bestAlts)
				bestAlts = append(bestAlts[:indice], bestAlts[indice+1:]...)
				//suppression de l'alternativ dans le compte
				delete(count, Alternative(bestAlt))
			}
		}
		return sortedAlts, nil
	}
}


func SCFFactoryWithOptions(
	scf func(Profile, []int) ([]Alternative, error),
	tb func([]Alternative) (Alternative, error),
) func(Profile, []int) (Alternative, error) {
	return func(p Profile, o []int) (Alternative, error) {
		//récupération des meilleures alternatives
		bestAlts, errSCF := scf(p, o)
		if errSCF != nil {
			return Alternative(0), errSCF
		}
		//récupération de la meilleure alternative
		bestAlt, errTB := tb(bestAlts)
		return bestAlt, errTB
	}
}

func SCFFactory(scf func(p Profile) ([]Alternative, error), tb func([]Alternative) (Alternative, error)) func(Profile) (Alternative, error) {
	return func(p Profile) (Alternative, error) {
		//récupération des meilleures alternatives
		bestAlts, errSCF := scf(p)
		if errSCF != nil {
			return Alternative(0), errSCF
		}
		//récupération de la meilleure alternative
		bestAlt, errTB := tb(bestAlts)
		return bestAlt, errTB
	}
}


func Test_sCFFactory() {
	//Définition de l'Ordre strict
	orderedAlts := []Alternative{8, 9, 6, 1, 3, 2}
	fmt.Println("Ordre strict :", orderedAlts)

	//Construction d'un profil avec alternatives ex aequo
	profil := make([][]Alternative, 2)
	profil[0] = []Alternative{1, 2, 3, 4, 5, 6}
	profil[1] = []Alternative{3, 2, 1, 4, 5, 6}
	fmt.Println("Profil :", profil)

	//Construction de la fonction Tie Break
	lambda := TieBreakFactory(orderedAlts)
	mu := SCFFactory(BordaSCF, lambda)
	//Construction d'une fonction
	best_Alt, _ := mu(profil)
	fmt.Println("Meilleure alternative selon la méthode de Borda :", best_Alt)
}