#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>

#include "../../src/util/memory.h"
#include "../../src/util/timer.h"
#include "../../src/util/units.h"
#include "../../src/wmm/wmm.h"

static int err;

struct Sample {
	double date; // year
	double altitude; // km
	double latitude; // deg
	double longitude; // deg
	double X, Y, Z; // nT
	double Incl; // deg
	double Decl; // deg
};

static struct Sample samples[] = {
	
	// Test data from the file WMM2015testvalues.pdf:
	{2015.0, 0, 80, 0, 6627.1, -445.9, 54432.3, 83.04, -3.85},
	{2015.0, 0, 0, 120, 39518.2, 392.9, -11252.4, -15.89, 0.57},
	{2015.0, 0, -80, 240, 5797.3, 15761.1, -52919.1, -72.39, 69.81},
	{2015.0, 100, 80, 0, 6314.3, -471.6, 52269.8, 83.09, -4.27},
	{2015.0, 100, 0, 120, 37535.6, 364.4, -10773.4, -16.01, 0.56},
	{2015.0, 100, -80, 240, 5613.1, 14791.5, -50378.6, -72.57, 69.22},
	{2017.5, 0, 80, 0, 6599.4, -317.1, 54459.2, 83.08, -2.75},
	{2017.5, 0, 0, 120, 39571.4, 222.5, -11030.1, -15.57, 0.32},
	{2017.5, 0, -80, 240, 5873.8, 15781.4, -52687.9, -72.28, 69.58},
	{2017.5, 100, 80, 0, 6290.5, -348.5, 52292.7, 83.13, -3.17},
	{2017.5, 100, 0, 120, 37585.5, 209.5, -10564.2, -15.70, 0.32},
	{2017.5, 100, -80, 240, 5683.5, 14808.8, -50163.0, -72.45, 69.00},
	
	// My samples:
	{2017.5, 0.200, 44.5, 11.4, 23044.1, 1162.9, 41210.9, 60.75, 2.88}, // near Bologna
	{0, 0, 0, 0, 0, 0, 0, 0, 0} // date=0 marks the end of the data
};


/**
 * Checks if the got value is close enough to the expected value.
 * Error must be within 1%, which is quite arbitrary limit.
 */
static void compare(int sample_no, char *value_name, double exp, double got)
{
	int e = 100 * (got - exp)/exp;
	if( !(-1 <= e && e <= 1) ){
		printf("sample no. %d, %s: exp %g, got %g, error %d%%\n",
			sample_no, value_name, exp, got, e);
		err++;
	}
}


static void testSample(int i, struct Sample *s)
{
	wmm_MagneticField mf;
	wmm_getMagneticField(s->date,
		units_DEGtoRAD(s->latitude),
		units_DEGtoRAD(s->longitude),
		1000.0 * s->altitude,
		&mf);
	compare(i, "X (nT)", s->X, mf.X);
	compare(i, "Y (nT)", s->Y, mf.Y);
	compare(i, "Z (nT)", s->Z, mf.Z);
	compare(i, "Incl (DEG)", s->Incl, units_RADtoDEG(mf.Incl));
	compare(i, "Decl (DEG)", s->Decl, units_RADtoDEG(mf.Decl));
}


/**
 * Speed test evaluating the magnetic field along a very long journey.
 * @param samples How many computations along the journey.
 */
static void journey(int samples)
{
	double
		date = 2017.5,
		fromLat = units_DEGtoRAD(+80),  fromLon = units_DEGtoRAD(-90),
		toLat = units_DEGtoRAD(-80),    toLon = units_DEGtoRAD(+90),
		alt = 2000.0;
	
	int i;
	for(i = 0; i < samples; i++){
		double lat = fromLat + (toLat - fromLat) * i / (samples - 1);
		double lon = fromLon + (toLon - fromLon) * i / (samples - 1);
		wmm_MagneticField mf;
		wmm_getMagneticField(date, lat, lon, alt, &mf);
		//printf("lat %+06.2f lon %+06.2f --> mag var %+5.1f\n",
		//	units_RADtoDEG(lat), units_RADtoDEG(lon), units_RADtoDEG(mf.Decl));
	}
}


int main(int argc, char** argv)
{
	wmm_init("../../objects/WMM.COF");
	
	// Test with samples:
	int i;
	for (i = 0; samples[i].date != 0.0; i++){
		testSample(i, &samples[i]);
	}
	
	// Speed test:
	{
		timer_Type *t = timer_new();
		timer_start(t);
		int computations = 0;
		do {
			journey(1000);
			computations += 1000;
		} while( computations < 1000 && timer_getElapsedMilliseconds(t) < 1000 );
		timer_stop(t);
		printf("Speed test: %d computations per second\n",
			1000 * computations / timer_getElapsedMilliseconds(t));
	}
	
	return err ? 1 : 0;
}

