/* ReSCAL - Main entry
 *
 * Copyright (C) 2011-2012
 *
 * Author: Olivier Rozier <rozier@ipgp.fr>
 *
 * This file is part of ReSCAL.
 *
 * ReSCAL 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */



#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#define _MAIN_
#include <locale.h>
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include "defs.h"
#include "macros.h"
#include "entry.h"
#include "param.h"
#include "cells.h"
#include "simul.h"
#include "interface.h"
#include "callbacks.h"
//#include "monitor.h"
#include "view.h"
#include "rescal.h"
#include "space.h"
#include "format.h"
#ifdef PARALLEL
#include "messages.h"
#endif


extern int graine;            // graine pour la generation des nombres aleatoires
extern float dump_delay_png;
extern float dump_delay_csp;
extern unsigned char csphpp_flag;
extern float stop_delay_t0;
#ifdef PARALLEL
extern int mode_par;    //mode parallele
extern int proc_id;
#endif

int prog=PROG_RESCAL;
int arch=0;
gint area_w=0, area_h=0;
float frame_delay=0;
unsigned char opt_nv=0, opt_info=0,  opt_dcsp=0, opt_dbin=0, opt_stop=0, opt_quit=0;
gint dcsp_delay, dbin_delay, stop_delay;

unsigned char opt_dpng=0, opt_djpeg=0;
gint dpng_delay=0, djpeg_delay=0;

int main (int argc, char *argv[])
{
  DumpPar dp_info, dp_csp, dp_bin, dp_png, dp_jpeg;

#ifdef PARALLEL
  if (argc>1) msg_init();
#endif

#ifdef LOG_FILE
  char log_filename[100];
  sprintf(log_filename,"RESCAL.log");
#ifdef PARALLEL
  if (proc_id>=0){
    sprintf(log_filename,"RESCAL%d.log",proc_id);
  }
#endif
  if (argc>1) log_file = fopen(log_filename,"w");
#endif

  LogPrintf("ReSCAL\n");
  LogPrintf("version %s (%s)\n",VER_NUM,VER_DAT);
#ifdef VER_BRA
  LogPrintf("branch %s\n",VER_BRA);
#endif
  gtk_set_locale();
#ifdef NUM_MODE
  LogPrintf("format of numerical values: %s \n", NUM_MODE);
  setlocale(LC_NUMERIC, NUM_MODE);
#endif
  if (sizeof(long)==4) arch = W32;
  if (sizeof(long)==8) arch = W64;
  LogPrintf("size of long: %ld bytes (%s)\n", sizeof(long), (arch==W32)?"32b":"64b");
  LogPrintf("size of cell: %lu byte(s)\n", sizeof(Cell));
#ifdef USE_GD
  LogPrintf("use of libgd\n");
#endif
#ifdef LGCA
  LogPrintf("lattice gas enabled\n");
#endif
#ifdef CYCLAGE_HOR
  LogPrintf("pbc_mode supported\n");
#endif
#ifdef PARALLEL
  LogPrintf("parallel execution\n");
#endif
  LogPrintf("%s model\n",MOD_NAME);

  if ((argc == 1) || (*argv[1]=='-')){
    rescal_usage();
    show_general_options();
    show_view_options();
    exit(-4);
  }

  general_options(argc, argv);

  if (!opt_nv){
    g_thread_init(NULL);
    gdk_threads_init();
    gtk_init (&argc, &argv);
  }
  /*else gtk_init_check (&argc, &argv);*/

  callbacks_init();
  rescal_init(argv[1]);
  view_init(argc, argv, &area_w, &area_h);

  //if (opt_nv) rescal(0);

#ifdef GUI
  if (!opt_nv) {
    create_window();
    gcallback_flash(0);
  }
#endif

  if (opt_info) {
    log_info();
    dp_info = (DumpPar){DUMP_LOG, 1};
    g_timeout_add_seconds(60, gcallback_dump, &dp_info); //toutes les minutes
    //g_timeout_add_seconds(1, callback_dump, &dp_info); //toutes les secondes
  }

  if (opt_dcsp) {
    //dump_terre(DUMP_CSP, dcsp_delay);
    dp_csp = (DumpPar){DUMP_CSP, dcsp_delay};
    gcallback_dump(&dp_csp);
    g_timeout_add_seconds(dcsp_delay*60, gcallback_dump, &dp_csp);
  }

  if (opt_dbin) {
    //dump_terre(DUMP_BIN, dbin_delay);
    dp_bin = (DumpPar){DUMP_BIN, dbin_delay};
    gcallback_dump(&dp_bin);
    g_timeout_add_seconds(dbin_delay*60, gcallback_dump, &dp_bin);
  }

#if defined(USE_LIBPNG) || defined(USE_GD)
  view_dump_init();
#endif

  if (opt_dpng) {
    dump_image_inter(dpng_delay, "png");
    dp_png = (DumpPar){DUMP_PNG, dpng_delay};
    g_timeout_add_seconds(dpng_delay, gcallback_dump, &dp_png);
  }
  if (opt_djpeg) {
    dump_image_inter(djpeg_delay, "jpeg");
    dp_jpeg = (DumpPar){DUMP_JPEG, djpeg_delay};
    g_timeout_add_seconds(djpeg_delay, gcallback_dump, &dp_jpeg);
  }

  if (opt_stop) {
    g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, stop_delay*60+15, gcallback_stop, 0, NULL);
    //gtk_timeout_add(stop_delay*60000+15000, callback_stop, 0);
  }
  if (opt_quit){
    g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1, gcallback_quit, 0, NULL);
  }

  int nRetVal;
  pthread_t pth;
  nRetVal = pthread_create( &pth, 0, rescal_thread, 0);

  /*
  GThread *pth;
  GError *err = NULL;
  g_thread_init(NULL);
  pth = g_thread_create((GThreadFunc)rescal_thread, 0, FALSE, &err);
  */

  //gtk_idle_add(idle, NULL);

  gtk_main ();

#ifdef LOG_FILE
  fclose(log_file);
#endif

  return 0;
}

void read_delay(char *str, DumpDelay *pd)
{
  int n;
  if (strstr(str, "t0")){
    pd->unit = UNIT_T0;
    if (sscanf(str, "%ft0", &pd->val)!=1) {ErrPrintf("ERROR: invalid delay value %s\n",str); exit(-1);}
  }
  else{
    pd->unit = UNIT_COMP;
    if (sscanf(str, "%d", &n)!=1) {ErrPrintf("ERROR: invalid delay value %s\n",str); exit(-1);}
    pd->val = n;
  }
  //LogPrintf("delay: %f (%d)\n", pd->val, pd->unit);
}

void general_options(int argc, char *argv[])
{
  int i;
  DumpDelay dd;

  //log the command line options
  LogPrintf("args: ");
  for(i=1; i<argc; i++) LogPrintf("%s ", argv[i]);
  LogPrintf("\n");

  //parse the command line options
  for(i=1; i<argc; i++){
    if (!strcmp(argv[i],"-nv"))
      opt_nv = 1;
    else if (!strcmp(argv[i],"-fr")){
      int frame_rate = atoi(argv[++i]);
      frame_delay = (frame_rate<100) ? 1000/frame_rate : 10;
    }
    else if (!strcmp(argv[i],"-info")){
      opt_info = 1;
#ifdef PARALLEL
      if (proc_id > 0) opt_info = 0;
#endif
    }
    else if (!strcmp(argv[i],"-dcsp") || !strcmp(argv[i],"-dcsphpp")){
      if (!strcmp(argv[i],"-dcsphpp")) csphpp_flag = 1;
      //opt_dcsp = 1;
      //dcsp_delay = atoi(argv[++i]);
      read_delay(argv[++i], &dd);
      if (dd.unit==UNIT_COMP){
        opt_dcsp = 1;
        dcsp_delay = dd.val;
      }
      else if (dd.unit==UNIT_T0){
        dump_delay_csp = dd.val;
      }
    }
    else if (!strcmp(argv[i],"-dbin")){
      opt_dbin = 1;
      dbin_delay = atoi(argv[++i]);
    }
    else if (!strcmp(argv[i],"-dpng")){

      //dpng_delay = atoi(argv[++i]);
      read_delay(argv[++i], &dd);
      if (dd.unit==UNIT_COMP){
        opt_dpng = 1;
        dpng_delay = dd.val;
      }
      else if (dd.unit==UNIT_T0){
        dump_delay_png = dd.val;
      }
    }
#ifndef USE_LIBPNG
    else if (!strcmp(argv[i],"-djpeg")){
      opt_djpeg = 1;
      djpeg_delay = atoi(argv[++i]);
      if (!djpeg_delay) djpeg_delay = 1;
    }
#endif
    else if (!strcmp(argv[i],"-stop")){
      read_delay(argv[++i], &dd);
      if (dd.unit==UNIT_COMP){
        opt_stop = 1;
        stop_delay = dd.val; //atoi(argv[++i]);
      }
      else if (dd.unit==UNIT_T0){
        stop_delay_t0 = dd.val + 0.5;
      }
      LogPrintf("stop delay: %s\n", argv[i]);
    }
    else if (!strcmp(argv[i],"-q")){
      opt_quit = 1;
    }
    else if (!strcmp(argv[i],"-g")){
      graine = atoi(argv[++i]);
    }
#ifdef PARALLEL
    else if (!strcmp(argv[i],"-par")){
      mode_par = 1;
    }
#endif
  }
  if (opt_dcsp && opt_dbin){
    ErrPrintf("ERROR: -dcsp and -dbin options are mutually exclusive\n");
    exit(-1);
  }
#ifndef GUI
  opt_nv = 1;
#endif
}

void show_general_options()
{
  printf("\nGENERAL OPTIONS\n");
#ifdef GUI
  printf("  -nv \t\t no window mode\n");
#endif
  printf("  -info \t monitoring data every minutes\n");
  printf("  -dcsp <n> \t generation of a CSP file every <n> minutes\n");
  printf("  -dcsp <f>t0 \t generation of a CSP file with delay in t0 unit (float value)\n");
  printf("  -dcsphpp <n> \t generation of CSP and HPP files every <n> minutes\n");
  printf("  -dcsphpp <f>t0 \t generation of CSP and HPP files with delay in t0 unit (float value)\n");
  printf("  -dbin <n> \t generation of a binary file every <n> minutes\n");
  printf("  -dpng <n> \t generation of a PNG image every <n> seconds\n");
  printf("  -dpng <f>t0\t generation of PNG images with delay in t0 unit (float value)\n");
#ifndef USE_LIBPNG
  printf("  -djpeg <n> \t generation of a Jpeg image every <n> seconds\n");
#endif
  printf("  -stop <n> \t quit after <n> minutes\n");
  printf("  -stop <f>t0 \t quit when simulated time reaches <f> t0\n");
  printf("  -q \t\t quit at the end of simulation\n");
#ifdef GUI
  printf("  -fr <n> \t frame rate (fps)\n");
#endif
  printf("  -g <n> \t random seed\n");
//#ifdef MODELE_DUN
//  printf("  -ro <n> : rotation d'angle aleatoire toutes les <n> minutes\n");
//#endif
#ifdef PARALLEL
  printf("  -par \t\t parallel mode enabled\n");
  //printf("  -pars : mode parallele, process serveur\n");
  //printf("  -parc <i> : mode parallele, process client numero <i>\n");
#endif
  fflush(stdout);
}

#ifdef GTK_OLD_SYNTAX
void g_timeout_add_seconds(int nsec, GtkFunction gcallback_dump, gpointer data)
{
  gtk_timeout_add(nsec*1000, gcallback_dump, data);
}

void g_timeout_add_seconds_full(int prior, int nsec, GtkFunction gcallback_dump, gpointer data, void *notify)
{
  gtk_timeout_add(nsec*1000, gcallback_dump, data);
}
#endif
