/*
 # 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 "plotoutline.h"
#include "cairoutils.h"
#include "ioutils.h"
#include "log.h"
#include "errors.h"
#include "sip-utils.h"
#include "sip_qfits.h"
#include "starutil.h"

DEFINE_PLOTTER(outline);

plotoutline_t* plot_outline_get(plot_args_t* pargs) {
    return plotstuff_get_config(pargs, "outline");
}

void* plot_outline_init(plot_args_t* plotargs) {
    plotoutline_t* args = calloc(1, sizeof(plotoutline_t));
    args->stepsize = 10;
    return args;
}

static void walk_callback(const anwcs_t* wcs, double ix, double iy,
                          double ra, double dec, void* token) {
    dl* radecs = (dl*)token;
    dl_append(radecs, ra);
    dl_append(radecs, dec);
}

int plot_outline_plot(const char* command,
                      cairo_t* cairo, plot_args_t* pargs, void* baton) {
    plotoutline_t* args = (plotoutline_t*)baton;
    dl* rd;
    int i;
    pl* lists;

    assert(args->stepsize > 0);
    assert(args->wcs);
    assert(pargs->wcs);

    plotstuff_builtin_apply(cairo, pargs);

    logverb("Plotting outline of WCS: image size is %g x %g\n",
            anwcs_imagew(args->wcs), anwcs_imageh(args->wcs));

    rd = dl_new(256);
    anwcs_walk_image_boundary(args->wcs, args->stepsize, walk_callback, rd);
    logverb("Outline: walked in %zu steps\n", dl_size(rd)/2);

    if (dl_size(rd) == 0) {
        printf("plot_outline: empty WCS outline.\n");
        anwcs_print(args->wcs, stdout);
        dl_free(rd);
        return 0;
    }

    // avoid special case when there is a break between
    // the beginning and end of the list.
    dl_append(rd, dl_get(rd, 0));
    dl_append(rd, dl_get(rd, 1));

    lists = anwcs_walk_outline(pargs->wcs, rd, args->fill);
    dl_free(rd);
    for (i=0; i<pl_size(lists); i++) {
        dl* xy = pl_get(lists, i);
        int j;
        for (j=0; j<dl_size(xy)/2; j++) {
            double x,y;
            x = dl_get(xy, j*2+0);
            y = dl_get(xy, j*2+1);
            if (j == 0) {
                cairo_move_to(cairo, x, y);
            } else {
                cairo_line_to(cairo, x, y);
            }
        }
        cairo_close_path(cairo);
        if (args->fill)
            cairo_fill(cairo);
        else
            cairo_stroke(cairo);
        dl_free(xy);
    }
    pl_free(lists);
    return 0;
}

int plot_outline_set_wcs_size(plotoutline_t* args, int W, int H) {
    if (!args->wcs) {
        ERROR("No WCS is currently set.");
        return -1;
    }
    anwcs_set_size(args->wcs, W, H);
    return 0;
}

int plot_outline_set_wcs_file(plotoutline_t* args, const char* filename, int ext) {
    anwcs_t* wcs = anwcs_open(filename, ext);
    if (!wcs) {
        ERROR("Failed to read WCS file \"%s\"", filename);
        return -1;
    }
    logverb("Read WCS file %s\n", filename);
    if (args->wcs)
        anwcs_free(args->wcs);
    args->wcs = wcs;
    //anwcs_print(args->wcs, stdout);
    return 0;
}

int plot_outline_set_wcs(plotoutline_t* args, const sip_t* wcs) {
    if (args->wcs)
        anwcs_free(args->wcs);
    args->wcs = anwcs_new_sip(wcs);
    return 0;
}

int plot_outline_set_tan_wcs(plotoutline_t* args, const tan_t* wcs) {
    if (args->wcs)
        anwcs_free(args->wcs);
    args->wcs = anwcs_new_tan(wcs);
    return 0;
}

int plot_outline_set_fill(plotoutline_t* args, anbool fill) {
    args->fill = fill;
    return 0;
}

int plot_outline_command(const char* cmd, const char* cmdargs,
                         plot_args_t* pargs, void* baton) {
    plotoutline_t* args = (plotoutline_t*)baton;
    if (streq(cmd, "outline_wcs")) {
        if (plot_outline_set_wcs_file(args, cmdargs, 0)) {
            return -1;
        }
    } else if (streq(cmd, "outline_fill")) {
        if (streq(cmdargs, "0"))
            args->fill = FALSE;
        else
            args->fill = TRUE;
    } else if (streq(cmd, "outline_step")) {
        args->stepsize = atof(cmdargs);
    } else {
        ERROR("Did not understand command \"%s\"", cmd);
        return -1;
    }
    return 0;
}

void plot_outline_free(plot_args_t* plotargs, void* baton) {
    plotoutline_t* args = (plotoutline_t*)baton;
    free(args);
}

