# include <ohana.h>
# include <gfitsio.h>
# include <zlib.h>

/* functions defined in ricecomp.c */
int fits_rcomp(int a[], int nx,	unsigned char *c, int clen, int nblock);
int fits_rdecomp (unsigned char *c, int clen, unsigned int array[], int nx, int nblock);
int fits_rdecomp_short (unsigned char *c, int clen, unsigned short array[], int nx, int nblock);
int fits_rdecomp_byte (unsigned char *c, int clen, unsigned char array[], int nx, int nblock);

/* functions defined in fits_hcompress.c */
# define LONGLONG long long
int fits_hcompress(int *a, int ny, int nx, int scale, char *output, long *nbytes, int *status);
int fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, char *output, long *nbytes, int *status);

/* functions defined in fits_hdeccompress.c */
int fits_hdecompress(unsigned char *input, int smooth, int *a, int *ny, int *nx, int *scale, int *status);
int fits_hdecompress64(unsigned char *input, int smooth, LONGLONG *a, int *ny, int *nx, int *scale, int *status);

/* functions defined in pliocomp.c */
int pl_p2li (int *pxsrc, int xs, short *lldst, int npix);
int pl_l2pi (short *ll_src, int xs, int *px_dst, int npix);

/* functions defined in gzip.c */
int gfits_gz_stripheader (unsigned char *data, int *ndata);
int gfits_uncompress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);

int gfits_uncompress_data (char *zdata, int Nzdata, char *cmptype, char **optname, char **optvalue, int Nopt, char *outdata, int *Nout, int out_pixsize) {

  int status;
  static int Ninsum = 0;
  static int Noutsum = 0;

  if (!strcasecmp(cmptype, "GZIP_1")) {
    unsigned long tNout = *Nout * out_pixsize;

    // for GZIP data, I need to check for and remove the header on the first block:
    gfits_gz_stripheader ((unsigned char *)zdata, &Nzdata);

    // uncompress does not require us to know the expected number of pixel; it tells us the number
    status = gfits_uncompress ((Bytef *) outdata, &tNout, (Bytef *) zdata, Nzdata);
    if (status != Z_OK) {
      fprintf (stderr, "error in uncompress (GZIP)\n");
      return (FALSE);
    }
    *Nout = tNout / out_pixsize;

    // the resulting uncompressed data is byteswapped
    if (!gfits_byteswap_zdata (outdata, *Nout, out_pixsize)) return (FALSE);

    return (TRUE);
  }

  if (!strcasecmp(cmptype, "RICE_1")) {
    int i, blocksize;
    // look for the BLOCKSIZE
    blocksize = 32;
    for (i = 0; i < Nopt; i++) {
      if (!strcmp(optname[i], "BLOCKSIZE")) {
	blocksize = atoi (optvalue[i]);
	if ((blocksize != 16) && (blocksize != 32)) {
	  fprintf (stderr, "RICE blocksize is not valid: %d (%s = %s)\n", blocksize, optname[i], optvalue[i]);
	  return (FALSE);
	}
      }
    }

    int Npix;
    // Npix = *Nout * (out_pixsize / 4.0);
    Npix = *Nout;

    Ninsum += Nzdata;
    Noutsum += *Nout;
    // fprintf (stderr, "%d comp bytes; %d uncomp 'pixels', totals: %d %d\n", Nzdata, Npix, Ninsum, Noutsum);

    switch (out_pixsize) {
      case 4:
	// rice decompression from the CFITSIO source tree : we need to tell it the expected number of pixels
	// is also REQUIRES 4byte output, which is fairly stupid.
	if (fits_rdecomp ((unsigned char *) zdata, Nzdata, (unsigned int *) outdata, Npix, blocksize)) {
	  fprintf (stderr, "error in rice decompression\n");
	  return (FALSE);
	}
	return (TRUE);

      case 2:
	if (fits_rdecomp_short ((unsigned char *) zdata, Nzdata, (unsigned short *) outdata, Npix, blocksize)) {
	  fprintf (stderr, "error in rice decompression\n");
	  return (FALSE);
	}
	return (TRUE);

      case 1:
	if (fits_rdecomp_byte ((unsigned char *) zdata, Nzdata, (unsigned char *) outdata, Npix, blocksize)) {
	  fprintf (stderr, "error in rice decompression\n");
	  return (FALSE);
	}
	return (TRUE);
	
      default:
	fprintf (stderr, "invalid output pixel size %d\n", out_pixsize);
	return (FALSE);
    }
    
  }
  
  if (!strcasecmp(cmptype, "PLIO_1")) {
    int Npix;
    Npix = pl_l2pi ((short *) zdata, 1, (int *) outdata, *Nout);
    if (Npix != *Nout) {
      fprintf (stderr, "error in plio decompression\n");
      return (FALSE);
    }
    return (TRUE);
  }

  if (!strcasecmp(cmptype, "HCOMPRESS_1")) {
    int Nx, Ny, scale;
    status = 0;
    // call hdecompress without smoothing
    fits_hdecompress ((unsigned char *) zdata, FALSE, (int *) outdata, &Nx, &Ny, &scale, &status);
    if (status) {
      fprintf (stderr, "error in hdecompress\n");
      return (FALSE);
    }
    // fprintf (stderr, "decompression yields image %d x %d (scale: %d)\n", Nx, Ny, scale);
    
    if (Nx * Ny != *Nout) {
      fprintf (stderr, "error in hdecompress: mismatched output size\n");
      return (FALSE);
    }
    return (TRUE);
  }

  fprintf (stderr, "unknown compression %s\n", cmptype);
  return (FALSE);
}

