#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/glocale.h>

#include "landsat.h"

#define PI  M_PI
#define R2D 180./M_PI
#define D2R M_PI/180.

/****************************************************************************
 * PURPOSE: Calibrated Digital Number to at-satellite Radiance
 *****************************************************************************/
double lsat_qcal2rad(double qcal, band_data * band)
{
    return (double)(qcal * band->gain + band->bias);
}

/****************************************************************************
 * PURPOSE: Radiance of non-thermal band to at-satellite Reflectance
 *****************************************************************************/
double lsat_rad2ref(double rad, band_data * band)
{
    return (double)(rad / band->K1);
}

/****************************************************************************
 * PURPOSE: Radiance of thermal band to at-satellite Temperature
 *****************************************************************************/
double lsat_rad2temp(double rad, band_data * band)
{
    return (double)(band->K2 / log((band->K1 / rad) + 1.));
}

/****************************************************************************
 * PURPOSE: Some band constants
 *
 *      zenith = 90 - sun_elevation
 *      sin( sun_elevation ) = cos( sun_zenith )
 *
 *      lsat : satellite data
 *         i : band number
 *    method : level of atmospheric correction
 *   percent : percent of solar irradiance in path radiance
 *      dark : digital number of  object for DOS
  *****************************************************************************/

#define abs(x)	(((x)>0)?(x):(-x))

void lsat_bandctes(lsat_data * lsat, int i, char method,
		   double percent, int dark, double rayleigh)
{
    double pi_d2, sin_e, cos_v;

    /* TAUv  = at. transmittance surface-sensor */
    /* TAUz  = at. transmittance sun-surface    */
    /* Edown = diffuse sky spectral irradiance  */
    double TAUv, TAUz, Edown;

    pi_d2 = (double)(PI * lsat->dist_es * lsat->dist_es);
    sin_e = (double)(sin(D2R * lsat->sun_elev));
    cos_v = (double)(cos(D2R * (lsat->number < 4 ? 9.2 : 8.2)));

    /** Global irradiance on the sensor.
	 * Radiance to reflectance coefficient, only NO thermal bands.
	 * K1 and K2 variables are also utilized as thermal constants
     */
    if (lsat->band[i].thermal == 0) {
	switch (method) {
	case DOS2:
	    {
		TAUv = 1.;
		TAUz = (lsat->band[i].wavemax < 1.) ? sin_e : 1.;
		Edown = 0.;
		break;
	    }
	case DOS2b:
	    {
		TAUv = (lsat->band[i].wavemax < 1.) ? cos_v : 1.;
		TAUz = (lsat->band[i].wavemax < 1.) ? sin_e : 1.;
		Edown = 0.;
		break;
	    }
	case DOS3:
	    {
		double t;

		t = 2. / (lsat->band[i].wavemax + lsat->band[i].wavemin);
		t = 0.008569 * t * t * t * t * (1 + 0.0113 * t * t + 0.000013 * t * t * t * t);
		TAUv = exp(-t / cos_v);
		TAUz = exp(-t / sin_e);
		Edown = rayleigh;
		break;
	    }
	case DOS4:
	    {
		double Ro =
		    (lsat->band[i].lmax - lsat->band[i].lmin) * (dark -
								 lsat->
								 band[i].
								 qcalmin) /
		    (lsat->band[i].qcalmax - lsat->band[i].qcalmin) +
		    lsat->band[i].lmin;
		double Tv = 1.;
		double Tz = 1.;
		double Lp = 0.;

		do {
		    TAUz = Tz;
		    TAUv = Tv;
		    Lp = Ro - percent * TAUv * (lsat->band[i].esun * sin_e * TAUz + PI * Lp) / pi_d2;
		    Tz = 1. - (4. * pi_d2 * Lp) / (lsat->band[i].esun * sin_e);
		    if (Tz <= 0)
			G_fatal_error(_("The DOS4 method is not applicable here: approximation of atmospheric transmittance coefficients is unstable. Use another DOS method or use other sun_elevation parameter"));
		    Tv = exp(sin_e * log(Tz) / cos_v);
		} while (TAUv != Tv && TAUz != Tz);
		TAUz = (Tz < 1. ? Tz : 1.);
		TAUv = (Tv < 1. ? Tv : 1.);
		Edown = (Lp < 0. ? 0. : PI * Lp);
		break;
	    }
	default:		/* DOS1 and Without atmospheric-correction */
	    TAUv = 1.;
	    TAUz = 1.;
	    Edown = 0.;
	    break;
	}
	lsat->band[i].K2 = 0.;
	lsat->band[i].K1 = TAUv * (lsat->band[i].esun * sin_e * TAUz + Edown) / pi_d2;
	if (method > DOS)
	    G_verbose_message("... TAUv = %.5f, TAUz = %.5f, Edown = %.5f\n", TAUv, TAUz, Edown);
    }

    /** Digital number to radiance coefficients.
	 * Without atmospheric calibration for thermal bands.
     */
    lsat->band[i].gain = (lsat->band[i].lmax - lsat->band[i].lmin) / (lsat->band[i].qcalmax - lsat->band[i].qcalmin);

    if (method == UNCORRECTED || lsat->band[i].thermal) {
	/* L = G * (DN - Qmin) + Lmin
	 *  -> bias = Lmin - G * Qmin    
	 */
	lsat->band[i].bias = (lsat->band[i].lmin - lsat->band[i].gain * lsat->band[i].qcalmin);
    }
    else if (method > DOS) {
	/* L = Lsat - Lpath = G * DNsat + B - (G *  + B - p * rad_sun) 
	 *   = G * DNsat - G *  + p * rad_sun
	 *  -> bias = p * rad_sun - G 
	 */
	lsat->band[i].bias = percent * lsat->band[i].K1 - lsat->band[i].gain * dark;
    }
}
