/*
* 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 and Antonin Descampe
* 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 "opj_includes.h"
#include "mj2.h"

/*  -----------------------        */
/*              */
/*              */
/*  Count the number of frames        */
/*  in a YUV file          */
/*              */
/*  -----------------------        */

int yuv_num_frames(mj2_tk_t * tk, char *infile)
{
  int numimages, frame_size;
  long end_of_f;
  FILE *f;

  f = fopen(infile,"rb");
  if (!f) {
    fprintf(stderr, "failed to open %s for reading\n",infile);
    return -1;
  }

  frame_size = (int) (tk->w * tk->h * (1.0 + (double) 2 / (double) (tk->CbCr_subsampling_dx * tk->CbCr_subsampling_dy)));  /* Calculate frame size */

  fseek(f, 0, SEEK_END);
  end_of_f = ftell(f);    /* Calculate file size */

  if (end_of_f < frame_size) {
    fprintf(stderr,
      "YUV does not contains any frame of %d x %d size\n", tk->w,
      tk->h);
    return -1;
  }

  numimages = end_of_f / frame_size;  /* Calculate number of images */
  fclose(f);

  return numimages;
}

//  -----------------------
//
//
//  YUV to IMAGE
//
//  -----------------------

opj_image_t *mj2_image_create(mj2_tk_t * tk, opj_cparameters_t *parameters)
{
  opj_image_cmptparm_t cmptparm[3];
  opj_image_t * img;
  int i;
  int numcomps = 3;
  int subsampling_dx = parameters->subsampling_dx;
  int subsampling_dy = parameters->subsampling_dy;

  /* initialize image components */
  memset(&cmptparm[0], 0, 3 * sizeof(opj_image_cmptparm_t));
  for(i = 0; i < numcomps; i++) {
    cmptparm[i].prec = 8;
    cmptparm[i].bpp = 8;
    cmptparm[i].sgnd = 0;
    cmptparm[i].dx = i ? subsampling_dx * tk->CbCr_subsampling_dx : subsampling_dx;
    cmptparm[i].dy = i ? subsampling_dy * tk->CbCr_subsampling_dy : subsampling_dy;
    cmptparm[i].w = tk->w;
    cmptparm[i].h = tk->h;
  }
  /* create the image */
  img = opj_image_create(numcomps, cmptparm, CLRSPC_SRGB);
  return img;
}

char yuvtoimage(mj2_tk_t * tk, opj_image_t * img, int frame_num, opj_cparameters_t *parameters, char* infile)
{
  int i, compno;
  int offset;
  long end_of_f, position;
  int numcomps = 3;
  int subsampling_dx = parameters->subsampling_dx;
  int subsampling_dy = parameters->subsampling_dy;
  FILE *yuvfile;

  yuvfile = fopen(infile,"rb");
  if (!yuvfile) {
    fprintf(stderr, "failed to open %s for readings\n",parameters->infile);
    return 1;
  }

  offset = (int) ((double) (frame_num * tk->w * tk->h) * (1.0 +
    1.0 * (double) 2 / (double) (tk->CbCr_subsampling_dx * tk->CbCr_subsampling_dy)));
  fseek(yuvfile, 0, SEEK_END);
  end_of_f = ftell(yuvfile);
  fseek(yuvfile, sizeof(unsigned char) * offset, SEEK_SET);
  position = ftell(yuvfile);
  if (position >= end_of_f) {
    fprintf(stderr, "Cannot reach frame number %d in yuv file !!\n",
      frame_num);
    fclose(yuvfile);
    return 1;
  }

  img->x0 = tk->Dim[0];
  img->y0 = tk->Dim[1];
  img->x1 = !tk->Dim[0] ? (tk->w - 1) * subsampling_dx + 1 : tk->Dim[0] +
    (tk->w - 1) * subsampling_dx + 1;
  img->y1 = !tk->Dim[1] ? (tk->h - 1) * subsampling_dy + 1 : tk->Dim[1] +
    (tk->h - 1) * subsampling_dy + 1;

  for(compno = 0; compno < numcomps; compno++) {
    for (i = 0; i < (tk->w * tk->h / (img->comps[compno].dx * img->comps[compno].dy))
      && !feof(yuvfile); i++) {
      if (!fread(&img->comps[compno].data[i], 1, 1, yuvfile)) {
        fprintf(stderr, "Error reading %s file !!\n", infile);
        return 1;
      }
    }
  }
  fclose(yuvfile);

  return 0;
}



//  -----------------------
//
//
//  IMAGE to YUV
//
//  -----------------------


bool imagetoyuv(opj_image_t * img, char *outfile)
{
  FILE *f;
  int i;

  if (img->numcomps == 3) {
    if (img->comps[0].dx != img->comps[1].dx / 2
      || img->comps[1].dx != img->comps[2].dx) {
      fprintf(stderr,
        "Error with the input image components size: cannot create yuv file)\n");
      return false;
    }
  } else if (!(img->numcomps == 1)) {
    fprintf(stderr,
      "Error with the number of image components(must be one or three)\n");
    return false;
  }

  f = fopen(outfile, "a+b");
  if (!f) {
    fprintf(stderr, "failed to open %s for writing\n", outfile);
    return false;
  }


  for (i = 0; i < (img->comps[0].w * img->comps[0].h); i++) {
    unsigned char y;
    y = img->comps[0].data[i];
    fwrite(&y, 1, 1, f);
  }


  if (img->numcomps == 3) {
    for (i = 0; i < (img->comps[1].w * img->comps[1].h); i++) {
      unsigned char cb;
      cb = img->comps[1].data[i];
      fwrite(&cb, 1, 1, f);
    }


    for (i = 0; i < (img->comps[2].w * img->comps[2].h); i++) {
      unsigned char cr;
      cr = img->comps[2].data[i];
      fwrite(&cr, 1, 1, f);
    }
  } else if (img->numcomps == 1) {
    for (i = 0; i < (img->comps[0].w * img->comps[0].h * 0.25); i++) {
      unsigned char cb = 125;
      fwrite(&cb, 1, 1, f);
    }


    for (i = 0; i < (img->comps[0].w * img->comps[0].h * 0.25); i++) {
      unsigned char cr = 125;
      fwrite(&cr, 1, 1, f);
    }
  }
  fclose(f);
  return true;
}

//  -----------------------
//
//
//  IMAGE to BMP
//
//  -----------------------

int imagetobmp(opj_image_t * img, char *outfile) {
  int w,wr,h,hr,i,pad;
  FILE *f;

  if (img->numcomps == 3 && img->comps[0].dx == img->comps[1].dx
    && img->comps[1].dx == img->comps[2].dx
    && img->comps[0].dy == img->comps[1].dy
    && img->comps[1].dy == img->comps[2].dy
    && img->comps[0].prec == img->comps[1].prec
    && img->comps[1].prec == img->comps[2].prec) {
    /* -->> -->> -->> -->>

      24 bits color

    <<-- <<-- <<-- <<-- */

    f = fopen(outfile, "wb");
    if (!f) {
      fprintf(stderr, "failed to open %s for writing\n", outfile);
      return 1;
    }

    w = img->comps[0].w;
    wr = int_ceildivpow2(img->comps[0].w, img->comps[0].factor);

    h = img->comps[0].h;
    hr = int_ceildivpow2(img->comps[0].h, img->comps[0].factor);

    fprintf(f, "BM");

    /* FILE HEADER */
    /* ------------- */
    fprintf(f, "%c%c%c%c",
      (unsigned char) (hr * wr * 3 + 3 * hr * (wr % 2) +
      54) & 0xff,
      (unsigned char) ((hr * wr * 3 + 3 * hr * (wr % 2) + 54)
      >> 8) & 0xff,
      (unsigned char) ((hr * wr * 3 + 3 * hr * (wr % 2) + 54)
      >> 16) & 0xff,
      (unsigned char) ((hr * wr * 3 + 3 * hr * (wr % 2) + 54)
      >> 24) & 0xff);
    fprintf(f, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff,
      ((0) >> 16) & 0xff, ((0) >> 24) & 0xff);
    fprintf(f, "%c%c%c%c", (54) & 0xff, ((54) >> 8) & 0xff,
      ((54) >> 16) & 0xff, ((54) >> 24) & 0xff);

    /* INFO HEADER   */
    /* ------------- */
    fprintf(f, "%c%c%c%c", (40) & 0xff, ((40) >> 8) & 0xff,
      ((40) >> 16) & 0xff, ((40) >> 24) & 0xff);
    fprintf(f, "%c%c%c%c", (unsigned char) ((wr) & 0xff),
      (unsigned char) ((wr) >> 8) & 0xff,
      (unsigned char) ((wr) >> 16) & 0xff,
      (unsigned char) ((wr) >> 24) & 0xff);
    fprintf(f, "%c%c%c%c", (unsigned char) ((hr) & 0xff),
      (unsigned char) ((hr) >> 8) & 0xff,
      (unsigned char) ((hr) >> 16) & 0xff,
      (unsigned char) ((hr) >> 24) & 0xff);
    fprintf(f, "%c%c", (1) & 0xff, ((1) >> 8) & 0xff);
    fprintf(f, "%c%c", (24) & 0xff, ((24) >> 8) & 0xff);
    fprintf(f, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff,
      ((0) >> 16) & 0xff, ((0) >> 24) & 0xff);
    fprintf(f, "%c%c%c%c",
      (unsigned char) (3 * hr * wr +
      3 * hr * (wr % 2)) & 0xff,
      (unsigned char) ((hr * wr * 3 + 3 * hr * (wr % 2)) >>
      8) & 0xff,
      (unsigned char) ((hr * wr * 3 + 3 * hr * (wr % 2)) >>
      16) & 0xff,
      (unsigned char) ((hr * wr * 3 + 3 * hr * (wr % 2)) >>
      24) & 0xff);
    fprintf(f, "%c%c%c%c", (7834) & 0xff, ((7834) >> 8) & 0xff,
      ((7834) >> 16) & 0xff, ((7834) >> 24) & 0xff);
    fprintf(f, "%c%c%c%c", (7834) & 0xff, ((7834) >> 8) & 0xff,
      ((7834) >> 16) & 0xff, ((7834) >> 24) & 0xff);
    fprintf(f, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff,
      ((0) >> 16) & 0xff, ((0) >> 24) & 0xff);
    fprintf(f, "%c%c%c%c", (0) & 0xff, ((0) >> 8) & 0xff,
      ((0) >> 16) & 0xff, ((0) >> 24) & 0xff);

    for (i = 0; i < wr * hr; i++) {
      unsigned char R, G, B;
      /* a modifier */
      // R = img->comps[0].data[w * h - ((i) / (w) + 1) * w + (i) % (w)];
      R = img->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)];
      // G = img->comps[1].data[w * h - ((i) / (w) + 1) * w + (i) % (w)];
      G = img->comps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)];
      // B = img->comps[2].data[w * h - ((i) / (w) + 1) * w + (i) % (w)];
      B = img->comps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)];
      fprintf(f, "%c%c%c", B, G, R);

      if ((i + 1) % wr == 0) {
        for (pad = (3 * wr) % 4 ? 4 - (3 * wr) % 4 : 0; pad > 0; pad--)  /* ADD */
          fprintf(f, "%c", 0);
      }
    }
    fclose(f);
  }
  return 0;
}
