#!/usr/bin/perl -w

use strict ;

use encoding "utf8" ;
use JSON ;

### Fonctions ###

## Affiche le mode d'emploi du programme ##
sub print_usage
{
	print "Usage : $0 input_file\n" ;
	exit 1 ;
}

## Retourne le minimum de deux entiers ##
sub min
{
	die "min() : premier argument non défini !" if not defined $_[0] ;
	die 'min() : second argument non défini ! ($_[0] = ' . "$_[0])" if not defined $_[1] ;

	return $_[0] if $_[0] < $_[1] ;
	return $_[1] ;
}

## Retourne le maximum de deux entiers ##
sub max
{
	die "max() : premier argument non défini !" if not defined $_[0] ;
	die 'max() : second argument non défini ! ($_[0] = ' . "$_[0])" if not defined $_[1] ;

	return $_[0] if $_[0] > $_[1] ;
	return $_[1] ;
}


### Lecture du fichier d'entrée ###

print_usage() if @ARGV != 1 ;
my $input_file = $ARGV[0] ;
warn "Fichier d'entrée : « $input_file »\n" ;

my $json = new JSON:: ;
$json->relaxed ; # Autoriser des "extensions" au langage (commentaires, virgules en trop)
$json->allow_nonref ; # Autoriser decode à prendre une chaîne

open(INPUT_FILE, $input_file) || die "Erreur lors de l'ouverture du fichier d'entrée « $input_file » !\n" ;
my $descr = $json->decode(join ' ', <INPUT_FILE>) ;
close INPUT_FILE ;


### Parcours des données lues dans le fichier ###

warn "Parcours du fichier…\n" ;

my %competences ; # Hash contenant toutes les compétences et compétences associées
my $score ; # Variable stockant la valeur du tronc entre la compétence principale du tronc et la compétence actuelle

foreach my $tronc (sort keys %$descr)
{
	warn "Compétence « $tronc » :\n" ;

	foreach my $comp (sort keys %{$$descr{$tronc}})
	{
		warn "* Tronc avec « $comp » à ". $$descr{$tronc}{$comp} ."%.\n" ;

		# Association de la compétence courante avec la compétence principale
		# du tronc :
		$score = $$descr{$tronc}{$comp} ;
		$score = max($score, $competences{$tronc}{$comp}) if defined $competences{$tronc}{$comp} ; # Si l'association est déjà définie, on conserve le plus haut score.
		$competences{$tronc}{$comp} = $score ;
		$competences{$comp}{$tronc} = $score ;

		# Associacion de la compétence courante avec les autres compétences
		# du tronc :
		foreach my $c (sort keys %{$$descr{$tronc}})
		{
			next if $c eq $comp ; # On n'associe pas la compétence à elle-même
			my $s = min($$descr{$tronc}{$c}, $score) ;
			$s = max($s, $competences{$c}{$comp}) if defined $competences{$c}{$comp} ; # Si l'association est déjà définie, on conserve le plus haut score.
			$competences{$c}{$comp} = $s ;
			$competences{$comp}{$c} = $s ;
		}

		# Si la compétence principale du tronc actuel est associée à une autre
		# compétence principale de tronc, on doit associer la compétence
		# courante à la compétence principale et toutes les compétences de cet
		# autre tronc :
		foreach my $t (sort keys %$descr)
		{
			next if $t eq $tronc ;
			next if $t eq $comp ;

			if (defined $competences{$tronc}{$t})
			{
				my $s = min($competences{$tronc}{$t}, $score) ;
				$s = max($s, $competences{$t}{$comp}) if defined $competences{$t}{$comp} ; # Si l'association est déjà définie, on conserve le plus haut score.
				$competences{$t}{$comp} = $s ;
				$competences{$comp}{$t} = $s ;

				foreach my $c (sort keys %{$$descr{$t}})
				{
					next if $c eq $tronc ; # Déjà traité
					next if $c eq $comp ; # On n'associe pas la compétence à elle-même

					my $s2 = min($s, $$descr{$t}{$c}) ;
					$s2 = max($s2, $competences{$c}{$comp}) if defined $competences{$c}{$comp} ; # Si l'association est déjà définie, on conserve le plus haut score.
					$competences{$c}{$comp} = $s2 ;
					$competences{$comp}{$c} = $s2 ;
				}
			}
		}
	}
}


### Affichage des troncs par compétence ###
###   et tri par score pour plus tard   ###

warn "\n" ; # Juste pour faire beau
print "== Troncs, par compétence ==\n" ;

# Liste de tous les troncs ; chaque tronc est un hash dans lequel la clef
# '#' contient le score du tronc. Les autres clefs représentent les
# compétences du tronc (avec comme valeur 1) :
my @troncs ;

foreach my $comp (sort keys %competences) # Parcours des troncs
{
	print "\n; $comp :\n" ; # Affichage de la compétence principale

	## Classement des compétences du tronc par score ##
	##    et affichage des troncs par compétence     ##
	my %groups ; # Hash de hash groupant les compétences associées par score
	foreach my $c (sort keys %{$competences{$comp}})
	{
		print ":* $c : $competences{$comp}{$c}%\n" ; # Affichage de la compétence associée, avec son score
		$groups{$competences{$comp}{$c}}{$comp} = 1 ;
		$groups{$competences{$comp}{$c}}{$c} = 1 ;
	}

	## Parcours des groupes créés, et ajout à la liste globale @troncs ##
	foreach my $g (sort keys %groups)
	{
		# Cherche si le tronc existe dans la liste :
		my $same = 0 ;
		foreach my $t (@troncs)
		{
			if ($t->{'#'} == $g) # Score du tronc identique
			{
				foreach my $m (sort keys %{$groups{$g}})
				{
					foreach my $n (sort keys %{$t})
					{
						if ($m eq $n) # Tronc existant trouvé
						{ # On ajoute les compétences à ce tronc :
							foreach my $c (sort keys %{$groups{$g}})
							{
								$t->{$c} = 1 ;
							}
							$same = 1 ;
							last ;
						}
					}
					last if $same ;
				}
			}
		}

		# Pas de tronc existant dans la liste :
		if (! $same)
		{ # On crée donc un nouveau tronc :
			my $t = {} ;
			$t->{'#'} = $g ;
			foreach my $c (sort keys %{$groups{$g}})
			{ # Et on y ajoute les compétences
				$t->{$c} = 1 ;
			}
			push @troncs, $t ;
		}
	}
}


### Affichage des troncs par score ###

print "\n== Troncs, par score ==\n" ;

foreach my $t (
	sort {$a->{'#'} <=> $b->{'#'}} # Tri par ordre de score
 	@troncs)
{
	print "\n; $t->{'#'}% :\n"  ;
	foreach my $c (sort keys %$t)
	{
		next if $c eq '#' ;
		print ":* $c\n" ;
	}
}
