/*
# This file is part of the Astrometry.net suite.
# Licensed under a 3-clause BSD style license - see LICENSE
*/
#include <string.h>
#include <math.h>
#include <assert.h>

#include "plotindex.h"
#include "cairoutils.h"
#include "ioutils.h"
#include "log.h"
#include "errors.h"
#include "starutil.h"
#include "index.h"
#include "qidxfile.h"
#include "permutedsort.h"

DEFINE_PLOTTER(index);

plotindex_t* plot_index_get(plot_args_t* pargs) {
	return plotstuff_get_config(pargs, "index");
}

void* plot_index_init(plot_args_t* plotargs) {
	plotindex_t* args = calloc(1, sizeof(plotindex_t));
	args->indexes = pl_new(16);
	args->qidxes = pl_new(16);
	args->stars = TRUE;
	args->quads = TRUE;
	args->fill = FALSE;
	return args;
}

static void pad_qidxes(plotindex_t* args) {
	while ((pl_size(args->qidxes)) < pl_size(args->indexes))
		pl_append(args->qidxes, NULL);
}

void plot_quad_xy(cairo_t* cairo, double* quadxy, int dimquads) {
	int k;
	double cx, cy;
	double theta[DQMAX];
	int* perm;

	cx = cy = 0.0;
	for (k=0; k<dimquads; k++) {
		cx += quadxy[2*k+0];
		cy += quadxy[2*k+1];
	}
	cx /= dimquads;
	cy /= dimquads;

	for (k=0; k<dimquads; k++)
		theta[k] = atan2(quadxy[2*k+1] - cy, quadxy[2*k+0] - cx);
	perm = permuted_sort(theta, sizeof(double), compare_doubles_asc, NULL, dimquads);
	for (k=0; k<dimquads; k++) {
		double px,py;
		px = quadxy[2 * perm[k] + 0];
		py = quadxy[2 * perm[k] + 1];
		if (k == 0) {
			cairo_move_to(cairo, px, py);
		} else {
			cairo_line_to(cairo, px, py);
		}
	}
	free(perm);
	cairo_close_path(cairo);
}

static void plotquad(cairo_t* cairo, plot_args_t* pargs, plotindex_t* args, index_t* index, int quadnum, int DQ) {
	int k;
	unsigned int stars[DQMAX];
	double ra, dec;
	double px, py;
	double xy[DQMAX*2];
	int N;

	quadfile_get_stars(index->quads, quadnum, stars);
	N = 0;
	for (k=0; k<DQ; k++) {
		if (startree_get_radec(index->starkd, stars[k], &ra, &dec)) {
			ERROR("Failed to get RA,Dec for star %i\n", stars[k]);
			continue;
		}
		if (!plotstuff_radec2xy(pargs, ra, dec, &px, &py)) {
			ERROR("Failed to convert RA,Dec %g,%g to pixels for quad %i\n", ra, dec, quadnum);
			continue;
		}
		xy[2*k + 0] = px;
		xy[2*k + 1] = py;
		N++;
	}
	if (N < 3)
		return;
	plot_quad_xy(cairo, xy, N);
	if (args->fill)
		cairo_fill(cairo);
	else
		cairo_stroke(cairo);
}

void plot_index_plotquad(cairo_t* cairo, plot_args_t* pargs, plotindex_t* args, index_t* index, int quadnum, int DQ) {
	plotquad(cairo, pargs, args, index, quadnum, DQ);
}

int plot_index_plot(const char* command,
					cairo_t* cairo, plot_args_t* pargs, void* baton) {
	plotindex_t* args = (plotindex_t*)baton;
	int i;
	double ra, dec, radius;
	double xyz[3];
	double r2;

	pad_qidxes(args);

	plotstuff_builtin_apply(cairo, pargs);

	if (plotstuff_get_radec_center_and_radius(pargs, &ra, &dec, &radius)) {
		ERROR("Failed to get RA,Dec center and radius");
		return -1;
	}
	radecdeg2xyzarr(ra, dec, xyz);
	r2 = deg2distsq(radius);
	logmsg("Field RA,Dec,radius = (%g,%g), %g deg\n", ra, dec, radius);
	logmsg("distsq: %g\n", r2);

	for (i=0; i<pl_size(args->indexes); i++) {
		index_t* index = pl_get(args->indexes, i);
		int j, N;
		int DQ;
		double px,py;

		if (args->stars) {
			// plot stars
			double* radecs = NULL;
			startree_search_for(index->starkd, xyz, r2, NULL, &radecs, NULL, &N);
			if (N) {
				assert(radecs);
			}
			logmsg("Found %i stars in range in index %s\n", N, index->indexname);
			for (j=0; j<N; j++) {
				logverb("  RA,Dec (%g,%g) -> x,y (%g,%g)\n", radecs[2*j], radecs[2*j+1], px, py);
				if (!plotstuff_radec2xy(pargs, radecs[j*2], radecs[j*2+1], &px, &py)) {
					ERROR("Failed to convert RA,Dec %g,%g to pixels\n", radecs[j*2], radecs[j*2+1]);
					continue;
				}
				cairoutils_draw_marker(cairo, pargs->marker, px, py, pargs->markersize);
				cairo_stroke(cairo);
			}
			free(radecs);
		}
		if (args->quads) {
			DQ = index_get_quad_dim(index);
			qidxfile* qidx = pl_get(args->qidxes, i);
			if (qidx) {
				int* stars;
				int Nstars;
				il* quadlist = il_new(256);

				// find stars in range.
				startree_search_for(index->starkd, xyz, r2, NULL, NULL, &stars, &Nstars);
				logmsg("Found %i stars in range of index %s\n", N, index->indexname);
				logmsg("Using qidx file.\n");
				// find quads that each star is a member of.
				for (j=0; j<Nstars; j++) {
					uint32_t* quads;
					int Nquads;
					int k;
					if (qidxfile_get_quads(qidx, stars[j], &quads, &Nquads)) {
						ERROR("Failed to get quads for star %i\n", stars[j]);
						return -1;
					}
					for (k=0; k<Nquads; k++)
						il_insert_unique_ascending(quadlist, quads[k]);
				}
				for (j=0; j<il_size(quadlist); j++) {
					plotquad(cairo, pargs, args, index, il_get(quadlist, j), DQ);
				}

			} else {
				// plot quads
				N = index_nquads(index);
				for (j=0; j<N; j++) {
					plotquad(cairo, pargs, args, index, j, DQ);
				}
			}
		}
	}
	return 0;
}

int plot_index_add_qidx_file(plotindex_t* args, const char* fn) {
	int i;
	qidxfile* qidx = qidxfile_open(fn);
	if (!qidx) {
		ERROR("Failed to open quad index file \"%s\"", fn);
		return -1;
	}
	pad_qidxes(args);
	i = pl_size(args->indexes) - 1;
	pl_set(args->qidxes, i, qidx);
	return 0;
}

int plot_index_add_file(plotindex_t* args, const char* fn) {
	index_t* index = index_load(fn, 0, NULL);
	if (!index) {
		ERROR("Failed to open index \"%s\"", fn);
		return -1;
	}
	pl_append(args->indexes, index);
	return 0;
}

int plot_index_command(const char* cmd, const char* cmdargs,
					   plot_args_t* pargs, void* baton) {
	plotindex_t* args = (plotindex_t*)baton;
	if (streq(cmd, "index_file")) {
		const char* fn = cmdargs;
		return plot_index_add_file(args, fn);
	} else if (streq(cmd, "index_qidxfile")) {
		const char* fn = cmdargs;
		return plot_index_add_qidx_file(args, fn);
	} else if (streq(cmd, "index_draw_stars")) {
		args->stars = atoi(cmdargs);
	} else if (streq(cmd, "index_draw_quads")) {
		args->quads = atoi(cmdargs);
	} else if (streq(cmd, "index_fill")) {
		args->fill = atoi(cmdargs);
	} else {
		ERROR("Did not understand command \"%s\"", cmd);
		return -1;
	}
	return 0;
}

void plot_index_free(plot_args_t* plotargs, void* baton) {
	plotindex_t* args = (plotindex_t*)baton;
	int i;
	for (i=0; i<pl_size(args->indexes); i++) {
		index_t* index = pl_get(args->indexes, i);
		index_free(index);
	}
	pl_free(args->indexes);
	for (i=0; i<pl_size(args->qidxes); i++) {
		qidxfile* qidx = pl_get(args->qidxes, i);
		qidxfile_close(qidx);
	}
	pl_free(args->qidxes);
	free(args);
}

