package fr.iutfbleau.sae.mhuffman;

import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

/**
 * 
 * Cette classe construit un arbre de Huffman à partir d'un tableau
 * de fréquences. Une fois l'arbre construit, elle permet aussi de
 * générer les codes Huffman associés à chaque symbole.
 *
 * Le principe est le suivant :
 * - chaque symbole non nul devient une feuille avec sa fréquence
 * - on fusionne toujours les deux plus petites fréquences
 * - on obtient une racine unique
 * - un parcours de l'arbre permet ensuite de fabriquer les codes
 *
 * Les codes sont construits en descendant l'arbre :
 * aller à gauche ajoute "0", aller à droite ajoute "1".
 */
public class HuffmanTree {

    private HuffmanNode root;
    private Map<Integer, String> codes;

    /**
     * Construit l'arbre de Huffman à partir d'un tableau de fréquences.
     *
     * @param freq tableau contenant la fréquence de chaque symbole (0 à 255)
     */
    public HuffmanTree(int[] freq) {

        this.root = null;

        // Création de la file de priorité avec le comparateur
        ComparateurHuffmanNode cmp = new ComparateurHuffmanNode();
        PriorityQueue<HuffmanNode> feuilles = new PriorityQueue<>(cmp);

        // Ajout des feuilles
        for (int i = 0; i < freq.length; i++) {
            if (freq[i] > 0) {
                feuilles.add(new HuffmanNode(i, freq[i]));
            }
        }       
     

        // Fusion des nœuds (feuille) jusqu'à obtenir la racine
        while (feuilles.size() > 1) {
            HuffmanNode left = feuilles.poll();
            HuffmanNode right = feuilles.poll();
            HuffmanNode parent = new HuffmanNode(left, right);
            feuilles.add(parent);
        }

        this.root = feuilles.poll();
    }

    /**
     * Lance la génération des codes Huffman en parcourant l'arbre.
     *
     * @return une map associant chaque symbole à son code Huffman
     */
    public Map<Integer, String> generateCodes() {
        this.codes = new HashMap<>();
        generateCodesRec(this.root, "");
        return this.codes;
    }

    /**
     * Méthode récursive qui construit les codes Huffman.
     *
     * @param node nœud courant
     * @param prefixe code accumulé (suite de 0 et de 1)
     */
    private void generateCodesRec(HuffmanNode node, String prefixe) {

        // Cas feuille : on ajoute le code
        if (node.isLeaf()) {

            if (prefixe.length() > 0) {
                this.codes.put(node.getValue(), prefixe);
            } else {
                // Cas spécial : un seul symbole dans l'arbre
                this.codes.put(node.getValue(), "0");
            }
            return;
        }

        // Descendre à gauche s
        generateCodesRec(node.getLeft(), prefixe + "0");

        // Descendre à droite
        generateCodesRec(node.getRight(), prefixe + "1");
    }

    /**
     * Permet de récupérer la racine de l'arbre.
     *
     * @return la racine de l'arbre de Huffman
     */
    public HuffmanNode getRoot() {
        return this.root;
    }

    /**
     * Retourne les codes Huffman générés.
     *
     * @return une map symbole code
     */
    public Map<Integer, String> getCodes() {
        return this.codes;
    }
}
