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

#define NMAX 20480
#define NGAINMAX 100
#define NTGAINMAX 420

int GAINBIN[1024][1024][10];
int TGAINBIN[1024][1024][10];
int NGAINPOINTS[NMAX];
float PHA[NMAX][NGAINMAX];
float PHASHIFT[NMAX][NTGAINMAX];
float ENERGY[NMAX][NGAINMAX];

float lin_interpolate (float x, float *x0, float *y0, int n);
void locate(float *xx, int n, float x, int *j);

int DO_GAIN=0; int DO_TGAIN=0; int MOD_PHA=0;

main(int argc, char *argv[])
{
  extern pha_to_pi ();   /* EXTERNAL WORK FUNCTION IS PASSED TO THE ITERATOR */
  fitsfile *fptr;
  iteratorCol cols[100]; /* STRUCTURE USED BY THE ITERATOR FUNCTION */
  int ncols;
  long rows_per_loop, offset;
  char gainfile[200], tgainfile[200], parvalue[200];
  char commandline[1024];
  
  int status;
  int iarg;
  int lastarg;
  int i;
  
  time_t tloc;
  
  if (argc==1) {
    usage(argv[0]);
    exit (1);
  }
  
#ifndef NOREADPAR
  Ini_ReadPar(argc,argv);
  
  lastarg = argc;

  get_command_line_par ("gainfile",gainfile);
  if (!defined(gainfile)) {
    get_command_line_par ("gain",gainfile);
    if (defined(gainfile)) {
      iarg = last_command_line_par_position ();
      if (iarg<lastarg) lastarg = iarg;
    }
  } else {
    iarg = last_command_line_par_position ();
    if (iarg<lastarg) lastarg = iarg;
  }

  get_command_line_par ("tgain",tgainfile);
  if (defined(tgainfile)) {
    iarg = last_command_line_par_position ();
    if (iarg<lastarg) lastarg = iarg;
  }

  get_command_line_par ("changepha",parvalue);
  if (defined(parvalue)) {
    iarg = last_command_line_par_position ();
    if (iarg<lastarg) lastarg = iarg;
    if (!nopar(parvalue)) {
      MOD_PHA=1;
    }
  }

  if (!defined(gainfile)) strcpy(gainfile,"N O N E");
  if (!defined(tgainfile)) strcpy(tgainfile,"N O N E");

#else

  lastarg = argc;

  strcpy (gainfile,"N O N E");
  for (iarg=1;iarg<argc-1;iarg++) {
    if (strcmp(argv[iarg],"-gain")==0) {
      if (iarg<lastarg) lastarg = iarg;
      strcpy(gainfile,argv[iarg+1]);
      break;
    }
  }

  strcpy (tgainfile,"N O N E");
  for (iarg=1;iarg<argc-1;iarg++) {
    if (strcmp(argv[iarg],"-tgain")==0) {
      if (iarg<lastarg) lastarg = iarg;
      strcpy(tgainfile,argv[iarg+1]);
      break;
    }
  }

  for (iarg=1;iarg<argc-1;iarg++) {
    if (strcmp(argv[iarg],"-changepha")==0) {
      if (iarg<lastarg) lastarg = iarg;
      if (iarg+1<argc&&!strcmp(argv[iarg+1],"no")) {
	MOD_PHA=0;
      } else {
	MOD_PHA=1;
      }
      break;
    }
  }

#endif
  
  if (strcmp(gainfile,"N O N E")==0)
    DO_GAIN = 0;
  else
    DO_GAIN = 1;

  if (strcmp(tgainfile,"N O N E")==0)
    DO_TGAIN = 0;
  else
    DO_TGAIN = 1;

  
  if (!DO_GAIN && !DO_TGAIN) {
    usage(argv[0]);
    exit(1);
  }

 if (!DO_GAIN && (DO_TGAIN && !MOD_PHA)) {
    usage(argv[0]);
    exit(1);
  }


  if (DO_GAIN) {
    printf("Reading gain file: %s\n",gainfile);
    load_gain (gainfile);
  }

  if (DO_TGAIN) {
    printf("Reading tgain file: %s\n",tgainfile);
    load_tgain (tgainfile);
  }
  
  for (iarg=1;iarg<lastarg;iarg++) { /* Loop over file names */

    status = 0; 
    
    printf ("Correcting file: %s\n",argv[iarg]);
    fits_open_file(&fptr, argv[iarg], READWRITE, &status); /* open file */
    
    /* move to the EVENTS binary table extension */
    if (fits_movnam_hdu(fptr, BINARY_TBL, "EVENTS", 0, &status) )
      fits_report_error(stderr, status);    /* print out error messages */
    
    if ( ! DO_GAIN ) {
      ncols = 4;
    } else {
      ncols  = 6;   /* number of columns
		       We need: "ccd_id", "chipx", "chipy", "pha" and 
		       want to output "energy" and "PI"
		    */
    }
    
    /* define input column structure members for the iterator function */
    fits_iter_set_by_name(&cols[0], fptr, "ccd_id", TSHORT,  InputCol);
    fits_iter_set_by_name(&cols[1], fptr, "chipx",  TSHORT,  InputCol);
    fits_iter_set_by_name(&cols[2], fptr, "chipy",  TSHORT,  InputCol);
    if (MOD_PHA) {
      fits_iter_set_by_name(&cols[3], fptr, "pha",    TLONG,   InputOutputCol);
    } else {
      fits_iter_set_by_name(&cols[3], fptr, "pha",    TLONG,   InputCol);
    }
    if (DO_GAIN) {
      fits_iter_set_by_name(&cols[4], fptr, "energy", TFLOAT,  InputOutputCol);
      fits_iter_set_by_name(&cols[5], fptr, "pi",     TLONG,   InputOutputCol);
    }

    rows_per_loop = 0;  /* use default optimum number of rows */
    offset = 0;         /* process all the rows */

    /* apply the rate function to each row of the table */
    fits_iterate_data(ncols, cols, offset, rows_per_loop,
                      pha_to_pi, 0L, &status);

    /* update keywords */
    if (DO_GAIN) {
      fits_update_key_longstr(fptr,"GAINFILE",gainfile,"GAIN file",&status);
    }
    if (DO_TGAIN) {
      fits_update_key_longstr(fptr,"TGAIN",tgainfile,"Tgain file",&status);
    }
    fits_update_key_log (fptr,"TGAINPHA",MOD_PHA,
			 "tgain modified PHA?",&status);

    /* Add history */
    time(&tloc);
    strcpy(commandline,ctime(&tloc));
    i=strlen(commandline);
    commandline[i-1]='\0';
    fits_write_history (fptr,commandline,&status);
    commandline[0]='\0';
    for (i=0;i<argc;i++) {
      if (i>0) strcat (commandline," ");
      strcat (commandline,argv[i]);
    }
    fits_write_history (fptr,commandline,&status);

    /* update checksums */
    fits_write_chksum (fptr,&status);
      

    fits_close_file(fptr, &status);      /* all done */

    if (status)
      fits_report_error(stderr, status);  /* print out error messages */
  }
  printf ("OK\n");
  exit(0);
}
/*--------------------------------------------------------------------------*/
int pha_to_pi (long totalrows, long offset, long firstrow, long nrows,
             int ncols, iteratorCol *cols, void *user_strct ) 
{
  int ii, status = 0;
  
  /* Declare variables static to preserve their values between calls */
  /* 1) Data arrays: */ 
  static short *ccd_id, *chipx, *chipy;
  static long *pha, *pi;
  static float *energy;
  
  int iccd,chx,chy,ibin,i,iw;
  float xpha;
  
  int jj;
  

  /*--------------------------------------------------------*/
  /*  Initialization procedures: execute on the first call  */
  /*--------------------------------------------------------*/
  if (firstrow == 1)
    {
      if (DO_GAIN && ncols != 6) return(-1);  /* number of columns incorrect */
      if (! DO_GAIN && ncols != 4) return(-1);/* number of columns incorrect */
    
      if (DO_GAIN) {
	if (fits_iter_get_datatype(&cols[0]) != TSHORT ||
	    fits_iter_get_datatype(&cols[1]) != TSHORT ||
	    fits_iter_get_datatype(&cols[2]) != TSHORT ||
	    fits_iter_get_datatype(&cols[3]) != TLONG  ||
	    fits_iter_get_datatype(&cols[4]) != TFLOAT ||
	    fits_iter_get_datatype(&cols[5]) != TLONG )
	  return(-2);  /* bad data type */
      } else {
	if (fits_iter_get_datatype(&cols[0]) != TSHORT ||
	    fits_iter_get_datatype(&cols[1]) != TSHORT ||
	    fits_iter_get_datatype(&cols[2]) != TSHORT ||
	    fits_iter_get_datatype(&cols[3]) != TLONG )
	  return(-2);  /* bad data type */
      }
      
      /* assign the input pointers to the appropriate arrays and null ptrs*/
      ccd_id       = (short *) fits_iter_get_array(&cols[0]);
      chipx        = (short *) fits_iter_get_array(&cols[1]);
      chipy        = (short *) fits_iter_get_array(&cols[2]);
      pha          = (long  *) fits_iter_get_array(&cols[3]);
      if ( DO_GAIN ) {
	energy       = (float *) fits_iter_get_array(&cols[4]);
	pi           = (long  *) fits_iter_get_array(&cols[5]);
      }

    }
  
  /*--------------------------------------------*/
  /*  Main loop: process all the rows of data */
  /*--------------------------------------------*/

  /*  NOTE: 1st element of array is the null pixel value!  */
  /*  Loop from 1 to nrows, not 0 to nrows - 1.  */

  for (ii = 1; ii <= nrows; ii++)
    {
      iccd=ccd_id[ii];
      chx = chipx[ii];
      chy = chipy[ii];

      xpha=pha[ii];

      if (xpha<0.0) {
	fprintf (stderr,"Warning: pha<0: ccd_id=%d chipx=%d chipy=%d pha=%d\n",iccd,chx,chy,pha[ii]);
      } else {

	if (DO_TGAIN) {
	  ibin = TGAINBIN[chx-1][chy-1][iccd];
	  if (ibin>=0) {
	    /*	  fprintf (stderr,"no tgain data for %d,%d in ccd=%d\n",chx,chy,iccd);
		  exit(1);
		  }
	    */
	    iw = pha[ii]/10; if(iw>410) iw=410; if (iw<0) iw=0;
	    xpha = xpha - PHASHIFT[ibin][iw];
	  }
	}
	
	xpha = xpha + drand48() - 0.5;
	

	if (DO_TGAIN && MOD_PHA) {
	  pha[ii]=xpha; if (pha[ii]<0) pha[ii]=0;
	}
	
	if (DO_GAIN) {
	  /* locate coeff */
	  ibin = GAINBIN[chx-1][chy-1][iccd];
	  if (ibin<0) {
	    fprintf (stderr,"no gain data for %d,%d in ccd=%d\n",chx,chy,iccd);
	    exit(1);
	  }
	  energy[ii]=lin_interpolate(xpha,PHA[ibin],ENERGY[ibin],NGAINPOINTS[ibin]);
	  pi[ii]=((int)(energy[ii]/14.6))+1;
	  if (pi[ii]>1024) pi[ii]=1024;
	  if (pi[ii]<0)    pi[ii]=0;
	  
	}
      }
    }
  
  return(0);  /* return successful status */
}


/************ LOAD_GAIN *********************************/
int load_gain (char *gainfile)
{
  fitsfile *fptr;

  short NS=0; float NE=0;
  int ii,i,j,iccd,igainbin;
  short ccdid, chxmax,chxmin,chymax,chymin,npoints;
  int anynul,status;
  int energycol,energytype,phacol,phatype;
  long phanelem, energynelem,phawidth,energywidth;
  int ccdidcol, chipxmincol, chipxmaxcol, chipymincol, chipymaxcol, npointscol;
  long nrows;

  status = 0;
  fits_open_file(&fptr, gainfile, READONLY, &status);
  fits_movnam_hdu (fptr,BINARY_TBL,"AXAF_DETGAIN",0,&status);
  
  fits_get_num_rows (fptr,&nrows,&status);
  fits_get_colnum(fptr,CASEINSEN,"CCD_ID",&ccdidcol,&status);
  fits_get_colnum(fptr,CASEINSEN,"CHIPX_MIN",&chipxmincol,&status);
  fits_get_colnum(fptr,CASEINSEN,"CHIPX_MAX",&chipxmaxcol,&status);
  fits_get_colnum(fptr,CASEINSEN,"CHIPY_MIN",&chipymincol,&status);
  fits_get_colnum(fptr,CASEINSEN,"CHIPY_MAX",&chipymaxcol,&status);
  fits_get_colnum(fptr,CASEINSEN,"NPOINTS",&npointscol,&status);
  fits_get_colnum(fptr,CASEINSEN,"PHA",&phacol,&status);
  fits_get_colnum(fptr,CASEINSEN,"ENERGY",&energycol,&status);
  fits_get_coltype(fptr,phacol,&phatype,&phanelem,&phawidth,&status);
  fits_get_coltype(fptr,energycol,&energytype,&energynelem,&energywidth,&status);
  
  for (i=0;i<1024;i++) {
    for (j=0;j<1024;j++) {
      for (iccd=0;iccd<10;iccd++) {
	GAINBIN[i][j][iccd]=-1;
      }
    }
  }

  igainbin = 0;
  for (ii=1;ii<=nrows;ii++) {
    fits_read_col (fptr,TSHORT,ccdidcol,ii,1,1,&NS,&ccdid,&anynul,&status);
    fits_read_col (fptr,TSHORT,chipxmincol,ii,1,1,&NS,&chxmin,&anynul,&status);
    fits_read_col (fptr,TSHORT,chipxmaxcol,ii,1,1,&NS,&chxmax,&anynul,&status);
    fits_read_col (fptr,TSHORT,chipymincol,ii,1,1,&NS,&chymin,&anynul,&status);
    fits_read_col (fptr,TSHORT,chipymaxcol,ii,1,1,&NS,&chymax,&anynul,&status);
    fits_read_col (fptr,TSHORT,npointscol,ii,1,1,&NS,&npoints,&anynul,&status);
    if (status) {
      fits_report_error(stderr, status);  /* print out error messages */ 
      exit(1);
    }
    iccd = ccdid;
    for (i=chxmin-1;i<=chxmax-1;i++) {
      for (j=chymin-1;j<=chymax-1;j++) {
	GAINBIN[i][j][iccd]=igainbin;
      }
    }
    NGAINPOINTS[igainbin]=npoints;
    if (npoints>NGAINMAX) {
      fprintf (stderr,"increase max number of gain points: %d %d\n",
	       npoints,NGAINMAX);
      exit(1);
    }

    fits_read_col 
      (fptr,TFLOAT,
       phacol,ii,1,phanelem,&NE,PHA[igainbin],&anynul,&status);

    fits_read_col 
      (fptr,TFLOAT,
       energycol,ii,1,energynelem,&NE,ENERGY[igainbin],&anynul,&status);

    igainbin++;
    
    if (igainbin>NMAX) {
      fprintf (stderr,"increase max number of gain regions: %d %d\n",
	       igainbin,NMAX);
      exit(1);
    }
      
  }
  fits_close_file(fptr, &status);      /* all done */
  if (status) {
    fits_report_error(stderr, status);  /* print out error messages */ 
    exit(1);
  }
  
  return 0;
}

int load_tgain (char *gainfile)
{
  
  fitsfile *fptr;

  long nrows;
  int typecode; long repeat, width;
  int ccdidcol,chipxmincol,chipxmaxcol,chipymincol,chipymaxcol, deltaphacol;
  int cxmin, cxmax, cymin, cymax,ccdid;
  int inull=0; float enull=0.0;
  int ngain, i, ix,iy;
  int status, anynul;

  status = 0;

  fits_open_file(&fptr, gainfile, READONLY, &status); /* open file */
  /* move to the EVENTS binary table extension */
  if (fits_movnam_hdu(fptr, BINARY_TBL, "ACIS TGAIN", 0, &status) )
    fits_report_error(stderr, status);    /* print out error messages */
  /* define input column structure members for the iterator function */

  fits_get_num_rows (fptr, &nrows, &status);
  fits_get_colnum (fptr, 0, "ccd_id", &ccdidcol, &status);
  fits_get_colnum (fptr, 0, "chipx_min", &chipxmincol, &status);
  fits_get_colnum (fptr, 0, "chipx_max", &chipxmaxcol, &status);
  fits_get_colnum (fptr, 0, "chipy_min", &chipymincol, &status);
  fits_get_colnum (fptr, 0, "chipy_max", &chipymaxcol, &status);
  fits_get_colnum (fptr, 0, "deltapha",  &deltaphacol,  &status);
  
  /* check type for deltapha */
  fits_get_coltype (fptr, deltaphacol, &typecode, &repeat, &width, &status);
  if (status)
    fits_report_error(stderr, status);  /* print out error messages */
  if (repeat != 411 ) {
    fprintf (stderr,"Error: expect DELTAPHA type 411E in %s\n",gainfile);
    exit(1);
  }

  for (ix=0;ix<1024;ix++) {
    for (iy=0;iy<1024;iy++) {
      for (ccdid=0;ccdid<10;ccdid++) {
	TGAINBIN[ix][iy][ccdid]=-1;
      }
    }
  }

  ngain=0;
  for (i=1;i<=nrows;i++) {
    fits_read_col(fptr,TLONG,ccdidcol,i,1,1,&inull,&ccdid,&anynul,&status);
    fits_read_col(fptr,TLONG,chipxmincol,i,1,1,&inull,&cxmin,&anynul,&status);
    fits_read_col(fptr,TLONG,chipxmaxcol,i,1,1,&inull,&cxmax,&anynul,&status);
    fits_read_col(fptr,TLONG,chipymincol,i,1,1,&inull,&cymin,&anynul,&status);
    fits_read_col(fptr,TLONG,chipymaxcol,i,1,1,&inull,&cymax,&anynul,&status);

    for (ix=cxmin-1;ix<=cxmax-1;ix++) 
      {
	for (iy=cymin-1;iy<=cymax-1;iy++)
	  {
	    TGAINBIN[ix][iy][ccdid]=ngain;
	  }
      }
    
    fits_read_col(fptr,TFLOAT,deltaphacol,i,1,411,&enull,PHASHIFT[ngain],
		  &anynul,&status);
    ngain++;
  }
  
  fits_close_file(fptr, &status);      /* all done */
  if (status)
    fits_report_error(stderr, status);  /* print out error messages */
  
}

/******** LIN_INTERPOLATE ***************************************/

float lin_interpolate (float x, float *x0, float *y0, int n)
{
  int ibin;
  int igainbin;
  float e;

  if (x<=x0[0]) {
    ibin=0;
  }
  else if (x>=x0[n-1]) {
    ibin=n-2;
  } else {
    locate (x0,n,x,&ibin);
/*     if (x<x0[ibin]||x>=x0[ibin+1]) { */
/*       fprintf (stderr,"problem in locate\n"); */
/*       exit(1); */
/*     } */
  }

  if (x0[ibin+1] != x0[ibin]) {
    e=y0[ibin]+(y0[ibin+1]-y0[ibin])*(x-x0[ibin])/(x0[ibin+1]-x0[ibin]);
  } else {
    e=y0[ibin];
  }
  if (e<0.0) {
    fprintf (stderr,"problem with interpolation in apply_gain\n");
    exit(1);
  }
return e;
}

void locate(float *xx, int n, float x, int *j)
{
        unsigned long ju,jm,jl;
        int ascnd;

        jl=-1;
        ju=n;
        ascnd=(xx[n-1] > xx[0]);
        while (ju-jl > 1) {
                jm=(ju+jl) >> 1;
                if (x > xx[jm] == ascnd)
                        jl=jm;
                else
                        ju=jm;
        }
        *j=jl;
}


/************ USAGE *****************************************************/
int usage (char *progname) {
  fprintf (stderr,"usage: %s file1.fits [file2.fits ...] [-gain gain.fits] [-tgain tgain.fits [-changepha]]\n",progname);
  return 0;
}
