/*
  This file is part of the Astrometry.net suite.
  Copyright 2006, 2007 Dustin Lang, Keir Mierle and Sam Roweis.
  Copyright 2009, 2010, 2012 Dustin Lang.

  The Astrometry.net suite 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, version 2.

  The Astrometry.net suite 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 the Astrometry.net suite ; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
*/
#include <sys/param.h>
#include <assert.h>

#include "plotxy.h"
#include "xylist.h"
#include "cairoutils.h"
#include "log.h"
#include "errors.h"
#include "sip_qfits.h"
#include "tic.h"

DEFINE_PLOTTER(xy);

plotxy_t* plot_xy_get(plot_args_t* pargs) {
	return plotstuff_get_config(pargs, "xy");
}

void* plot_xy_init(plot_args_t* plotargs) {
	plotxy_t* args = calloc(1, sizeof(plotxy_t));
	args->ext = 1;
	args->scale = 1.0;
	args->xyvals = dl_new(32);
	// FITS pixels.
	args->xoff = 1.0;
	args->yoff = 1.0;
	return args;
}

int plot_xy_setsize(plot_args_t* pargs, plotxy_t* args) {
	xylist_t* xyls;
	xyls = xylist_open(args->fn);
	if (!xyls) {
		ERROR("Failed to open xylist from file \"%s\"", args->fn);
		return -1;
	}
	pargs->W = xylist_get_imagew(xyls);
	pargs->H = xylist_get_imageh(xyls);
	if (pargs->W == 0 && pargs->H == 0) {
		qfits_header* hdr = xylist_get_primary_header(xyls);
		pargs->W = qfits_header_getint(hdr, "IMAGEW", 0);
		pargs->H = qfits_header_getint(hdr, "IMAGEH", 0);
	}
	xylist_close(xyls);
	return 0;
}

int plot_xy_plot(const char* command, cairo_t* cairo,
				 plot_args_t* pargs, void* baton) {
	plotxy_t* args = (plotxy_t*)baton;
	// Plot it!
	xylist_t* xyls;
	starxy_t myxy;
	starxy_t* xy = NULL;
	starxy_t* freexy = NULL;
	int Nxy;
	int i;
	double t0;

	plotstuff_builtin_apply(cairo, pargs);

	if (args->fn && dl_size(args->xyvals)) {
		ERROR("Can only plot one of xylist filename and xy_vals");
		return -1;
	}
	if (!args->fn && !dl_size(args->xyvals)) {
		ERROR("Neither xylist filename nor xy_vals given!");
		return -1;
	}

	if (args->fn) {
		t0 = timenow();
		// Open xylist.
		xyls = xylist_open(args->fn);
		if (!xyls) {
			ERROR("Failed to open xylist from file \"%s\"", args->fn);
			return -1;
		}
		// we don't care about FLUX and BACKGROUND columns.
		xylist_set_include_flux(xyls, FALSE);
		xylist_set_include_background(xyls, FALSE);
		if (args->xcol)
			xylist_set_xname(xyls, args->xcol);
		if (args->ycol)
			xylist_set_yname(xyls, args->ycol);

		// Find number of entries in xylist.
		xy = xylist_read_field_num(xyls, args->ext, NULL);
		freexy = xy;
		xylist_close(xyls);
		if (!xy) {
			ERROR("Failed to read FITS extension %i from file %s.\n", args->ext, args->fn);
			return -1;
		}
		Nxy = starxy_n(xy);
		// If N is specified, apply it as a max.
		if (args->nobjs)
			Nxy = MIN(Nxy, args->nobjs);
		//logmsg("%g s to read xylist\n", timenow()-t0);
	} else {
		assert(dl_size(args->xyvals));
		starxy_from_dl(&myxy, args->xyvals, FALSE, FALSE);
		xy = &myxy;
		Nxy = starxy_n(xy);
	}

	// Transform through WCSes.
	if (args->wcs) {
		double ra, dec, x, y;
		assert(pargs->wcs);
		/*
		 // check for any overlap.
		 double pralo,prahi,pdeclo,pdechi;
		 double ralo,rahi,declo,dechi;
		 anwcs_get_radec_bounds(pargs->wcs, 100, &pralo, &prahi, &pdeclo, &pdechi);
		 anwcs_get_radec_bounds(args->wcs, 100, &ralo, &rahi, &declo, &dechi);
		 if (
		 */
		for (i=0; i<Nxy; i++) {
			anwcs_pixelxy2radec(args->wcs,
								// I used to add 1 here
								starxy_getx(xy, i), starxy_gety(xy, i),
								&ra, &dec);
			if (!plotstuff_radec2xy(pargs, ra, dec, &x, &y))
				continue;
			logverb("  xy (%g,%g) -> RA,Dec (%g,%g) -> plot xy (%g,%g)\n",
					starxy_getx(xy,i), starxy_gety(xy,i), ra, dec, x, y);

			// add shift and scale...
			// FIXME -- not clear that we want to do this here...
			/*
			 starxy_setx(xy, i, args->scale * (x - args->xoff));
			 starxy_sety(xy, i, args->scale * (y - args->yoff));
			 starxy_setx(xy, i, x-1);
			 starxy_sety(xy, i, y-1);
			 */

			// Output coords: FITS -> 0-indexed image
			starxy_setx(xy, i, x-1);
			starxy_sety(xy, i, y-1);
		}
	} else {
		// Shift and scale xylist entries.
		if (args->xoff != 0.0 || args->yoff != 0.0) {
			for (i=0; i<Nxy; i++) {
				starxy_setx(xy, i, starxy_getx(xy, i) - args->xoff);
				starxy_sety(xy, i, starxy_gety(xy, i) - args->yoff);
			}
		}
		if (args->scale != 1.0) {
			for (i=0; i<Nxy; i++) {
				starxy_setx(xy, i, args->scale * starxy_getx(xy, i));
				starxy_sety(xy, i, args->scale * starxy_gety(xy, i));
			}
		}
	}

	// Plot markers.
	t0 = timenow();
	for (i=args->firstobj; i<Nxy; i++) {
		double x = starxy_getx(xy, i);
		double y = starxy_gety(xy, i);
		if (plotstuff_marker_in_bounds(pargs, x, y))
			plotstuff_stack_marker(pargs, x, y);
	}
	plotstuff_plot_stack(pargs, cairo);
	//logmsg("%g s to plot xylist\n", timenow()-t0);

	starxy_free(freexy);
	return 0;
}

void plot_xy_set_xcol(plotxy_t* args, const char* col) {
	free(args->xcol);
	args->xcol = strdup_safe(col);
}

void plot_xy_set_ycol(plotxy_t* args, const char* col) {
	free(args->ycol);
	args->ycol = strdup_safe(col);
}

void plot_xy_set_filename(plotxy_t* args, const char* fn) {
	free(args->fn);
	args->fn = strdup_safe(fn);
}

int plot_xy_set_wcs_filename(plotxy_t* args, const char* fn, int ext) {
	anwcs_free(args->wcs);
	args->wcs = anwcs_open(fn, ext);
	if (!args->wcs) {
		ERROR("Failed to read WCS file \"%s\"", fn);
		return -1;
	}
	return 0;
}

int plot_xy_set_offsets(plotxy_t* args, double xo, double yo) {
	args->xoff = xo;
	args->yoff = yo;
	return 0;
}

void plot_xy_vals(plotxy_t* args, double x, double y) {
	dl_append(args->xyvals, x);
	dl_append(args->xyvals, y);
}

void plot_xy_clear_list(plotxy_t* args) {
	dl_remove_all(args->xyvals);
}

int plot_xy_command(const char* cmd, const char* cmdargs,
					plot_args_t* plotargs, void* baton) {
	plotxy_t* args = (plotxy_t*)baton;
	if (streq(cmd, "xy_file")) {
		plot_xy_set_filename(args, cmdargs);
	} else if (streq(cmd, "xy_ext")) {
		args->ext = atoi(cmdargs);
	} else if (streq(cmd, "xy_xcol")) {
		plot_xy_set_xcol(args, cmdargs);
	} else if (streq(cmd, "xy_ycol")) {
		plot_xy_set_ycol(args, cmdargs);
	} else if (streq(cmd, "xy_xoff")) {
		args->xoff = atof(cmdargs);
	} else if (streq(cmd, "xy_yoff")) {
		args->yoff = atof(cmdargs);
	} else if (streq(cmd, "xy_firstobj")) {
		args->firstobj = atoi(cmdargs);
	} else if (streq(cmd, "xy_nobjs")) {
		args->nobjs = atoi(cmdargs);
	} else if (streq(cmd, "xy_scale")) {
		args->scale = atof(cmdargs);
		//} else if (streq(cmd, "xy_wcs")) {
		//return plot_xy_set_wcs_filename(args, cmdargs);
	} else if (streq(cmd, "xy_vals")) {
		plotstuff_append_doubles(cmdargs, args->xyvals);
	} else {
		ERROR("Did not understand command \"%s\"", cmd);
		return -1;
	}
	return 0;
}

void plot_xy_free(plot_args_t* plotargs, void* baton) {
	plotxy_t* args = (plotxy_t*)baton;
	free(args->xyvals);
	anwcs_free(args->wcs);
	free(args->xcol);
	free(args->ycol);
	free(args->fn);
	free(args);
}

