/**
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_interp.h>
#include <gsl/gsl_multifit_nlin.h>
#include "aXe_grism.h"
#include "aXe_utils.h"
//#include "spce_PET.h"
#include "inout_aper.h"
//#include "trace_conf.h"
#include "aper_conf.h"
//#include "spc_sex.h"
//#include "disp_conf.h"
//#include "spc_wl_calib.h"
//#include "spce_binning.h"
#include "spc_spc.h"
//#include "spce_output.h"
//#include "spc_FITScards.h"
#include "fringe_conf.h"
#include "trfit_utils.h"
#include "ipixcorr_utils.h"

#define AXE_IMAGE_PATH "AXE_IMAGE_PATH"
#define AXE_OUTPUT_PATH "AXE_OUTPUT_PATH"
#define AXE_CONFIG_PATH "AXE_CONFIG_PATH"

int
main (int argc, char *argv[])
{
  char *opt;

  char ipc_aper_file[MAXCHAR];
  char ipc_aper_file_path[MAXCHAR];

  char aper_file[MAXCHAR];
  char aper_file_path[MAXCHAR];

  char conf_file[MAXCHAR];
  char conf_file_path[MAXCHAR];

  char grism_file[MAXCHAR];
  char grism_file_path[MAXCHAR];

  char bck_image[MAXCHAR];
  char bck_image_path[MAXCHAR];

  char IPC_file[MAXCHAR];
  char IPC_file_path[MAXCHAR];

  char ipc_file[MAXCHAR];
  char ipc_file_path[MAXCHAR];

  char SPC_file[MAXCHAR];
  char SPC_file_path[MAXCHAR];

  char IPC_func[MAXCHAR];
  char IPC_func_path[MAXCHAR];

  aperture_conf *conf;

  object **oblist;
  observation *obs=NULL, *bck;

  //Initialize obs struct memebers
  obs->grism = NULL;
  obs->pixerrs = NULL;
  obs->dq = NULL;

  interpolator *ipcorr;

  interpolator *nlincorr;

  int index;
  //int i;

  full_spectr *SPC;

  fitsfile  *SPC_ptr;
  fitsfile  *IPC_ptr;
  FITScards *cards;

  int f_status=0;

  int aperID=0, beamID=0;
  //objindex;

  int spec_OAF=0;
  int point_like=0;

  beam act_beam;
  beam *beam_ptr;

  double max_ext=0.0;
  //double adcgain;
  double exptime=NAN;

  gsl_matrix *data_matrix;

  int ipixc=0;
  int nlinc=0;
  int icorr=0;
  int num=0;

  if ((argc < 3) || (opt = get_online_option ("help", argc, argv)))
    {
      fprintf (stdout,
         "aXe_FRINGECORR Version %s:\n",RELEASE);
      exit (1);
    }

  fprintf (stdout, "aXe_INTPIXCORR: Starting...\n");

  // reset the parameter index
  index = 0;

  // read in the grism image name
  strcpy(grism_file, argv[++index]);
  build_path (AXE_IMAGE_PATH, grism_file, grism_file_path);

  // read in the configuration file name
  strcpy(conf_file, argv[++index]);
  build_path (AXE_CONFIG_PATH, conf_file, conf_file_path);

  // load the configuration file
  conf = get_aperture_descriptor(conf_file_path);

  // determine the science extension number
  get_extension_numbers(grism_file_path, conf,conf->optkey1,conf->optval1);

  // get or set up the input OAF name
  if ((opt = get_online_option("in_OAF", argc, argv)))
    {
      // copy the parameter value if given
      strcpy(aper_file, opt);

      // mark that there is a special OAF file
      spec_OAF=1;
    }
  else
    {
      // copy the grism name and construct the name
      // if not given as parameter
      strcpy(aper_file, grism_file);
      replace_file_extension (grism_file, aper_file, ".fits",
			      ".OAF", conf->science_numext);
    }
  build_path(AXE_OUTPUT_PATH, aper_file, aper_file_path);

  // build the name of the modified
  // OAF file
  replace_file_extension (grism_file, ipc_aper_file, ".fits",
			      "_ipc.OAF", conf->science_numext);
  build_path(AXE_OUTPUT_PATH, ipc_aper_file, ipc_aper_file_path);

  // get or set up the output SPC name
  if ((opt = get_online_option("out_SPC", argc, argv)))
    {
      // copy the parameter value if given
      strcpy(IPC_file, opt);
    }
  else
    {
      // copy the grism name and construct the name
      // if not given as parameter
      strcpy(IPC_file, grism_file);
      replace_file_extension(grism_file, IPC_file, ".fits",
			     "_ipc.SPC.fits", conf->science_numext);
    }
  build_path(AXE_OUTPUT_PATH, IPC_file, IPC_file_path);

  // get or set up the output SPC name
  if ((opt = get_online_option("in_SPC", argc, argv)))
    {
      // copy the parameter value if given
      strcpy(SPC_file, opt);
    }
  else
    {
      // copy the grism name and construct the name
      // if not given as parameter
      strcpy(SPC_file, grism_file);
      replace_file_extension(grism_file, SPC_file, ".fits",
			     ".SPC.fits", conf->science_numext);
    }
  build_path(AXE_OUTPUT_PATH, SPC_file, SPC_file_path);

  // form the background image name
  replace_file_extension(grism_file, bck_image, ".fits",
			 ".BCK.fits", conf->science_numext);
  build_path (AXE_OUTPUT_PATH, bck_image, bck_image_path);

  // check whether an intra pixel sensitivity correction
  // shall be applied
  if ((opt = get_online_option("ipixcorr", argc, argv)))
    ipixc = 1;
  // check whether an intra pixel sensitivity correction
  // shall be applied
  if ((opt = get_online_option("nlincorr", argc, argv)))
    nlinc = 1;

  // check whether something is
  // done at all
  if (ipixc || nlinc)
    {
      // Build the intrapixel correction file name
      sprintf(IPC_func,"%s",conf->IPIXfunc);

      // overwrite it with the parameter if given
      if ((opt = get_online_option ("IPIXFUNCTION", argc, argv)))
	strcpy(IPC_func,opt);

      if (!strcmp(IPC_file,"None"))
	// check whether the correction function is given;
	// complain if not given!
	aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		     "aXe_INTPIXCORR: Correction function must be given!!\n");
      else
	// compose the full pathname
	build_path (AXE_CONFIG_PATH, IPC_func, IPC_func_path);

      // get the maximum extension number
      if ((opt = get_online_option ("max_ext", argc, argv)))
	max_ext = atof(opt);

      // check whether either max_ext is defined
      // OR a special OAF file is given
      if (!max_ext && !spec_OAF)
	aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		     "aXe_INTPIXCORR: Either maximum extension\n"
		     "OR a special OAF file must be given!\n");
    }

  // print on screen what's done
  fprintf (stdout, "aXe_INTPIXCORR: Grism file name:                     %s\n",
	   grism_file_path);
  fprintf (stdout, "aXe_INTPIXCORR: Configuration file name:             %s\n",
	   conf_file_path);
  fprintf (stdout, "aXe_INTPIXCORR: Correction function:                 %s\n",
	   IPC_func_path);
  fprintf (stdout, "aXe_INTPIXCORR: Input OAF file name:                 %s\n",
	   aper_file_path);
  fprintf (stdout, "aXe_INTPIXCORR: Input SPC file name:                 %s\n",
	   SPC_file_path);
  fprintf (stdout, "aXe_INTPIXCORR: Output SPC file name:                %s\n",
	   IPC_file_path);
  fprintf (stdout, "aXe_INTPIXCORR: Maximal extension for point objects: %f\n",
	   max_ext);
  if (ipixc)
    fprintf (stdout, "aXe_INTPIXCORR: Intra-pixel sensitivity correction is aplied!\n");
  if (nlinc)
    fprintf (stdout, "aXe_INTPIXCORR: Non-linearity correction is aplied!\n");

  // check whether something is
  // done at all
  if (ipixc || nlinc)
    {
      // check whether the intra pixel
      // sensitivity is applied
      if (ipixc)
	{
	  // determine the exposure time
	  if (isnan(exptime))
	    exptime = (double)get_float_from_keyword(grism_file_path, 1,
						     conf->exptimekey);
	  if (isnan(exptime))
	    exptime = 1.0;

	  // load the image data
	  obs = load_image_t(grism_file_path, conf->science_numext,
			     conf->errors_numext, conf->dq_numext,
			     conf->dqmask, exptime, conf->rdnoise);

	  // load the background data
	  bck = load_image_t(bck_image_path, conf->science_numext,
			     conf->errors_numext, conf->dq_numext,
			     conf->dqmask, exptime, conf->rdnoise);

	  // subtract the background from
	  // the image data
	  data_matrix = bcksub_observation(obs, bck);
	}

      // load the aperture list
      fprintf (stdout, "aXe_INTPIXCORR: Loading object aperture list...");fflush(stdout);
      oblist = file_to_object_list_seq (aper_file_path, obs);
      fprintf (stdout,"%d objects loaded.\n",object_list_size(oblist));fflush(stdout);

      // open the input SPC file
      SPC_ptr = get_SPC_opened(SPC_file_path, 0);

      // open the output SPC file
      IPC_ptr = create_SPC_opened (IPC_file_path,1);
      cards = get_FITS_cards_opened(SPC_ptr);
      put_FITS_cards_opened(IPC_ptr, cards);
      free_FITScards(cards);

      if (ipixc)
	// create an interpolator reading in the fits file
	ipcorr = create_interp_ftable(IPC_func_path,2,"X","FX",IPCORR_INTERP_TYPE);
      else
	ipcorr = NULL;

      if (nlinc)
	// for the NICMOS nonlinearity correction
	// create an interpolator for the non-linear correction
	//nlincorr = create_nlincor();
	nlincorr = create_interp_ftable(IPC_func_path,2,"LAMBDA","BPAR",NLINCORR_INTERP_TYPE);
      else
	nlincorr = NULL;

      while ((aperID!=-1) || (beamID!=-1))
	{
	  // load the next extension from the SPC table
	  SPC = get_ALL_from_next_in_SPC(SPC_ptr, &aperID, &beamID);

	  // break the loop if the end was reached
	  if ((aperID==-1) && (beamID==-1))
	    break;

	  // if the full spectrum is NULL,
	  // go to the next beam
	  if (!SPC)
	    continue;

	  // select the actual beam from the beam list
	  act_beam = find_beam_in_object_list(oblist, aperID, beamID);
	  beam_ptr = find_beamptr_in_object_list(oblist, aperID, beamID);

	  // determine whether the object is pointlike
	  point_like = is_pointlike(act_beam, spec_OAF, max_ext);

	  // if the obejct is indeed
	  // a pointlike one
	  if (point_like)
	    {
	      // check whether the intra-pixel sensititvity correction
	      // shall be applied
	      if (ipixc)
		{
		  // form the name of the file which contain
		  // the data
		  replace_file_extension(grism_file, ipc_file, ".fits",
					 ".IPC.dat", aperID);
		  build_path(AXE_OUTPUT_PATH, ipc_file, ipc_file_path);

		  icorr = fitting_ipc_corr(act_beam, conf_file_path, ipcorr,
					   SPC, obs, data_matrix, ipc_file_path, beam_ptr);

		  if (icorr)
		    fprintf(stdout, "aXe_INTPIXCORR: Corrected BEAM %d%c\n", SPC->aperID, BEAM(SPC->beamID));
		  else
		    fprintf(stdout, "aXe_INTPIXCORR: phase accuracy in BEAM %d%c too low\n", SPC->aperID, BEAM(SPC->beamID));

		}

	      // check whether the non-linearity correction
	      // shall be applied
	      if (nlinc)
		{
		  /*
		  if (!strcmp(conf->gainkey,"None"))
		    adcgain=1.0;
		  else
		    adcgain = (double)get_float_from_keyword(grism_file_path, 1, conf->gainkey);
		  */
		  // apply the NICMOS non-linearity correction
		  fprintf(stdout, "aXe_INTPIXCORR: Non-linearity correting BEAM %d%c...\n", SPC->aperID, BEAM(SPC->beamID));
		  // the gain mostly used in NICMOS
		  // is hardcoded here!!!!!
		  nlin_corr_beam(nlincorr, 6.5, SPC);
		}
	    }
	  else
	    {
	      fprintf(stdout, "aXe_INTPIXCORR: Skipping BEAM %d%c...\n", SPC->aperID, BEAM(SPC->beamID));
	    }

	  add_spectra_to_SPC_opened (IPC_ptr, SPC->fgr_spec, SPC->bck_spec,
				     SPC->obj_spec, SPC->aperID, SPC->beamID);

	  /* Copy header from OPET extension into this SPC extension */
	  cards = get_FITS_cards_opened(SPC_ptr);
	  put_FITS_cards_opened(IPC_ptr,cards);
	  free_FITScards(cards);

	  free_full_spectr(SPC);
      	}

      refurbish_object_list(oblist, 1, -100, 0);

      fprintf (stdout, "aXe_INTPIXCORR: Writing aperture file %s ... ", ipc_aper_file);fflush(stdout);
      num = object_list_to_file (oblist, ipc_aper_file_path, 0);
      fprintf (stdout, "%d beams written.\n", num);

      // free the object list
      if (oblist!=NULL)
	free_oblist (oblist);

      if (ipcorr)
	// free the interpolator
	free_interp(ipcorr);

      if (nlincorr)
	// free the interpolator
	free_interp(nlincorr);

      fits_close_file (SPC_ptr, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_INTPIXCORR: " "Error closing SPC: %s\n",
		       SPC_file_path);
	}

      fits_close_file (IPC_ptr, &f_status);
      if (f_status)
	{
	  ffrprt (stderr, f_status);
	  aXe_message (aXe_M_FATAL, __FILE__, __LINE__,
		       "aXe_INTPIXCORR: " "Error closing SPC: %s\n",
		       IPC_file_path);
	}
    }
  // say goodbye
  fprintf (stdout, "aXe_INTPIXCORR: Done...\n");
  exit (0);
}


#ifndef Photometry_h
#define Photometry_h
/** This is free and unencumbered software released into the public domain.
The authors of ISIS do not claim copyright on the contents of this file.
For more details about the LICENSE terms and the AUTHORS, you will
find files of those names at the top level of this repository. **/

/* SPDX-License-Identifier: CC0-1.0 */

#include <string>
#include <vector>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_min.h>
#include <gsl/gsl_roots.h>

namespace Isis {
  class Pvl;
  class PhotoModel;
  class AtmosModel;
  class NormModel;
  /**
   * @author ????-??-?? Unknown
   *
   * @internal
   *  @history 2007-08-02 Steven Lambright - Fixed memory leak
   *  @history 2008-03-07 Janet Barrett - Added SetPhotomWl method to allow
   *                      the application to set the p_normWavelength variable for
   *                      use by MoonAlbedo normalization.
   *  @history 2008-06-18 Steven Koechle - Fixed Documentation Errors
   *  @history 2008-07-09 Steven Lambright - Fixed unit test
   *  @history 2011-08-19 Sharmila Prasad - Implemented brentminimizer using GSL
   *  @history 2011-09-15 Sharmila Prasad - Implemented brent's root solver using GSL
   */
  class Photometry {
    public:
      Photometry(Pvl &pvl);
      Photometry() {};
      virtual ~Photometry();

      //! Calculate the surface brightness
      void Compute(double pha, double inc, double ema, double dn,
                   double &albedo, double &mult, double &base);
      void Compute(double pha, double inc, double ema, double deminc,
                   double demema, double dn, double &albedo,
                   double &mult, double &base);

      //! Set the wavelength
      virtual void SetPhotomWl(double wl);

      //! Double precision version of bracketing algorithm ported from Python.
      //! Solution bracketing for 1-D minimization routine.
      static void minbracket(double &xa, double &xb, double &xc, double &fa,
          double &fb, double &fc, double Func(double par, void *params),
          void *params);

      //! Brent's method 1-D minimization routine using GSL's r8Brent minimization Algorithm
      static int brentminimizer(double x_lower, double x_upper, gsl_function *Func,
          double & x_minimum, double tolerance);

      //! GSL's the Brent-Dekker method (Brent's method) combines an interpolation strategy
      //! with the bisection algorithm to estimate the root of the quadratic function.
      static int brentsolver(double x_lo, double x_hi, gsl_function *Func, double tolerance, double &root);

      PhotoModel *GetPhotoModel() const {
        return p_phtPmodel;
      }

      AtmosModel *GetAtmosModel() const {
        return p_phtAmodel;
      }

      NormModel *GetNormModel() const {
        return p_phtNmodel;
      }

    protected:
      AtmosModel *p_phtAmodel;
      PhotoModel *p_phtPmodel;
      NormModel *p_phtNmodel;
  };
};

#endif


/*--------------------------------------------------------------------
 * $Id$
 *
 * This file is part of libRadtran.
 * Copyright (c) 1997-2012 by Arve Kylling, Bernhard Mayer,
 *                            Claudia Emde, Robert Buras
 *
 * ######### Contact info: http://www.libradtran.org #########
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *--------------------------------------------------------------------*/

#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include "errors.h"
#include "uvspecrandom.h"

#if HAVE_LIBGSL
#include <gsl/gsl_rng.h>
#include <gsl/gsl_sf.h>
#include <gsl/gsl_randist.h>
#endif

/* global variable: random number generator */
#if HAVE_LIBGSL
gsl_rng* uvspecrng;
#endif

/*****************************************************************/
/* uvspec random number generator; either the highly-recommended */
/* MT19937 if GSL is available, or random() otherwise.           */
/*****************************************************************/

double uvspec_random() {
#if HAVE_LIBGSL
  return gsl_rng_uniform (uvspecrng);
#else
  return (double)random();
#endif
}

double uvspec_random_gauss (double sigma) {
#if HAVE_LIBGSL
  return gsl_ran_gaussian (uvspecrng, sigma);
#else
  return (double)random();
#endif
}

int init_uvspec_random (int* randomseed, int need_mt19937, int quiet) {
  int rseed = 0;

/* global variable, defined in uvspec_lex.l */
#if HAVE_LIBGSL
  /*  extern gsl_rng *uvspecrng; */ /* global random number generator */
  const gsl_rng_type* T;
#endif

  /* always initialize standard rng */
  srandom (rseed);

  if (*randomseed > 0)
    rseed = *randomseed;
  else
    rseed = (int)time (NULL) + (int)getpid();

#if HAVE_LIBGSL
  T                    = gsl_rng_mt19937;
  gsl_rng_default_seed = rseed;
  uvspecrng            = gsl_rng_alloc (T);
  if (!quiet && need_mt19937)
    fprintf (stderr, " ... using %s random number generator\n", gsl_rng_name (uvspecrng));
#else
  if (need_mt19937) {
    fprintf (stderr, "Error, GSL not found. The MT19937 random number generator is\n");
    fprintf (stderr, "required for MYSTIC simulations. Please install GSL and recompile!\n");
    return -1;
  }
#endif

  return rseed;
}

/***********************************************************************************/
/* Function: read_and_write_random_status                                 @62_30i@ */
/* Description:                                                                    */
/*  check whether to read/write random status.                                     */
/* Parameters:                                                                     */
/* Return value:                                                                   */
/* Example:                                                                        */
/* Files:                                                                          */
/* Known bugs:                                                                     */
/* Author: RPB                                                                     */
/*                                                                        @i62_30@ */
/***********************************************************************************/

#if HAVE_LIBGSL
int read_and_write_random_status (char* randomstatusfilename,
                                  int*  ili,
                                  int*  backward_is,
                                  int*  backward_js,
                                  int   photoncounter,
                                  int*  readrandomstatus) {
  FILE* randomstatusfile = NULL;
  int   status           = 0;

  /************************************+**************************/
  /* starting with the randomseed is nice, but starting with the */
  /* actual photon that produced the problem is even nicer...    */
  /* here we read and write the status of the random number      */
  /* generator                                                   */
  /* this only works with the gsl !!!                            */
  /************************************+**************************/

  /* read randomstatusfile if asked for */
  if (*readrandomstatus && photoncounter == 1) {
    if ((randomstatusfile = fopen (randomstatusfilename, "r")) != NULL) {
      status = gsl_rng_fread (randomstatusfile, uvspecrng);
      if (status)
        return err_out ("Error %d returned by gsl_rng_fread()\n", status);
      if (!fscanf (randomstatusfile, "%d %d %d", ili, backward_is, backward_js)) {
        fprintf (stderr, "Error reading file %s\n", randomstatusfilename);
        return -1;
      }
      (void)fclose (randomstatusfile);
      fprintf (stderr, "reading random status for photon...\n");
    } else
      return err_out ("Error %d, could not open randomstatusfile\n", -1);
  }

#ifdef WRITERANDOMSTATUS
  /* write status every 10 photons, starting with photon 1 */
  if (photoncounter % 10 == 1 && !(*readrandomstatus)) {
    if ((randomstatusfile = fopen (randomstatusfilename, "w")) != NULL) {
      status = gsl_rng_fwrite (randomstatusfile, uvspecrng);
      if (status)
        return err_out ("Error %d returned by gsl_rng_fwrite()\n", status);
      fprintf (randomstatusfile, "%d %d %d\n", *ili, *backward_is, *backward_js);
      (void)fclose (randomstatusfile);
    } else
      return err_out ("Error %d, could not open randomstatusfile\n", -1);
  }
#endif
  *readrandomstatus = 0;

  return 0;
}
#endif


#ifndef CWANNIER_HTIGHTBINDING_H
#define CWANNIER_HTIGHTBINDING_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_errno.h>
#include "bstrlib/bstrlib.h"

typedef struct {
    int num_rs, num_bands;
    double *ras;
    double *rbs;
    double *rcs;
    double *degens;
    gsl_matrix_complex **Hrs;
} HTightBinding;

HTightBinding* ExtractHTightBinding(char *filePath);

void FreeHTightBinding(HTightBinding *Hrs);

void HkRecip(HTightBinding *Hrs, double k[3], gsl_matrix_complex *Hk);

gsl_matrix_complex* HrAtR(HTightBinding *Hrs, double R[3], double *degen);

#endif // CWANNIER_HTIGHTBINDING_H