#include <netcdf.h>

#include "nq_basic.h"
#include "nq_structure.h"
#include "nq_density.h"
#include <visu_tools.h>
#include <renderingMethods/renderingAtomic.h>

#define NANOQUANTA_DESCRIPTION _("<span size=\"smaller\">" \
				 "This plug-in introduces support for\n" \
				 "<b>NETCDF</b> file format defined by the\n" \
				 "European network <b>NANOQUANTA</b>.</span>")
#define NANOQUANTA_AUTHORS     _("Caliste Damien:\n   structure/density loading.")

static gchar *iconPath;

/* Required methods for a loadable module. */
gboolean nanoquantaInit()
{
  RenderingFormatLoad* meth;

  DBG_fprintf(stderr, "Nanoquanta : loading plug-in 'nanoquanta'...\n");
  NQ_ERROR = g_quark_from_string("nanoquanta");

  DBG_fprintf(stderr, "Nanoquanta : declare a new rendering load method.\n");
  meth = nqStructuralInit();
  renderingAtomicAdd_loadMethod(meth);

  iconPath = g_build_filename(v_sim_pixmaps_dir, "nanoquanta.png", NULL);

  DBG_fprintf(stderr, "Nanoquanta : declare a new density load method.\n");
  nqDensityInit();

  return TRUE;
}

const char* nanoquantaGet_description()
{
  return NANOQUANTA_DESCRIPTION;
}

const char* nanoquantaGet_authors()
{
  return NANOQUANTA_AUTHORS;
}

const char* nanoquantaGet_icon()
{
  return iconPath;
}



gboolean nqOpen_netcdfFile(const char* filename, int *netcdfId, GError **error)
{
  int status, i;
  char *varsNames[3] = {"file_format", "file_format_version", "Conventions"};
  nc_type varsType[3] = {NC_CHAR, NC_FLOAT, NC_CHAR};
  nc_type altVarsType[3] = {NC_CHAR, NC_DOUBLE, NC_CHAR};
  size_t varsLength[3] = {80, 1, 80};
  nc_type readType;
  size_t readLength[3];
  char format[256];
  float version;

  g_return_val_if_fail(error && *error == (GError*)0, FALSE);
  g_return_val_if_fail(netcdfId && filename, FALSE);

  /* Open the file as a NETCDF file. */
  status = nc_open(filename, NC_NOWRITE, netcdfId);
  if (status != NC_NOERR)
    {
      *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_OPEN,
			                     nc_strerror(status));
      return FALSE;
    }

  /* From here, the file is opened. */

  /* Grep the header variables to check that it is a NETCDF file
     following rules of NANOQUANTA specifications. */
  DBG_fprintf(stderr, "NQ Basic : checking header of file '%s'.\n", filename);
  /* Check lengths and types. */
  for (i = 0; i < 3; i++)
    {
      status = nc_inq_att(*netcdfId, NC_GLOBAL, varsNames[i], &readType, readLength + i);
      if (status != NC_NOERR)
        {
          *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_HEADER,
                      			   nc_strerror(status));
          nqClose_netcdfFile(*netcdfId);
          return FALSE;
        }
      DBG_fprintf(stderr, " | header '%s' : type %d (%d), length %d (%d).\n",
                  varsNames[i], (int)readType, (int)varsType[i],
                  (int)readLength[i], (int)varsLength[i]);
      if ((readType != varsType[i] && readType != altVarsType[i]) || 
           readLength[i] > varsLength[i])
        {
          *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_HEADER,
    			                     _("Global attribute '%s' has a wrong "
    			                       "length or type.\n"), varsNames[i]);
          nqClose_netcdfFile(*netcdfId);
          return FALSE;
        }
    }
  /* Check values. */
  status = nc_get_att_text(*netcdfId, NC_GLOBAL, varsNames[0], format);
  if (status != NC_NOERR)
    {
      *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_HEADER,
			   nc_strerror(status));
      nqClose_netcdfFile(*netcdfId);
      return FALSE;
    }
  format[readLength[0]] = '\0';
  DBG_fprintf(stderr, " | header '%s' value '%s'.\n", varsNames[0], format);
  if (strcmp(format, "ETSF Nanoquanta"))
    {
      *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_HEADER,
			   _("Variable 'file_format' should be "
			     "'ETSF Nanoquanta' but is '%s'.\n"), format);
      nqClose_netcdfFile(*netcdfId);
      return FALSE;
    }
  status = nc_get_att_float(*netcdfId, NC_GLOBAL, varsNames[1], &version);
  if (status != NC_NOERR)
    {
      *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_HEADER,
			   nc_strerror(status));
      nqClose_netcdfFile(*netcdfId);
      return FALSE;
    }
  DBG_fprintf(stderr, " | header '%s' value %f.\n", varsNames[1], version);
  if (version < 1.2)
    {
      *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_HEADER,
			   _("Supported version are 1.2 and over but"
			     " this file is only %f.\n"), version);
      nqClose_netcdfFile(*netcdfId);
      return FALSE;
    }

  return TRUE;
}

gboolean nqClose_netcdfFile(int netcdfId)
{
  int status;

  status = nc_close(netcdfId);
  if (status != NC_NOERR)
    {
      g_warning(nc_strerror(status));
      return FALSE;
    }

  return TRUE;
}

static gboolean nqErrorReport(GError **error, const char *message, ...)
{
  va_list args;
  gchar *formatted;

  va_start(args, message);
  formatted = g_strdup_vprintf(message, args);
  va_end(args);

  *error = g_error_new(NQ_ERROR, NQ_ERROR_FILE_FORMAT, formatted);

  g_free(formatted);
  
  return FALSE;
}

gboolean nqGetDim(int netcdfId, GError **error, char *name, int *varId, size_t *value)
{
  int status;

  /* Grep the number of elements. */
  status = nc_inq_dimid(netcdfId, name, varId);
  if (status != NC_NOERR)
    return nqErrorReport(error, _("Reading '%s': %s."),
			                   name, nc_strerror(status));
  status = nc_inq_dimlen(netcdfId, *varId, value);
  if (status != NC_NOERR)
    return nqErrorReport(error, _("Retrieve value for variable '%s': %s."),
			                   name, nc_strerror(status));
  return TRUE;
}

gboolean nqCheckVar(int netcdfId, GError **error, char *name, int *varId,
			              nc_type ncType, int nbDims, size_t *nbEleDims)
{
  int status;
  nc_type localType;
  char *typeNames[] = {"NAT", "BYTE", "CHAR", "SHORT", "INT", "FLOAT", "DOUBLE"};
  int localDims;
  int *localNbDims;
  int i;
  size_t dimSize;

  status = nc_inq_varid(netcdfId, name, varId);
  if (status != NC_NOERR)
    return nqErrorReport(error, _("Reading '%s': %s."),
			    name, nc_strerror(status));
  status = nc_inq_vartype(netcdfId, *varId, &localType);
  if (status != NC_NOERR)
    return nqErrorReport(error, _("Checking variable '%s': %s."),
			    name, nc_strerror(status));
  if (localType != ncType)
    return nqErrorReport(error, _("Variable '%s' should be of type '%s'."),
			    name, typeNames[ncType]);
  status = nc_inq_varndims(netcdfId, *varId, &localDims);
  if (status != NC_NOERR)
    return nqErrorReport(error, _("Checking variable '%s': %s."),
			    name, nc_strerror(status));
  if (localDims != nbDims)
    return nqErrorReport(error,
			    _("Variable '%s' should be a %d dimension array."),
			    name, nbDims);
  localNbDims = g_malloc(sizeof(int) * nbDims);
  status = nc_inq_vardimid(netcdfId, *varId, localNbDims);
  if (status != NC_NOERR)
    {
      g_free(localNbDims);
      return nqErrorReport(error, _("Checking variable '%s': %s."),
			      name, nc_strerror(status));
    }
  for (i = 0; i< nbDims; i++)
    {
      status = nc_inq_dimlen(netcdfId, localNbDims[i], &dimSize);
      if (status != NC_NOERR)
	{
	  g_free(localNbDims);
	  return nqErrorReport(error, _("Checking dimension ID %d: %s."),
				  localNbDims[i], nc_strerror(status));
	}
      if (dimSize != nbEleDims[i])
      {
	g_free(localNbDims);
	return nqErrorReport(error,
				_("Variable '%s' is not consistent with"
				  " declaration of dimensions."), name);
      }
    }
  g_free(localNbDims);
  return TRUE;
}



syntax highlighted by Code2HTML, v. 0.9.1