package eg.ufe.codecorrecteur;

import java.util.Arrays;

/**
 * Bloc de données transmis. Composé de 3 octets censés représenter le même
 * octet. Cet octet est copié 2 fois au départ, ce qui permet à l'arrivée de
 * repérer et de corriger des erreurs de transmission.
 * 
 * Cette version permet d'obtenir un rapport sur les erreurs de transmission.
 */
public class BlocAvecRapportErreurs {
  private byte[] data = new byte[3];

  public enum TypeErreur {
    CORRECT, CORRECTION, ERREUR
  };

  /**
   * Indique si une erreur a été repérée lors du dernier décodage (voir les
   * constantes ci-dessus).
   */
  private TypeErreur typeErreur = null;

  /** 
   * Indique si le décodage doit rapporter les corrections et erreurs.
   */
  private boolean avecRapport = false;

  /**
   * Construit un bloc en dupliquant 2 fois un octet. Il s'agit du codage de la
   * donnée passée en paramètre.
   * 
   * @param donnee
   *          donnée codée par le bloc.
   * @param avecRapport
   */
  public BlocAvecRapportErreurs(byte donnee) {
    data[0] = donnee;
    data[1] = donnee;
    data[2] = donnee;
  }
  
  public TypeErreur getTypeErreur() {
    return typeErreur;
  }

  // public byte get(int i) {
  // return data[i];
  // }

  /**
   * Retourne la valeur supposée être représentée par le bloc. Version
   * "détaillée", plus simple à trouver que getValeur().
   * 
   * @return la valeur supposée être représentée par le bloc.
   */
  public byte getValeurDecodeeAvecRapport() {
    if (data[0] == data[1] && data[0] == data[2]) {
      // Les 3 sont égaux
      // Dans le cas où il y a peu d'erreurs de transmission, on commence par ce
      // cas.
      typeErreur = TypeErreur.CORRECT;
      return data[0];
    }
    if (data[0] == data[1] || data[0] == data[2]) {
      // Le premier est égal à un des 2 autres donc c'est la bonne valeur dans
      // data[0]
      typeErreur = TypeErreur.CORRECTION;
      return data[0];
    } else {
      if (data[1] == data[2]) {
        // Le 2ème est égal au 3ème mais pas au 1er
        typeErreur = TypeErreur.CORRECTION;
        return data[1];
      } else {
        // 3 valeurs différentes ! On ne peut rien faire pour réparer
        // Pour ce 1er programme, on retourne la valeur de data[0]
        typeErreur = TypeErreur.ERREUR;
        return data[0];
      }
    }
  }

  /**
   * Retourne la valeur supposée être représentée par le bloc. Pas de rapport de
   * ce qui s'est passé pendant le décodage du bloc.
   * 
   * @return la valeur du bloc décodé.
   */
  public byte getValeurDecodee() {
    if (data[1] == data[2]) {
      // Le seul cas où on ne retourne pas data[0]
      return data[1];
    } else {
      return data[0];
    }
  }

  /**
   * Méthode utilitaire pour coder un tableau d'octets en tableau de Bloc.
   * 
   * @param valeurs
   * @return
   */
  public static BlocAvecRapportErreurs[] coder(byte[] valeurs) {
    BlocAvecRapportErreurs[] resultat = new BlocAvecRapportErreurs[valeurs.length];
    for (int i = 0; i < valeurs.length; i++) {
      resultat[i] = new BlocAvecRapportErreurs(valeurs[i]);
    }
    return resultat;
  }

  /**
   * Méthode utilitaire pour décoder un tableau de blocs en tableau d'octet.
   * Pour chaque bloc on enregistre ou non (selon le paramètre avecRapport)
   * s'il y a eu un problème de décodage (erreur de transmission).
   * 
   * @param valeursTransmises
   *          les blocs à décoder
   * @return les octets obtenus à partir des blocs
   */
  public static byte[] decoder(BlocAvecRapportErreurs[] blocsTransmis,
      boolean avecRapport) {
    byte[] resultat = new byte[blocsTransmis.length];
    if (avecRapport) {
      for (int i = 0; i < blocsTransmis.length; i++) {
        resultat[i] = blocsTransmis[i].getValeurDecodeeAvecRapport();
      }
    } else {
      for (int i = 0; i < blocsTransmis.length; i++) {
        resultat[i] = blocsTransmis[i].getValeurDecodee();
      }
    }
    return resultat;
  }
  
  /**
   * Méthode utilitaire pour décoder un tableau de blocs en tableau d'octet
   * en enregistrant s'il y a eu un problème de décodage (erreur de transmission).
   * @param blocsTransmis
   * @return
   */
  public static byte[] decoder(BlocAvecRapportErreurs[] blocsTransmis) {
    return decoder(blocsTransmis, true);
  }

  /**
   * Simule une erreur de transmission du bloc.
   * 
   * @param i
   *          numéro de l'exemplaire de l'octet modifié.
   * @param valeur
   *          valeur modifiée
   */
  public void simulationErreurTransmission(int i, byte valeur) {
    data[i] = valeur;
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return "Bloc {data=" + Arrays.toString(data) + "}";
  }

}