/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set sw=2 sts=2 et cin: */
/*
 * This file is part of the MUSE Instrument Pipeline
 * Copyright (C) 2013-2014 European Southern Observatory
 *
 * This program 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <muse.h>
#include <cairo.h>
#include <cairo-pdf.h>
#include <string.h>

/*----------------------------------------------------------------------------*/
/**
 * @defgroup tools_musegeoplot              Tool muse_geo_plot
 *
 * <b>muse_geo_plot</b>: Plot the contents of a MUSE geometry table.
 *
 * This uses the cairo library to plot a GEOMETRY_TABLE to a file.
 *
 * <b>Command line arguments:</b>
 *   - <tt>-f format</tt> (optional, default is pdf)\n
 *     output format to plot, can be either pdf or png
 *   - <tt>-s xscale yscale</tt> (optional, default is 4., 4.)\n
 *     scale for the output plot, arguments are the scale factors in x and y
 *   - <tt><b>GEOMETRY_TABLE</b></tt>\n
 *     the filename of the input geometry table
 *   - <tt><b>OUTPUT_PLOT</b></tt>\n
 *     the filename of the output plot
 *
 * <b>Return values:</b>
 *   - <tt> 0</tt>\n   Success
 *   - <tt> 1</tt>\n   no input or output filename given
 *   - <tt> 2</tt>\n   argument <tt>-f</tt> without any format string
 *   - <tt> 3</tt>\n   argument <tt>-f</tt> with an unsupported format string
 *   - <tt> 4</tt>\n   argument <tt>-s</tt> with less than two numbers
 *   - <tt> 5</tt>\n   argument <tt>-s</tt> with one negative argument
 *   - <tt> 9</tt>\n   unknown option given
 *   - <tt>10</tt>\n   geometry table could not be loaded from file
 *   - <tt>11</tt>\n   file does not seem to contain a MUSE geometry table
 *   - <tt>50</tt>\n   unknown error
 */
/*----------------------------------------------------------------------------*/

/**@{*/

#define PRINT_USAGE(rc)                                                        \
  fprintf(stderr, "Usage: %s [ -f format ] [ -s xscale yscale ] "              \
          "GEOMETRY_TABLE OUTPUT_PLOT\n", argv[0]);                            \
  cpl_end(); return (rc);

int main(int argc, char **argv)
{
  cpl_init(CPL_INIT_DEFAULT);
  muse_processing_recipeinfo(NULL);

  if (argc <= 2) {
    /* filename is needed at least */
    PRINT_USAGE(1);
  }

  /* argument processing */
  char *tiname = NULL, /* input table */
       *oname = NULL; /* output plot */
  enum eformats {
    PLOT_FORMAT_PDF = 0,
    PLOT_FORMAT_PNG
  };
  unsigned char oformat = PLOT_FORMAT_PDF;
  double xscale = 4.,
         yscale = 4.;
  int i;
  for (i = 1; i < argc; i++) {
    if (strncmp(argv[i], "-f", 3) == 0) {
      /* skip to next arg to get start value */
      i++;
      if (i < argc) {
        if (!strncmp(argv[i], "pdf", 4)) {
          oformat = PLOT_FORMAT_PDF;
        } else if (!strncmp(argv[i], "png", 4)) {
          oformat = PLOT_FORMAT_PNG;
        } else {
          PRINT_USAGE(3);
        }
      } else {
        PRINT_USAGE(2);
      }
    } else if (strncmp(argv[i], "-s", 3) == 0) {
      if (++i + 1 < argc) {
        xscale = atof(argv[i++]);
        yscale = atof(argv[i]);
        if (xscale <= 0 || yscale <= 0) {
          PRINT_USAGE(5);
        }
      } else {
        PRINT_USAGE(4);
      }
    } else if (strncmp(argv[i], "-", 1) == 0) { /* unallowed options */
      PRINT_USAGE(9);
    } else {
      if (tiname && oname) {
        break; /* we have the required name, skip the rest */
      }
      if (!tiname) {
        tiname = argv[i]; /* set the name for the input table */
      } else {
        oname = argv[i]; /* set the name for the output plot */
      }
    }
  } /* for i (all arguments) */
  if (!tiname) {
    PRINT_USAGE(1);
  }

  cpl_table *table = cpl_table_load(tiname, 1, 1);
  if (!table) {
    PRINT_USAGE(10);
  }
  /* check the critical table columns */
  if (!cpl_table_has_column(table, MUSE_GEOTABLE_X) ||
      !cpl_table_has_column(table, MUSE_GEOTABLE_Y) ||
      !cpl_table_has_column(table, MUSE_GEOTABLE_WIDTH) ||
      !cpl_table_has_column(table, MUSE_GEOTABLE_ANGLE)) {
    cpl_table_delete(table);
    PRINT_USAGE(11);
  }

  /* extrema for the size calculation and translation, with some safety margin */
  double wmax = cpl_table_get_column_max(table, MUSE_GEOTABLE_WIDTH),
         xmin = cpl_table_get_column_min(table, MUSE_GEOTABLE_X) - wmax / 2.,
         ymin = cpl_table_get_column_min(table, MUSE_GEOTABLE_Y) - 2,
         xmax = cpl_table_get_column_max(table, MUSE_GEOTABLE_X) + wmax / 2.,
         ymax = cpl_table_get_column_max(table, MUSE_GEOTABLE_Y) + 2,
         sx = xmax - xmin + 1,
         sy = ymax - ymin + 1;
  printf("Using scaling of %.3f in x- and %.3f in y-direction\n", xscale, yscale);
  double xt = -xmin,
         yt = -ymax;
  printf("Translating coordinates by %.3f in x- and %.3f in y-direction\n  "
         "(extrema: %f / %f ... %f / %f)\n", xt, yt, xmin, ymin, xmax, ymax);

  /* create the cairo objects */
  cairo_status_t rc = CAIRO_STATUS_SUCCESS;
  cairo_surface_t *cs;
  if (oformat == PLOT_FORMAT_PDF) { /* PDF surface and output */
    cs = cairo_pdf_surface_create(oname, xscale * sx, yscale * sy);
    rc = cairo_surface_status(cs);
    if (rc != CAIRO_STATUS_SUCCESS) {
      fprintf(stderr, "opening PDF (\"%s\") returned %d!\n", oname, rc);
      rc = 50;
    } else {
      fprintf(stdout, "writing PDF to \"%s\"\n", oname);
    }
  } else { /* image surface and PNG output */
    cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
                                    xscale * sx, yscale * sy);
  }
  cairo_t *cr = cairo_create(cs);
  /* fill with greyish background */
  cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
  cairo_paint(cr);
  /* set up scaling and origin */
  cairo_scale(cr, xscale, -yscale);
  cairo_translate(cr, xt, yt);
#if 0 /* coordinate transform debugging */
  cairo_matrix_t *cm = cpl_calloc(1, sizeof(cairo_matrix_t));
  cairo_get_matrix(cr, cm);
  cairo_status_t cstat = cairo_status(cr);
  fprintf(stderr, "cm = %p, cstat = %d/%s, size is %f,%f\n",
          cm, cstat, cairo_status_to_string(cstat), xscale * sx, yscale * sy);
  double xtt = xmin, ytt = ymin + 2;
  cairo_matrix_transform_point(cm, &xtt, &ytt);
  fprintf(stderr, "%f,%f is transformed to %f,%f\n", xmin, ymin + 2, xtt, ytt);
  xtt = xmin, ytt = ymax - 2;
  cairo_matrix_transform_point(cm, &xtt, &ytt);
  fprintf(stderr, "%f,%f is transformed to %f,%f\n", xmin, ymax - 2, xtt, ytt);
  xtt = xmax, ytt = ymin + 2;
  cairo_matrix_transform_point(cm, &xtt, &ytt);
  fprintf(stderr, "%f,%f is transformed to %f,%f\n", xmax, ymin + 2, xtt, ytt);
  xtt = xmax, ytt = ymax - 2;
  cairo_matrix_transform_point(cm, &xtt, &ytt);
  fprintf(stderr, "%f,%f is transformed to %f,%f\n", xmax, ymax - 2, xtt, ytt);
  cpl_free(cm);
#endif

  /* mark the origin (x,y) = (0,0) */
  cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
  cairo_save(cr);
  cairo_move_to(cr, -1000., 0.);
  cairo_rel_line_to(cr, +5000, 0.);
  cairo_scale(cr, 1., 1. / yscale); /* stroke 1 pixel high */
  cairo_stroke(cr);
  cairo_restore(cr);
  cairo_save(cr);
  cairo_move_to(cr, 0., -1000);
  cairo_rel_line_to(cr, 0., +5000);
  cairo_scale(cr, 1. / xscale, 1.); /* stroke 1 pixel wide */
  cairo_stroke(cr);
  cairo_restore(cr);
#if 0 /* mark origin with a bit dot */
  cairo_save(cr);
  cairo_scale(cr, 1. / xscale, 1. / yscale);
  cairo_set_source_rgb(cr, 0.0, 0.0, 0.9);
  cairo_arc(cr, 0, 0, 5., 0, CPL_MATH_2PI);
  cairo_fill(cr);
  cairo_restore(cr);
#endif

  /* loop through all table rows and draw the stuff */
  int n = cpl_table_get_nrow(table);
  for (i = 0; i < n; i++) {
    double x = cpl_table_get(table, MUSE_GEOTABLE_X, i, NULL),
           y = cpl_table_get(table, MUSE_GEOTABLE_Y, i, NULL),
           width = cpl_table_get(table, MUSE_GEOTABLE_WIDTH, i, NULL),
           angrad = cpl_table_get(table, MUSE_GEOTABLE_ANGLE, i, NULL)
                  * CPL_MATH_RAD_DEG;
    unsigned short ifu = cpl_table_get_int(table, MUSE_GEOTABLE_FIELD, i, NULL);
    if ((ifu - 0) % 3 == 0) { /* e.g. IFU 21 */
      cairo_set_source_rgba(cr, 0.2, 0.2, 0.4, 0.5); /* blueish-grey */
    } else if ((ifu - 1) % 3 == 0) { /* e.g. IFU 22 */
      cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.5);
    } else if ((ifu - 2) % 3 == 0) { /* e.g. IFU 23 */
      cairo_set_source_rgba(cr, 1.0, 0.843, 0.0, 0.5); /* gold = #FFD700 */
    } else {
      cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.5);
    }
#if 0
    printf("ifu %hu slicesky %hu angle %f\n",
           ifu, cpl_table_get_int(table, MUSE_GEOTABLE_SKY, i, NULL),
           angrad * CPL_MATH_DEG_RAD);
#endif
    /* in the cairo rectangle function, x/y define the top left corner! */
    cairo_save(cr);
    cairo_translate(cr, x, y);
    cairo_rotate(cr, angrad);
    cairo_rectangle(cr, -width / 2., -0.5, width, 1.);
    cairo_fill(cr);
    /* Mark the reference slice. We want a really fine line, so this doesn't *
     * work with cairo_rectangle() function, but we need to draw vertical    *
     * and horizontal lines separately, with different linewidths.           */
    if (y == 0.0) {
      cairo_set_source_rgba(cr, 0.0, 0.0, 0.9, 0.8);
      cairo_move_to(cr, - width / 2., - 0.5);
      cairo_set_line_width(cr, 1. / yscale);
      cairo_rel_line_to(cr, width, 0.);
      cairo_set_line_width(cr, 1. / xscale);
      cairo_rel_line_to(cr, 0., 1.);
      cairo_set_line_width(cr, 1. / yscale);
      cairo_rel_line_to(cr, -width, 0.);
      cairo_close_path(cr);
      cairo_stroke(cr);
    } /* if y == 0.0 (reference slice) */
    cairo_restore(cr);
  } /* for i (all table rows) */

  if (oformat == PLOT_FORMAT_PNG) {
    cairo_surface_write_to_png(cs, oname);
    if (rc != CAIRO_STATUS_SUCCESS) {
      fprintf(stderr, "writing PNG (\"%s\") returned %d!\n", oname, rc);
      rc = 50;
    } else {
      fprintf(stdout, "written PNG to \"%s\"\n", oname);
    }
  } /* if PNG */
  cairo_destroy(cr);
  cairo_surface_destroy(cs);
  cairo_debug_reset_static_data();
  cpl_table_delete(table);
  cpl_errorstate_dump(0,0,0);
  cpl_memory_dump();
  cpl_end();
  return rc;
}

/**@}*/
