/*
# 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 "os-features.h"
#include "plothealpix.h"
#include "sip-utils.h"
#include "cairoutils.h"
#include "ioutils.h"
#include "log.h"
#include "errors.h"
#include "healpix-utils.h"
#include "healpix.h"

DEFINE_PLOTTER(healpix);

plothealpix_t* plot_healpix_get(plot_args_t* pargs) {
	return plotstuff_get_config(pargs, "healpix");
}

void* plot_healpix_init(plot_args_t* plotargs) {
	plothealpix_t* args = calloc(1, sizeof(plothealpix_t));
	args->nside = 1;
	args->stepsize = 50;
	return args;
}

int plot_healpix_plot(const char* command,
					  cairo_t* cairo, plot_args_t* pargs, void* baton) {
	plothealpix_t* args = (plothealpix_t*)baton;
	double ra,dec,rad;
	il* hps;
	int i;
	double hpstep;
	int minx[12], maxx[12], miny[12], maxy[12];

	plotstuff_builtin_apply(cairo, pargs);

	if (plotstuff_get_radec_center_and_radius(pargs, &ra, &dec, &rad)) {
		ERROR("Failed to get RA,Dec center and radius");
		return -1;
	}
	hps = healpix_rangesearch_radec(ra, dec, rad, args->nside, NULL);
	logmsg("Found %zu healpixes in range.\n", il_size(hps));
	hpstep = args->nside * args->stepsize * plotstuff_pixel_scale(pargs) / 60.0 / healpix_side_length_arcmin(args->nside);
	hpstep = MIN(1, hpstep);
	logmsg("Taking steps of %g in healpix space\n", hpstep);

	// For each of the 12 top-level healpixes, find the range of healpixes covered by this image.
	for (i=0; i<12; i++) {
		maxx[i] = maxy[i] = -1;
		minx[i] = miny[i] = args->nside+1;
	}
	for (i=0; i<il_size(hps); i++) {
		int hp = il_get(hps, i);
		int hpx, hpy;
		int bighp;
		healpix_decompose_xy(hp, &bighp, &hpx, &hpy, args->nside);
		logverb("  hp %i: bighp %i, x,y (%i,%i)\n", i, bighp, hpx, hpy);
		minx[bighp] = MIN(minx[bighp], hpx);
		maxx[bighp] = MAX(maxx[bighp], hpx);
		miny[bighp] = MIN(miny[bighp], hpy);
		maxy[bighp] = MAX(maxy[bighp], hpy);
	}
	il_free(hps);

	for (i=0; i<12; i++) {
		int hx,hy;
		int hp;
		double d, frac;
		double x,y;

		if (maxx[i] == -1)
			continue;
		logverb("Big healpix %i: x range [%i, %i], y range [%i, %i]\n",
			   i, minx[i], maxx[i], miny[i], maxy[i]);

		for (hy = miny[i]; hy <= maxy[i]; hy++) {
			logverb("  y=%i\n", hy);
			for (d=minx[i]; d<=maxx[i]; d+=hpstep) {
				hx = floor(d);
				frac = d - hx;
				hp = healpix_compose_xy(i, hx, hy, args->nside);
				healpix_to_radecdeg(hp, args->nside, frac, 0.0, &ra, &dec);
				if (!plotstuff_radec2xy(pargs, ra, dec, &x, &y))
					continue;
				if (d == minx[i])
					cairo_move_to(pargs->cairo, x, y);
				else
					cairo_line_to(pargs->cairo, x, y);
			}
			cairo_stroke(pargs->cairo);
		}
		for (hx = minx[i]; hx <= maxx[i]; hx++) {
			for (d=miny[i]; d<=maxy[i]; d+=hpstep) {
				hy = floor(d);
				frac = d - hy;
				hp = healpix_compose_xy(i, hx, hy, args->nside);
				healpix_to_radecdeg(hp, args->nside, 0.0, frac, &ra, &dec);
				if (!plotstuff_radec2xy(pargs, ra, dec, &x, &y))
					continue;
				if (d == miny[i])
					cairo_move_to(pargs->cairo, x, y);
				else
					cairo_line_to(pargs->cairo, x, y);
			}
			cairo_stroke(pargs->cairo);
		}
	}
	return 0;
}

int plot_healpix_command(const char* cmd, const char* cmdargs,
						 plot_args_t* pargs, void* baton) {
	plothealpix_t* args = (plothealpix_t*)baton;
	if (streq(cmd, "healpix_nside")) {
		args->nside = atoi(cmdargs);
	} else if (streq(cmd, "healpix_stepsize")) {
		args->stepsize = atoi(cmdargs);
	} else {
		ERROR("Did not understand command \"%s\"", cmd);
		return -1;
	}
	return 0;
}

void plot_healpix_free(plot_args_t* plotargs, void* baton) {
	plothealpix_t* args = (plothealpix_t*)baton;
	free(args);
}

