#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "fitsio.h"

#include "imcopy.hpp"
#include "io.hpp"

int imcopy(std::string filename)
//int main(int argc, char *argv[])
{
   LOG_trace(__func__);

    fitsfile *infptr, *outfptr;   /* FITS file pointers defined in fitsio.h */
    int status = 0, tstatus, ii = 1, iteration = 0, single = 0, hdupos;
    int hdutype, bitpix, bytepix, naxis = 0, nkeys, datatype = 0, anynul;
    long naxes[9] = {1, 1, 1, 1, 1, 1, 1, 1, 1};
    long long first, totpix = 0, npix;
    double *array, bscale = 1.0, bzero = 0.0, nulval = 0.;
    char card[81];

    /* Open the input file and create output file */
    std::cerr << "BEFORE fits_open_file()" << std::endl;

    fits_open_file(&infptr, filename.c_str(), READONLY, &status);

    fits_close_file(infptr, &status);

      return 0;

   // FINAL: ideally re-implement (with buffering) fits_copy_image_section 
   // relies on func set for each type:
   //  fits_read_subset_dbl  ffgsvd
   //   fits_write_imgnull_dbl ffppnd
   //
   //   but copies always entire image in one step which for huge cuts runs out of mem.
   //   e.g. re-do but in smaller chunks
   // OR check is there something similar already done ?  




    std::cerr << "AFTER  fits_open_file() stsus: " << status << std::endl;
   LOG_STREAM << "fits_open_file()" << std::endl;
      LOG_STREAM.flush();
    //status = CREATE_DISK_FILE;
    fits_create_file(&outfptr, "-", &status);
    std::cerr << "AFTER  fits_create_file()" << std::endl;
    //fits_create_file(&outfptr, argv[2], &status);


   // NOTES two independent problems:
   // * redirect to stdout '-' causes 100% mem usage and 'crash'
   // * extended syntax with pixel ranges at fits_open will reserve memory for all the area
   //   opening with fits_diskfile (no extended syntax) -> mem at same level, ok.
   // * e.g. copying wihtout extended-syntax to filename works ok also for big files: copies all file
   //
   // After seeing cfitsio ources cfileio.c :
   // imcopy could be implemented with only fit_open_file(); and fits_close_file() with filename:
   // /path/to/fitsfile.fits(temp_cut_uniqfname.fits)[a:B c:d]
   //
   // open and closing such file creates temp_cut_uniqfname.fits with the subimg
   // if fname not given fitsfile is created in memory -> problem if it is too big
   // Dirty solution: 
   // check size based on filterstring [a:b c:d ...] -> pixelsize
   // if size > LIMIT_IN_PIXELS=100*1024*1024 : assuming double 4 bytes -> 4*LIMIT_IN_PIXELS ~ 500MB
   //    use filename(tempfilename)[] -> operate on disk and then open tempfilename and stream it to stdout
   // else
   //    do as now: outfile = '-' & infile = filename[pixfilter]
   //

    // in detail:
   // what does '-' do to fits_create_file (how is redirect implemented in cfitsio) ?
   // what does extended syntax[pixels] do to fits_open_file ?


   LOG_STREAM << "fits_create_file()" << std::endl;

      LOG_STREAM.flush();

    if (status != 0) {    
        fits_report_error(stderr, status);
        return(status);
    }

    fits_get_hdu_num(infptr, &hdupos);  /* Get the current HDU position */
         std::cerr << "hdupos: " << hdupos << std::endl;

    /* Copy only a single HDU if a specific extension was given */ 
    //if (hdupos != 1 || strchr(filename.c_str(), '[')) single = 1;

    //for (; !status; hdupos++)  /* Main loop through each extension */
    {

      fits_get_hdu_type(infptr, &hdutype, &status);

      if (hdutype == IMAGE_HDU) {
         std::cerr << "hdutype IMAGE_HDU: " << hdutype << std::endl;

          /* get image dimensions and total number of pixels in image */
          for (ii = 0; ii < 9; ii++)
              naxes[ii] = 1;

          fits_get_img_param(infptr, 9, &bitpix, &naxis, naxes, &status);

          totpix = naxes[0] * naxes[1] * naxes[2] * naxes[3] * naxes[4]
             * naxes[5] * naxes[6] * naxes[7] * naxes[8];
      }

      if (hdutype != IMAGE_HDU || naxis == 0 || totpix == 0) { 

         std::cerr << "not IMAGE_HDO ???" << std::endl;

          /* just copy tables and null images */
          //fits_copy_hdu(infptr, outfptr, 0, &status);

      } else {

         std::cerr << "bitpix naxis naxes[]: " << bitpix << " " << naxis;
          LOG_STREAM << "bitpix naxis naxes[]: " << bitpix << " " << naxis;
          for(int ii=0; ii<10; ii++) std::cerr << " "<< naxes[ii];
          for(int ii=0; ii<10; ii++) LOG_STREAM << " "<< naxes[ii];
          std::cerr << std::endl;
          LOG_STREAM << std::endl;

          /* Explicitly create new image, to support compression */
          fits_create_img(outfptr, bitpix, naxis, naxes, &status);
          if (status) {
                 fits_report_error(stderr, status);
                 return(status);
          }
/*
          if (fits_is_compressed_image(outfptr, &status)) {

             LOG_STREAM << "creating EXTNAME COMPRESSED_IMAGE" << std::endl;

             /* write default EXTNAME keyword if it doesn't already exist * /
             tstatus = 0;
             fits_read_card(infptr, "EXTNAME", card, &tstatus);
             if (tstatus) {
                strcpy(card, "EXTNAME = 'COMPRESSED_IMAGE'   / name of this binary table extension");
                fits_write_record(outfptr, card, &status);
             }
          }
*/
          /* copy all the user keywords (not the structural keywords) */
          fits_get_hdrspace(infptr, &nkeys, NULL, &status); 
         std::cerr << "nkeys: " << nkeys << std::endl;

          for (ii = 1; ii <= nkeys; ii++) {
             fits_read_record(infptr, ii, card, &status);
             if (fits_get_keyclass(card) > TYP_CMPRS_KEY)
                fits_write_record(outfptr, card, &status);
          }

          /* delete default EXTNAME keyword if it exists */
          /*
             if (!fits_is_compressed_image(outfptr, &status)) {
             tstatus = 0;
             fits_read_key(outfptr, TSTRING, "EXTNAME", card, NULL, &tstatus);
             if (!tstatus) {
             if (strcmp(card, "COMPRESSED_IMAGE") == 0)
             fits_delete_key(outfptr, "EXTNAME", &status);
             }
             }
             */

          switch(bitpix) {
             case BYTE_IMG:
                datatype = TBYTE;
                break;
             case SHORT_IMG:
                datatype = TSHORT;
                break;
             case LONG_IMG:
                datatype = TINT;
                break;
             case FLOAT_IMG:
                datatype = TFLOAT;
                break;
             case DOUBLE_IMG:
                datatype = TDOUBLE;
                break;
          }

          bytepix = abs(bitpix) / 8;

          npix = totpix;
          iteration = 0;

          while(npix > 16*1024*1024) npix = npix / 2;

          /* try to allocate memory for the entire image */
          /* use double type to force memory alignment */
          array = (double *) calloc(npix, bytepix);

          /* if allocation failed, divide size by 2 and try again */
          while (!array && iteration < 10)  {
             iteration++;
             npix = npix / 2;
             array = (double *) calloc(npix, bytepix);
          }

          LOG_STREAM << "npix bytepix totpix :" << npix << " " << bytepix << " " << totpix << std::endl;
          std::cerr << "npix bytepix totpix :" << npix << " " << bytepix << " " << totpix << std::endl;

          if (!array)  {
             printf("Memory allocation error\n");
             return(0);
          }

          /* turn off any scaling so that we copy the raw pixel values */
          fits_set_bscale(infptr,  bscale, bzero, &status);
          fits_set_bscale(outfptr, bscale, bzero, &status);

          first = 1;
          while (totpix > 0 && !status)
          {
         //    LOG_STREAM << " "<< first ; LOG_STREAM.flush();
         //    std::cerr << " "<< first ;
             /* read all or part of image then write it back to the output file */
             fits_read_img(infptr, datatype, first, npix, 
                   &nulval, array, &anynul, &status);


             fits_write_img(outfptr, datatype, first, npix, array, &status);
             fits_flush_file(outfptr, &status);

             totpix = totpix - npix;
             first  = first  + npix;
          }
          LOG_STREAM << " X "<< first << std::endl;LOG_STREAM.flush();
          std::cerr << " X "<< first << std::endl;

          free(array);
          LOG_STREAM << " mem freed " << std::endl;LOG_STREAM.flush();
          std::cerr << " mem freed " << std::endl;

          fits_close_file(infptr, &status);
          std::cerr << " close file in " << std::endl;


      }

      //if (single) break;  /* quit if only copying a single HDU */
      //fits_movrel_hdu(infptr, 1, NULL, &status);  /* try to move to next HDU */
    }

          std::cerr << " HDU done " << std::endl;
    if (status == END_OF_FILE)  status = 0; /* Reset after normal error */

    fits_close_file(outfptr,  &status);
          std::cerr << " close file out " << std::endl;
    /* if error occurred, print out error message */
    //if (status)
    //   fits_report_error(stderr, status);
    return(status);
}
