package fr.iutfbleau.sae.mpif;
import java.io.IOException;
import java.io.InputStream;

/**
 * Décorateur de flux permettant la lecture binaire à granularité du bit.
 * <p>
 * Cette classe encapsule un {@link InputStream} existant et fournit
 * des opérations de lecture bit par bit ou par groupes de bits.
 * Elle ne gère ni l'ouverture ni la sélection du fichier source.
 * </p>
 * 
 * <p>
 * Utilisée notamment pour le décodage des fichiers compressés
 * (ex : format PIF utilisant des codes de Huffman).
 * </p>
 * 
 * 
 * 
 * @author Algassimou Pellel Diallo
 * @version 1.0
 * @since 2025-12-13
 */
public class BitInputStream {

    /** Flux d'entrée sous-jacent */
    private final InputStream fluxEntree;

    /** Octet actuellement chargé depuis le flux */
    private int octetCourant;

    /** Position du bit courant dans l'octet (du bit 7 au bit 0) */
    private int positionBit;

    /** Indique si la fin du flux a été atteinte */
    private boolean finDeFlux;

    /**
     * Construit un lecteur binaire à partir d'un flux existant.
     *
     * @param fluxEntree flux d'entrée à décorer
     * @throws IllegalArgumentException si le flux est nul
     */
    public BitInputStream(InputStream fluxEntree) {
        if (fluxEntree == null) {
            throw new IllegalArgumentException("Le flux d'entrée ne peut pas être nul");
        }
        this.fluxEntree = fluxEntree;
        this.octetCourant = 0;
        this.positionBit = -1; // force la lecture d'un nouvel octet
        this.finDeFlux = false;
    }

    /**
     * Lit un bit depuis le flux binaire.
     *
     * @return 0 ou 1 si un bit est lu, -1 si la fin du flux est atteinte
     * @throws IOException si une erreur de lecture survient
     */
    public int readBit() throws IOException {
        if (finDeFlux) {
            return -1;
        }

        if (this.positionBit < 0) {
            int octetLu = this.fluxEntree.read();
            if (octetLu == -1) {
                this.finDeFlux = true;
            } else {
                this.octetCourant = octetLu;
                this.positionBit = 7;
            }
        }
        
        if (finDeFlux) {
            return -1;
        }

        int bit = (this.octetCourant >> this.positionBit) & 1;
        this.positionBit--;
        return bit;
    }

    /**
     * Lit une séquence de bits consécutifs et les assemble dans un entier.
     *
     * @param nombreBits nombre de bits à lire (strictement positif)
     * @return valeur entière correspondant aux bits lus,
     *         ou -1 si la fin du flux est atteinte prématurément
     * @throws IOException si une erreur de lecture survient
     */
    public int readBits(int nombreBits) throws IOException {
        int res=0;
        for (int i = 0; i < nombreBits; i++) {
            int bit = readBit();
            if (bit == -1) {
                return -1;
            }
            res = (res << 1) | bit;
        }
        return res;
    }

    /**
     * Ferme le flux d'entrée sous-jacent.
     *
     * @throws IOException si une erreur survient lors de la fermeture
     */
    public void closeFlux() throws IOException {
        this.fluxEntree.close();
    }



}
