/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2006)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2006)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "visu_basic.h"
#include "visu_object.h"
#include "visu_rendering.h"
#include "visu_extension.h"
#include "visu_data.h"
#include "visu_elements.h"
#include "visu_commandLine.h"
#include "opengl.h"
#include "visu_dump.h"
#include "visu_pairs.h"
#include "visu_tools.h"
#include "visu_configFile.h"
#include "visu_pickMesure.h"
#include "extraFunctions/dataFile.h"
#include "extraFunctions/plane.h"
#include "openGLFunctions/objectList.h"
#include "OSOpenGL/visu_OSInterface.h"

#include "renderingMethods/renderingAtomic.h"
#include "renderingMethods/renderingSpin.h"

#include <stdio.h>
#include <locale.h>
#include <unistd.h> /* For the access markers R_OK, W_OK ... */

/* Default value for the opengl window size. */
#define x_gl 650
#define y_gl 50
#define size_gl 600

/* Valeurs par défaut pour les chemins utiles dans V_Sim. */
#if SYSTEM_X11 == 1
#define V_SIM_DATA_DIR_DEFAULT    "/usr/local/share/v_sim"
#define V_SIM_CONF_DIR_DEFAULT    "/usr/local/etc"
#define V_SIM_LEGAL_DIR_DEFAULT   "/usr/local/share/doc/v_sim"
#define V_SIM_PIXMAPS_DIR_DEFAULT "/usr/local/share/v_sim/pixmaps"
#endif
#if SYSTEM_WIN32 == 1
#define V_SIM_DATA_DIR_DEFAULT    "C:\\PROGRAM FILES\\V_SIM\\share\\v_sim"
#define V_SIM_CONF_DIR_DEFAULT    "C:\\PROGRAM FILES\\V_SIM\\etc"
#define V_SIM_LEGAL_DIR_DEFAULT   "C:\\PROGRAM FILES\\V_SIM\\share\\doc\\v_sim"
#define V_SIM_PIXMAPS_DIR_DEFAULT "C:\\PROGRAM FILES\\V_SIM\\share\\v_sim\\pixmaps"
#endif

/* Main loop if gtk is not used. */
GMainLoop *gLoop;
gboolean commandLineLoadWithoutGtk(gpointer data);

/* This calls the load method of the current rendering
   method. It returns a value different from zero if an
   error occurs and the message is stored in bufferError.
   bufferError is allocated by the routine via malloc and
   must therefore be freed by free after the call to this routine
   If there is no error, bufferError may contain different
   informations on what happen. */
int loadDataFromFile(VisuData *data, GString **bufferError)
{
  RenderingMethod *method;
  int res;

  if (bufferError)
    *bufferError = g_string_new("");

  method = getRenderingMethodInUse();
  if (!method)
    {
      if (bufferError)
	g_string_printf(*bufferError, _("WARNING! No rendering method is selected.\n"));
      return 0;
    }

  if (!method->loadMethod)
    {
      if (bufferError)
	g_string_printf(*bufferError, _("WARNING! No load method exists for the"
					" specified rendering method.\n"));
      return 0;
    }

  res = method->loadMethod(data, bufferError);
  /* Set flags... */
  if (res)
    visuDataSet_changeElementFlag(data, FALSE);

  DBG_fprintf(stderr, "Visu Basic : emitting 'dataLoaded' signal with %p data argument (%d).\n",
	      (gpointer)data, res);
  if (res)
    g_signal_emit (visu, VISU_GET_CLASS (visu)->dataLoaded_signal_id,
		   0 /* details */, (gpointer)data, NULL);
  else
    g_signal_emit (visu, VISU_GET_CLASS (visu)->dataLoaded_signal_id,
		   0 /* details */, (gpointer)0, NULL);
  return res;
}


void initVisuMain()
{
  int res;

  DBG_fprintf(stderr,"--- Initialising variables ---\n");
  /* We want to read . as floating point separator : in french it is , */
  setlocale(LC_NUMERIC, "C");

  /* Initialise le stockage des couleurs */
  initColorStorage();

  /* initialise la gestion des resources et des paramètres. */
  DBG_fprintf(stderr,"--- Initialising the VisuConfigFile part ---\n");
  res = visuConfigFile_init();
  if (!res)
    exit(1);

  /* initialise les fonctions d'export. */
  DBG_fprintf(stderr,"--- Initialising dump modules ---\n");
  res = initDumpModule();
  if (!res)
    exit(1);

  /* initialise OpenGL. */
  DBG_fprintf(stderr,"--- Initialising OpenGL ---\n");
  res = initOpengl();
  if (!res)
    exit(1);

  /* initialise les ajouts OpenGL. */
  DBG_fprintf(stderr,"--- Initialising OpenGL extensions ---\n");
  res = initOpenGLExtensions();
  if (!res)
    exit(1);

  /* initialise les méthodes de rendu. */
  DBG_fprintf(stderr,"--- Initialising rendering methods ---\n");
  res = initRenderingMethods();
  if (!res)
    exit(1);

  /* initialise la gestion des éléments et des données. */
  DBG_fprintf(stderr,"--- Initialising the elements and data part of visu ---\n");
  res = initVisuElements();
  if (!res)
    exit(1);

  /* initialise la gestion des liaisons. */
  DBG_fprintf(stderr,"--- Initialising pairs modules ---\n");
  res = initPairsModule();
  if (!res)
    exit(1);

  /* initialise la gestion des sessions pick. */
  DBG_fprintf(stderr,"--- Initialising pick module ---\n");
  initPick_module();

  /* initialise la gestion de la couleur des éléments par un fichier extérieur. */
  DBG_fprintf(stderr,"--- Initialising dataFile modules ---\n");
  res = initDataFileModule();
  if (!res)
    exit(1);

  /* Charge toutes les extensions OpenGL. */
  DBG_fprintf(stderr,"--- Initialising external OpenGL Extensions ---\n");
  loadExtensions();
}


int mainExport(void)
{
  char* path;
  int res;
  char* filename, *spin_filename;
  DumpImage *dumpData;
  VisuData *newData;
  GString *errorMessage;
  int width, height;

  gLoop = g_main_loop_new(NULL, FALSE);
  g_type_init();

  /* Creating the visu object to handle the signals. */
  visu = g_object_new (VISU_TYPE, NULL);

  DBG_fprintf(stderr,"--- Initialising the rendering window ---\n");
  res = initDisplay();
  res = initOpenGLContext() || res;
  if (!res)
    g_error("FATAL ERROR! Can't initialize the display.\n");

  initVisuMain();

  DBG_fprintf(stderr,"--- Attach the pixmap context ---\n");
  commandLineGet_XWindowGeometry(&width, &height);
  res = setOpenGLContext(TRUE, &dumpData, width, height);
  if (res)
    return 1;

  DBG_fprintf(stderr,"--- Initialising the parameters ---\n");
  /* Look for parameters file */
  path = visuConfigFileGet_validPath(VISU_CONFIGFILE_PARAMETER, R_OK, 0);
  if (path)
    {
      errorMessage = (GString*)0;
      res = visuConfigFileLoad(VISU_CONFIGFILE_PARAMETER, path, &errorMessage);
      if (!res && errorMessage)
	fprintf(stderr, "%s\n", errorMessage->str);
      if (errorMessage)
	g_string_free(errorMessage, TRUE);
      free(path);
    }
  else
    g_warning("Unable to find a valid parameters file."
	      " Starting with defaults.\n");

  DBG_fprintf(stderr,"--- Initialising resources ---\n");
  /* Look for resources file */
  path = visuConfigFileGet_validPath(VISU_CONFIGFILE_RESOURCE, R_OK, 0);
  if (path)
    {
      errorMessage = (GString*)0;
      res = visuConfigFileLoad(VISU_CONFIGFILE_RESOURCE, path, &errorMessage);
      if (!res && errorMessage)
	fprintf(stderr, "%s\n", errorMessage->str);
      if (errorMessage)
	g_string_free(errorMessage, TRUE);
      free(path);
    }

  newData = visuDataNew();
  if (!newData)
    return 1;

  filename = commandLineGet_ArgFilename();
  spin_filename = commandLineGet_ArgSpinFileName();
  if (filename && !spin_filename)
    {
      setRenderingMethodInUse(pointerOnRenderingAtomicMethod);

      visuDataAdd_file(newData, filename, FILE_KIND_POSITION);
      free(filename);
      DBG_fprintf(stderr, "Visu Main : adding the load call back in the queue.\n");
      g_idle_add(commandLineLoadWithoutGtk, (gpointer)newData);
    }
  else if(filename && spin_filename)
    {
      setRenderingMethodInUse(pointerOnRenderingSpinMethod);

      visuDataAdd_file(newData, filename, FILE_KIND_POSITION);
      visuDataAdd_file(newData, spin_filename, FILE_KIND_SPIN);
      free(filename);
      free(spin_filename);
      
      DBG_fprintf(stderr, "Visu Main : adding the load call back in the queue.\n");
      g_idle_add(commandLineLoadWithoutGtk, (gpointer)newData);
    }

  g_main_loop_run(gLoop);

  return 0;
}

gboolean quitVisu(gpointer data)
{
  if (gLoop)
    g_main_loop_quit(gLoop);

  return FALSE;
}

gboolean commandLineLoadWithoutGtk(gpointer data)
{
  gchar *colorFile, *planesFile;
  GString *message;
  int errors, i, presetShade;
  gboolean somethingIsLoaded, res;
  int *colUsed;
  float *translations;
  GList *list;
  Shade *shade;
  VisuData *visuData;
  guint width, height;
  GError *error;
  OpenGLExtension *plane;
  int planesId;

  visuData = VISU_DATA(data);
  g_return_val_if_fail(visuData, FALSE);

  loadFileWithoutGtk(visuData);

  commandLineGet_XWindowGeometry((int*)&width, (int*)&height);
  visuDataSet_sizeOfView(data, width, height);

  /* Post loading sequence, corresponding to all other arguments given to command line. */
  /* translate argument */
  translations = commandLineGet_translation();
  if (translations)
    {
      visuDataSet_XYZtranslation(visuData, translations);
      visuData_constrainedInTheBox(visuData);
    }

  DBG_fprintf(stderr, "Visu basic : emitting the 'dataReadyForRendering' signal.\n");
  g_signal_emit(visu, VISU_GET_CLASS (visu)->dataReadyForRendering_signal_id,
		0 /* details */, (gpointer)visuData, NULL);
  DBG_fprintf(stderr, "Visu data : emitting done.\n");

  /* Post loading sequence, corresponding to all other arguments given to command line. */
  /* colorize argument */
  colorFile = commandLineGet_colorizeFileName();
  if (colorFile)
    {
      DBG_fprintf(stderr, "Visu basic : loading a new data file '%s'.\n", colorFile);
      message = g_string_new(_("Loading data file...\n"));
      somethingIsLoaded = dataFileSet_file(visuData, colorFile, message, &errors);

      /* Print errors if necessary. */
      if (errors)
	fprintf(stderr, "%s\n", message->str);
      g_string_free(message, TRUE);

      if (somethingIsLoaded)
	{
	  colUsed = commandLineGet_colorizeColUsed();
	  if (colUsed)
	    for (i = 0; i < 2; i++)
	      dataFileSet_colUsed(visuData, colUsed[i] - 1, i);
	  else
	    {
	      presetShade = commandLineGet_colorizePresetColor();
	      if (presetShade < 0)
		presetShade = 0;
	      list = dataFileBuild_presetShadeList();
	      if (list)
		{
		  shade = (Shade*)g_list_nth_data(list, presetShade);
		  if (shade)
		    dataFileSet_shade(visuData, shade);
		}
	    }
	}
    }
  /* Planes argument. */
  planesFile = commandLineGet_planesFileName();
  if (planesFile)
    {
      list = (GList*)0;
      error = (GError*)0;
      somethingIsLoaded = planesParse_XMLFile(planesFile, &list, &error);
      if (!somethingIsLoaded)
	{
	  g_warning(error->message);
	  g_clear_error(&error);
	}
      else
	{
	  planesComputeInter_list(visuDataGet_openGLView(visuData), list);
	  planesId = openGLObjectList_new(1);
	  plane = OpenGLExtension_new(_("Planes"), (char*)0,
				      planesId, NULL);
	  plane->used = 1;
	  OpenGLExtensionSet_priority(plane, OPENGL_EXTENSION_PRIORITY_NORMAL);
	  OpenGLExtensionRegister(plane);
	  /* Create the planes list. */
	  glNewList(planesId, GL_COMPILE);
	  glEnable(GL_BLEND);
	  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	  planesDraw_list(visuDataGet_openGLView(visuData), list);
	  glEndList();
	  /* Hide nodes if required. */
	  res = planeShowHide_all(visuData, list);
	  if (res)
	    visuDataEmit_nodeRenderedChange(visuData);
	}
    }

  visuData_createAllNodes(visuData);

  dumpToImage(visuData);

  return FALSE;
}

int dumpToImage(VisuData *data)
{
  DumpType *format;
  int res;
  GList *pnt;
  GString *buffer;
  char *exportFileName;

  exportFileName = (char*)commandLineGet_ExportFileName();
  if (!exportFileName)
    g_error("This method should be called with"
	    " an argument that is the file name to export to.\n");
  
  buffer = g_string_new(_("Dumping the view to image file"));

  pnt = getAllDumpModules();
  while (pnt && !fileFormatUse_match( ((DumpType*)pnt->data)->fileType,
				     exportFileName))
    pnt = g_list_next(pnt);
  if (!pnt)
    {
      g_warning("The format can't be found from the"
		" filename '%s' entered.\n", exportFileName);
      return FALSE;
    }
  format = (DumpType*)pnt->data;

  DBG_fprintf(stderr, "Visu Basic : Preparing to dump the image to '%s' as"
	      " format '%s'.\n", exportFileName, format->fileType->description);

  res = format->writeFunc(format->fileType, buffer, exportFileName, -1, -1,
			  data, (voidDataFunc)0, (gpointer)0);
  if (res)
    fprintf(stderr, "%s\n", buffer->str);
  g_string_free(buffer, TRUE);

  g_idle_add(quitVisu, (gpointer)gLoop);

  return res;
}

void loadFileWithoutGtk(VisuData *data)
{
  GString *buffer;
  int res;
  RenderingMethod *method;

  if (!data)
    return;

  DBG_fprintf(stderr, "Visu Main : Loading data.\n");

  res = loadDataFromFile(data, &buffer);

  if (!res)
    {
      g_object_unref(data);
      fprintf(stderr, "%s\n", buffer->str);
      exit(1);
    }

  if (buffer)
    g_string_free(buffer, TRUE);
}

void getGeneralPaths()
{
  gchar *path;
#if SYSTEM_WIN32 == 1
  #define V_SIM_INI_FILE "v_sim.ini"
  GIOChannel *iniFile;
  gchar *iniPath;
  gchar *buffer, *line;
  GIOStatus res;
  GError *err;
  gchar **tokens;
  gsize length;

  v_sim_data_dir = g_strdup(V_SIM_DATA_DIR_DEFAULT);
  v_sim_conf_dir = g_strdup(V_SIM_CONF_DIR_DEFAULT);
  v_sim_legal_dir = g_strdup(V_SIM_LEGAL_DIR_DEFAULT);
  v_sim_pixmaps_dir = g_strdup(V_SIM_PIXMAPS_DIR_DEFAULT);
  iniPath = g_strdup(V_SIM_INI_FILE);
  iniFile = g_io_channel_new_file(iniPath, "r", (GError**)0);
  if (!iniFile)
    {
      iniPath = g_build_filename("C:\\WINDOWS", V_SIM_INI_FILE, NULL);
      iniFile = g_io_channel_new_file(iniPath, "r", (GError**)0);
    }
  if (iniFile)
    {
      buffer = (gchar*)0;
      err = (GError*)0;
      do
	{
	  res = g_io_channel_read_line(iniFile, &line, &length, NULL, &err);
	  if (line && res == G_IO_STATUS_NORMAL)
	    {
	      tokens = g_strsplit(line, ":", 2);
	      if (!strcmp(tokens[0], "[DATA_DIR]") && tokens[1])
		{
		  g_free(v_sim_data_dir);
		  v_sim_data_dir = g_strdup(g_strstrip(tokens[1]));
		}
	      if (!strcmp(tokens[0], "[CONF_DIR]") && tokens[1])
		{
		  g_free(v_sim_conf_dir);
		  v_sim_conf_dir = g_strdup(g_strstrip(tokens[1]));
		}
	      if (!strcmp(tokens[0], "[LEGAL_DIR]") && tokens[1])
		{
		  g_free(v_sim_legal_dir);
		  v_sim_legal_dir = g_strdup(g_strstrip(tokens[1]));
		}
	      if (!strcmp(tokens[0], "[PIXMAPS_DIR]") && tokens[1])
		{
		  g_free(v_sim_pixmaps_dir);
		  v_sim_pixmaps_dir = g_strdup(g_strstrip(tokens[1]));
		}
	      g_strfreev(tokens);
	      g_free(line);
	    }
	}
      while (res != G_IO_STATUS_EOF);
      g_io_channel_shutdown (iniFile, FALSE, (GError**)0);
      g_io_channel_unref (iniFile);
    }
  g_free(iniPath);
#endif
#if SYSTEM_X11 == 1
  v_sim_data_dir = g_strdup(V_SIM_DATA_DIR);
  v_sim_conf_dir = g_strdup(V_SIM_CONF_DIR);
  v_sim_legal_dir = g_strdup(V_SIM_LEGAL_DIR);
  v_sim_pixmaps_dir = g_strdup(V_SIM_PIXMAPS_DIR);
#endif
  v_sim_local_conf_dir = g_build_filename(g_get_home_dir(), ".v_sim", NULL);
  if (!v_sim_local_conf_dir)
    g_warning("WARNING! Impossible to get the default path $HOME/.%s.\n", V_SIM_NAME);
  DBG_fprintf(stderr, "Visu Basic : data directory : '%s'.\n", v_sim_data_dir);
  DBG_fprintf(stderr, "Visu Basic : conf directory : '%s'.\n", v_sim_conf_dir);
  DBG_fprintf(stderr, "Visu Basic : local conf directory : '%s'.\n", v_sim_local_conf_dir);
  DBG_fprintf(stderr, "Visu Basic : legal directory : '%s'.\n", v_sim_legal_dir);
  DBG_fprintf(stderr, "Visu Basic : pixmaps directory : '%s'.\n", v_sim_pixmaps_dir);
}

int main (int argc, char *argv[])
{
  int res;
  char *arg;
  const gchar* enc;

#ifdef ENABLE_NLS
  setlocale (LC_ALL, "");
  bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);
#endif

  gLoop = (GMainLoop*)0;

  /* Creating the rendering window. */
  initOSinterface();

  enc = g_getenv("G_FILENAME_ENCODING");
  if (enc && strcmp(enc, "UTF8") && strcmp(enc, "UTF-8"))
    setFileSystemInUTF8(0);
  else
    setFileSystemInUTF8(1);
  if (g_getenv("G_BROKEN_FILENAMES"))
    setFileSystemInUTF8(0);

  DBG_fprintf(stderr, "--- Get the default path ---\n");
  getGeneralPaths();

  DBG_fprintf(stderr, "--- Parse the command line ---\n");
  res = parseCommandLine(argc, argv);
  if (res)
    exit(1);

  /* if arg is not null, v_sim is in export mode. */
  arg = commandLineGet_ExportFileName();
  if (arg)
    {
      DBG_fprintf(stderr, "Visu Basic : V_Sim has been called in an export session,"
		  " with parameter '%s'.\n", arg);
      res = mainExport();

      return res;
    }

  /* the default is V_Sim with gtk interface. */
  res = mainWithGtk(argc, argv);

  return res;
}
