/**
 *   \file uncompress.c
 *   \brief Décompression de fichiers
 *
 *  Ce fichier contient l’implémentation des fonctions nécessaires à la
 *  décompression d’un fichier en `.su` généré par ce programme vers un fichier
 *  en `.ppm` identique à l’original.
 *
 */

#include "uncompress.h"

/**
 *  Lit le nombre de segments en `uint32_t` contenus dans la zone compressée,
 *  puis pour chaque \ref Segment ajoute ses données décompressées au tableau
 *  dynamique de \p t_zone.
 *
 *  \param[in] t_file Fichier d’entrée contenant les segments à lire
 *  \param[out] t_zone La zone dans laquelle stocker les \ref Segment
 */
void read_segments(FILE *t_file, Zone *t_zone) {
  uint64_t nb_segments, i;
  Segment *segment;
  fread(&nb_segments, sizeof(nb_segments), 1, t_file);
  for(i = 0; i < nb_segments; ++i) {
    segment = newSegment(0, 0);
    fread(&segment->left_limit, sizeof(Segment), 1, t_file);
    darrayPushBack(t_zone->segments, segment);
  }
}

/**
 *  Lit les données compressées du fichier d’entrée, tout d’abord le nombre de
 *  zones dans un `uint42_t`, puis pour chaque zone lit les segments. Une fois
 *  une zone lue, elle est ajoutée à \p t_zones.
 *
 *  \param[in] t_file Fichier d’entrée contenant les données compressées
 *  \param[out] t_zones Tableau dynamique contenant les zones lues
 */
void read_compressed_zones(FILE *t_file, darray *t_zones) {
  uint64_t nb_zones, i;
  Zone *zone;
  /* read number of zones */
  fread(&nb_zones, sizeof(nb_zones), 1, t_file);
  for(i = 0; i < nb_zones; ++i) {
    zone = newZone(0, 0, 0);
    /* read RGB into the zone */
    fread(&zone->red, sizeof(zone->red), 3, t_file);
    /* read each segments of the zone and add them to their vector */
    read_segments(t_file, zone);
    /* add the zone to the zones of the image */
    darrayPushBack(t_zones, zone);
  }
}

/**
 *  Lit les données basiques du fichier compressé, à savoir la taille en
 *  abscisse et en ordonnée du fichier ppm d’origine et les inscrit dans \p
 *  t_img.
 *
 *  \param[in] t_file Fichier d’entrée à lire
 *  \param[out] t_img Structure \ref Image stockant les données lues
 */
void read_compressed_file_meta(FILE *t_file, Image *t_img) {
  /* read sizeX and sizeY at once */
  fread(&t_img->sizeX, sizeof(t_img->sizeX), 2, t_file);
}

uint8_t *zones_to_data(darray *t_zones, Image *t_img) {
  uint64_t nb_zones, nb_segments, zoneID, segmentID, k, left_limit, right_limit;
  uint8_t *data, red, green, blue;
  Zone *current_zone;
  Segment *current_segment;
  data = (uint8_t *)malloc(sizeof(uint8_t) * t_img->sizeX * t_img->sizeY * 3);
  nb_zones = darraySize(t_zones);
  for (zoneID = 0; zoneID < nb_zones; ++zoneID) {
    current_zone = darrayGet(t_zones, zoneID);
    red = current_zone->red;
    green = current_zone->green;
    blue = current_zone->blue;
    nb_segments = darraySize(current_zone->segments);
    for (segmentID = 0; segmentID < nb_segments; ++segmentID) {
      current_segment = darrayGet(current_zone->segments, segmentID);
      left_limit = current_segment->left_limit;
      right_limit = current_segment->right_limit;
      for (k = left_limit; k < right_limit; ++k) {
        data[k * 3] = red;
        data[k * 3 + 1] = green;
        data[k * 3 + 2] = blue;
      }
    }
  }
  return data;
}

/**
 *  Décompresse le fichier d’entrée et écrit son équivalent décompressé au
 *  format ppm dans le fichier de sortie.
 *
 *  \param[in] t_input_file Nom/chemin du fichier d’entrée
 *  \param[in] t_output_file Nom/chemin du fichier de sortie
 */
void uncompress(const char *t_input_file, const char *t_output_file) {
  Image *img;
  darray *zones = darrayNew(sizeof(Zone));
  uint8_t *data;
  FILE *input_file;
  uint64_t i;
  if (!t_output_file) {
    t_output_file = DEFAULT_UNCOMPRESSED_FILE;
  }
  img = newImage();
  input_file = get_file(t_input_file, "rb");
  read_compressed_file_meta(input_file, img);
  read_compressed_zones(input_file, zones);
  data = zones_to_data(zones, img);
  imageSavePPM(t_output_file, img, data);
  fclose(input_file);
  /* free memory */
  for(i = 0; i < darraySize(zones); ++i) {
    Zone *zone = darrayGet(zones, i);
    darrayDelete(zone->segments);
  }
  darrayDelete(zones);
}
