/*
 * Copyright (c) 2002-2007, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium
 * Copyright (c) 2002-2007, Professor Benoit Macq
 * Copyright (c) 2003-2007, Francois-Olivier Devaux
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include "openjpeg.h"
#include "index.h"

/* ------------------------------------------------------------------------------------ */

/**
Write a structured index to a file
@param cstr_info Codestream information
@param index Index filename
@return Returns 0 if successful, returns 1 otherwise
*/
int write_index_file(opj_codestream_info_t *cstr_info, char *index) {
  int tileno, compno, layno, resno, precno, pack_nb, x, y;
  FILE *stream = NULL;
  double total_disto = 0;
/* UniPG>> */
  int tilepartno;
  char disto_on, numpix_on;

#ifdef USE_JPWL
  if (!strcmp(index, JPWL_PRIVATEINDEX_NAME))
    return 0;
#endif /* USE_JPWL */
/* <<UniPG */

  if (!cstr_info)
    return 1;

  stream = fopen(index, "w");
  if (!stream) {
    fprintf(stderr, "failed to open index file [%s] for writing\n", index);
    return 1;
  }

  if (cstr_info->tile[0].distotile)
    disto_on = 1;
  else
    disto_on = 0;

  if (cstr_info->tile[0].numpix)
    numpix_on = 1;
  else
    numpix_on = 0;

  fprintf(stream, "%d %d\n", cstr_info->image_w, cstr_info->image_h);
  fprintf(stream, "%d\n", cstr_info->prog);
  fprintf(stream, "%d %d\n", cstr_info->tile_x, cstr_info->tile_y);
  fprintf(stream, "%d %d\n", cstr_info->tw, cstr_info->th);
  fprintf(stream, "%d\n", cstr_info->numcomps);
  fprintf(stream, "%d\n", cstr_info->numlayers);
  fprintf(stream, "%d\n", cstr_info->numdecompos[0]); /* based on component 0 */

  for (resno = cstr_info->numdecompos[0]; resno >= 0; resno--) {
    fprintf(stream, "[%d,%d] ",
      (1 << cstr_info->tile[0].pdx[resno]), (1 << cstr_info->tile[0].pdx[resno]));  /* based on tile 0 and component 0 */
  }

  fprintf(stream, "\n");
/* UniPG>> */
  fprintf(stream, "%d\n", cstr_info->main_head_start);
/* <<UniPG */
  fprintf(stream, "%d\n", cstr_info->main_head_end);
  fprintf(stream, "%d\n", cstr_info->codestream_size);

  fprintf(stream, "\nINFO ON TILES\n");
  fprintf(stream, "tileno start_pos  end_hd  end_tile   nbparts");
  if (disto_on)
    fprintf(stream,"         disto");
  if (numpix_on)
    fprintf(stream,"     nbpix");
  if (disto_on && numpix_on)
    fprintf(stream,"  disto/nbpix");
  fprintf(stream, "\n");

  for (tileno = 0; tileno < cstr_info->tw * cstr_info->th; tileno++) {
    fprintf(stream, "%4d %9d %9d %9d %9d",
      cstr_info->tile[tileno].tileno,
      cstr_info->tile[tileno].start_pos,
      cstr_info->tile[tileno].end_header,
      cstr_info->tile[tileno].end_pos,
      cstr_info->tile[tileno].num_tps);
    if (disto_on)
      fprintf(stream," %9e", cstr_info->tile[tileno].distotile);
    if (numpix_on)
      fprintf(stream," %9d", cstr_info->tile[tileno].numpix);
    if (disto_on && numpix_on)
      fprintf(stream," %9e", cstr_info->tile[tileno].distotile / cstr_info->tile[tileno].numpix);
    fprintf(stream, "\n");
  }

  for (tileno = 0; tileno < cstr_info->tw * cstr_info->th; tileno++) {
    int start_pos, end_ph_pos, end_pos;
    double disto = 0;
    int max_numdecompos = 0;
    pack_nb = 0;

    for (compno = 0; compno < cstr_info->numcomps; compno++) {
      if (max_numdecompos < cstr_info->numdecompos[compno])
        max_numdecompos = cstr_info->numdecompos[compno];
    }

    fprintf(stream, "\nTILE %d DETAILS\n", tileno);
    fprintf(stream, "part_nb tileno  start_pack num_packs  start_pos end_tph_pos   end_pos\n");
    for (tilepartno = 0; tilepartno < cstr_info->tile[tileno].num_tps; tilepartno++)
      fprintf(stream, "%4d %9d   %9d %9d  %9d %11d %9d\n",
        tilepartno, tileno,
        cstr_info->tile[tileno].tp[tilepartno].tp_start_pack,
        cstr_info->tile[tileno].tp[tilepartno].tp_numpacks,
        cstr_info->tile[tileno].tp[tilepartno].tp_start_pos,
        cstr_info->tile[tileno].tp[tilepartno].tp_end_header,
        cstr_info->tile[tileno].tp[tilepartno].tp_end_pos
        );

    if (cstr_info->prog == LRCP) {  /* LRCP */
      fprintf(stream, "LRCP\npack_nb tileno layno resno compno precno start_pos end_ph_pos end_pos");
      if (disto_on)
        fprintf(stream, " disto");
      fprintf(stream,"\n");

      for (layno = 0; layno < cstr_info->numlayers; layno++) {
        for (resno = 0; resno < max_numdecompos + 1; resno++) {
          for (compno = 0; compno < cstr_info->numcomps; compno++) {
            int prec_max;
            if (resno > cstr_info->numdecompos[compno])
              break;
            prec_max = cstr_info->tile[tileno].pw[resno] * cstr_info->tile[tileno].ph[resno];
            for (precno = 0; precno < prec_max; precno++) {
              start_pos = cstr_info->tile[tileno].packet[pack_nb].start_pos;
              end_ph_pos = cstr_info->tile[tileno].packet[pack_nb].end_ph_pos;
              end_pos = cstr_info->tile[tileno].packet[pack_nb].end_pos;
              disto = cstr_info->tile[tileno].packet[pack_nb].disto;
              fprintf(stream, "%4d %6d %7d %5d %6d  %6d    %6d     %6d %7d",
                pack_nb, tileno, layno, resno, compno, precno, start_pos, end_ph_pos, end_pos);
              if (disto_on)
                fprintf(stream, " %8e", disto);
              fprintf(stream, "\n");
              total_disto += disto;
              pack_nb++;
            }
          }
        }
      }
    } /* LRCP */

    else if (cstr_info->prog == RLCP) {  /* RLCP */
      fprintf(stream, "RLCP\npack_nb tileno resno layno compno precno start_pos end_ph_pos end_pos\n");
      if (disto_on)
        fprintf(stream, " disto");
      fprintf(stream,"\n");

      for (resno = 0; resno < max_numdecompos + 1; resno++) {
        for (layno = 0; layno < cstr_info->numlayers; layno++) {
          for (compno = 0; compno < cstr_info->numcomps; compno++) {
            int prec_max;
            if (resno > cstr_info->numdecompos[compno])
              break;
            prec_max = cstr_info->tile[tileno].pw[resno] * cstr_info->tile[tileno].ph[resno];
            for (precno = 0; precno < prec_max; precno++) {
              start_pos = cstr_info->tile[tileno].packet[pack_nb].start_pos;
              end_ph_pos = cstr_info->tile[tileno].packet[pack_nb].end_ph_pos;
              end_pos = cstr_info->tile[tileno].packet[pack_nb].end_pos;
              disto = cstr_info->tile[tileno].packet[pack_nb].disto;
              fprintf(stream, "%4d %6d %5d %7d %6d %6d %9d   %9d %7d",
                pack_nb, tileno, resno, layno, compno, precno, start_pos, end_ph_pos, end_pos);
              if (disto_on)
                fprintf(stream, " %8e", disto);
              fprintf(stream, "\n");
              total_disto += disto;
              pack_nb++;
            }
          }
        }
      }
    } /* RLCP */

    else if (cstr_info->prog == RPCL) {  /* RPCL */

      fprintf(stream, "RPCL\npack_nb tileno resno precno compno layno start_pos end_ph_pos end_pos");
      if (disto_on)
        fprintf(stream, " disto");
      fprintf(stream,"\n");

      for (resno = 0; resno < max_numdecompos + 1; resno++) {
        int numprec = cstr_info->tile[tileno].pw[resno] * cstr_info->tile[tileno].ph[resno];
        for (precno = 0; precno < numprec; precno++) {
          /* I suppose components have same XRsiz, YRsiz */
          int x0 = cstr_info->tile_Ox + tileno - (int)floor((float)tileno/(float)cstr_info->tw ) * cstr_info->tw * cstr_info->tile_x;
          int y0 = cstr_info->tile_Ox + (int)floor( (float)tileno/(float)cstr_info->tw ) * cstr_info->tile_y;
          int x1 = x0 + cstr_info->tile_x;
          int y1 = y0 + cstr_info->tile_y;
          for (compno = 0; compno < cstr_info->numcomps; compno++) {
            int pcnx = cstr_info->tile[tileno].pw[resno];
            int pcx = (int) pow( 2, cstr_info->tile[tileno].pdx[resno] + cstr_info->numdecompos[compno] - resno );
            int pcy = (int) pow( 2, cstr_info->tile[tileno].pdy[resno] + cstr_info->numdecompos[compno] - resno );
            int precno_x = precno - (int) floor( (float)precno/(float)pcnx ) * pcnx;
            int precno_y = (int) floor( (float)precno/(float)pcnx );
            if (resno > cstr_info->numdecompos[compno])
              break;
            for(y = y0; y < y1; y++) {
              if (precno_y*pcy == y ) {
                for (x = x0; x < x1; x++) {
                  if (precno_x*pcx == x ) {
                    for (layno = 0; layno < cstr_info->numlayers; layno++) {
                      start_pos = cstr_info->tile[tileno].packet[pack_nb].start_pos;
                      end_ph_pos = cstr_info->tile[tileno].packet[pack_nb].end_ph_pos;
                      end_pos = cstr_info->tile[tileno].packet[pack_nb].end_pos;
                      disto = cstr_info->tile[tileno].packet[pack_nb].disto;
                      fprintf(stream, "%4d %6d %5d %6d %6d %7d %9d   %9d %7d",
                        pack_nb, tileno, resno, precno, compno, layno, start_pos, end_ph_pos, end_pos);
                      if (disto_on)
                        fprintf(stream, " %8e", disto);
                      fprintf(stream, "\n");
                      total_disto += disto;
                      pack_nb++;
                    }
                  }
                }/* x = x0..x1 */
              }
            }  /* y = y0..y1 */
          } /* precno */
        } /* compno */
      } /* resno */
    } /* RPCL */

    else if (cstr_info->prog == PCRL) {  /* PCRL */
      /* I suppose components have same XRsiz, YRsiz */
      int x0 = cstr_info->tile_Ox + tileno - (int)floor( (float)tileno/(float)cstr_info->tw ) * cstr_info->tw * cstr_info->tile_x;
      int y0 = cstr_info->tile_Ox + (int)floor( (float)tileno/(float)cstr_info->tw ) * cstr_info->tile_y;
      int x1 = x0 + cstr_info->tile_x;
      int y1 = y0 + cstr_info->tile_y;

      // Count the maximum number of precincts
      int max_numprec = 0;
      for (resno = 0; resno < max_numdecompos + 1; resno++) {
        int numprec = cstr_info->tile[tileno].pw[resno] * cstr_info->tile[tileno].ph[resno];
        if (numprec > max_numprec)
          max_numprec = numprec;
      }

      fprintf(stream, "PCRL\npack_nb tileno precno compno resno layno start_pos end_ph_pos end_pos");
      if (disto_on)
        fprintf(stream, " disto");
      fprintf(stream,"\n");

      for (precno = 0; precno < max_numprec; precno++) {
        for (compno = 0; compno < cstr_info->numcomps; compno++) {
          for (resno = 0; resno < cstr_info->numdecompos[compno] + 1; resno++) {
            int numprec = cstr_info->tile[tileno].pw[resno] * cstr_info->tile[tileno].ph[resno];
            int pcnx = cstr_info->tile[tileno].pw[resno];
            int pcx = (int) pow( 2, cstr_info->tile[tileno].pdx[resno] + cstr_info->numdecompos[compno] - resno );
            int pcy = (int) pow( 2, cstr_info->tile[tileno].pdy[resno] + cstr_info->numdecompos[compno] - resno );
            int precno_x = precno - (int) floor( (float)precno/(float)pcnx ) * pcnx;
            int precno_y = (int) floor( (float)precno/(float)pcnx );
            if (precno >= numprec)
              continue;
            for(y = y0; y < y1; y++) {
              if (precno_y*pcy == y ) {
                for (x = x0; x < x1; x++) {
                  if (precno_x*pcx == x ) {
                    for (layno = 0; layno < cstr_info->numlayers; layno++) {
                      start_pos = cstr_info->tile[tileno].packet[pack_nb].start_pos;
                      end_ph_pos = cstr_info->tile[tileno].packet[pack_nb].end_ph_pos;
                      end_pos = cstr_info->tile[tileno].packet[pack_nb].end_pos;
                      disto = cstr_info->tile[tileno].packet[pack_nb].disto;
                      fprintf(stream, "%4d %6d %6d %6d %5d %7d %9d   %9d %7d",
                        pack_nb, tileno, precno, compno, resno, layno, start_pos, end_ph_pos, end_pos);
                      if (disto_on)
                        fprintf(stream, " %8e", disto);
                      fprintf(stream, "\n");
                      total_disto += disto;
                      pack_nb++;
                    }
                  }
                }/* x = x0..x1 */
              }
            }  /* y = y0..y1 */
          } /* resno */
        } /* compno */
      } /* precno */
    } /* PCRL */

    else {  /* CPRL */
      // Count the maximum number of precincts
      int max_numprec = 0;
      for (resno = 0; resno < max_numdecompos + 1; resno++) {
        int numprec = cstr_info->tile[tileno].pw[resno] * cstr_info->tile[tileno].ph[resno];
        if (numprec > max_numprec)
          max_numprec = numprec;
      }

      fprintf(stream, "CPRL\npack_nb tileno compno precno resno layno start_pos end_ph_pos end_pos");
      if (disto_on)
        fprintf(stream, " disto");
      fprintf(stream,"\n");

      for (compno = 0; compno < cstr_info->numcomps; compno++) {
        /* I suppose components have same XRsiz, YRsiz */
        int x0 = cstr_info->tile_Ox + tileno - (int)floor( (float)tileno/(float)cstr_info->tw ) * cstr_info->tw * cstr_info->tile_x;
        int y0 = cstr_info->tile_Ox + (int)floor( (float)tileno/(float)cstr_info->tw ) * cstr_info->tile_y;
        int x1 = x0 + cstr_info->tile_x;
        int y1 = y0 + cstr_info->tile_y;

        for (precno = 0; precno < max_numprec; precno++) {
          for (resno = 0; resno < cstr_info->numdecompos[compno] + 1; resno++) {
            int numprec = cstr_info->tile[tileno].pw[resno] * cstr_info->tile[tileno].ph[resno];
            int pcnx = cstr_info->tile[tileno].pw[resno];
            int pcx = (int) pow( 2, cstr_info->tile[tileno].pdx[resno] + cstr_info->numdecompos[compno] - resno );
            int pcy = (int) pow( 2, cstr_info->tile[tileno].pdy[resno] + cstr_info->numdecompos[compno] - resno );
            int precno_x = precno - (int) floor( (float)precno/(float)pcnx ) * pcnx;
            int precno_y = (int) floor( (float)precno/(float)pcnx );
            if (precno >= numprec)
              continue;

            for(y = y0; y < y1; y++) {
              if (precno_y*pcy == y ) {
                for (x = x0; x < x1; x++) {
                  if (precno_x*pcx == x ) {
                    for (layno = 0; layno < cstr_info->numlayers; layno++) {
                      start_pos = cstr_info->tile[tileno].packet[pack_nb].start_pos;
                      end_ph_pos = cstr_info->tile[tileno].packet[pack_nb].end_ph_pos;
                      end_pos = cstr_info->tile[tileno].packet[pack_nb].end_pos;
                      disto = cstr_info->tile[tileno].packet[pack_nb].disto;
                      fprintf(stream, "%4d %6d %6d %6d %5d %7d %9d   %9d %7d",
                        pack_nb, tileno, compno, precno, resno, layno, start_pos, end_ph_pos, end_pos);
                      if (disto_on)
                        fprintf(stream, " %8e", disto);
                      fprintf(stream, "\n");
                      total_disto += disto;
                      pack_nb++;
                    }
                  }
                }/* x = x0..x1 */
              }
            } /* y = y0..y1 */
          } /* resno */
        } /* precno */
      } /* compno */
    } /* CPRL */
  } /* tileno */

  if (disto_on) {
    fprintf(stream, "%8e\n", cstr_info->D_max); /* SE max */
    fprintf(stream, "%.8e\n", total_disto);  /* SE totale */
  }
/* UniPG>> */
  /* print the markers' list */
  if (cstr_info->marknum) {
    fprintf(stream, "\nMARKER LIST\n");
    fprintf(stream, "%d\n", cstr_info->marknum);
    fprintf(stream, "type\tstart_pos    length\n");
    for (x = 0; x < cstr_info->marknum; x++)
      fprintf(stream, "%X\t%9d %9d\n", cstr_info->marker[x].type, cstr_info->marker[x].pos, cstr_info->marker[x].len);
  }
/* <<UniPG */
  fclose(stream);

  fprintf(stderr,"Generated index file %s\n", index);

  return 0;
}
