/*  Grafx2 - The Ultimate 256-color bitmap paint program
    Copyright 2008 Peter Gordon
    Copyright 2008 Franck Charlet
    Copyright 2007 Adrien Destugues
    Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
    Grafx2 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; version 2
    of the License.
    Grafx2 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 Grafx2; if not, see 
*/
#define GLOBAL_VARIABLES
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
// There is no WM on the GP2X...
#ifndef __GP2X__
	#include 
#endif
#include "const.h"
#include "struct.h"
#include "global.h"
#include "graph.h"
#include "misc.h"
#include "init.h"
#include "buttons.h"
#include "engine.h"
#include "pages.h"
#include "loadsave.h"
#include "sdlscreen.h"
#include "errors.h"
#include "readini.h"
#include "saveini.h"
#include "io.h"
#include "text.h"
#include "setup.h"
#include "windows.h"
#include "brush.h"
#include "palette.h"
#include "realpath.h"
#if defined(__WIN32__)
    #include 
    #include 
    #define chdir(dir) SetCurrentDirectory(dir)
#elif defined(__macosx__)
    #import 
    #import 
#elif defined(__FreeBSD__)
    #import 
#endif
#if defined (__WIN32__)
  // On Windows, SDL_putenv is not present in any compilable header.
  // It can be linked anyway, this declaration only avoids
  // a compilation warning.
  extern DECLSPEC int SDLCALL SDL_putenv(const char *variable);
#endif
// filename for the current GUI skin file.
static char Gui_skin_file[MAX_PATH_CHARACTERS];
//--- Affichage de la syntaxe, et de la liste des modes vidéos disponibles ---
void Display_syntax(void)
{
  int mode_index;
  printf("Syntax: grafx2 [] []\n\n");
  printf(" can be:]\n");
  printf("\t/? /h /help       for this help screen\n");
  printf("\t/wide             to emulate a video mode with wide pixels (2x1)\n");
  printf("\t/tall             to emulate a video mode with tall pixels (1x2)\n");
  printf("\t/double           to emulate a video mode with double pixels (2x2)\n");
  printf("\t/wide2            to emulate a video mode with double wide pixels (4x2)\n");
  printf("\t/tall2            to emulate a video mode with double tall pixels (2x4)\n");
  printf("\t/triple           to emulate a video mode with triple pixels (3x3)\n");
  printf("\t/quadruple        to emulate a video mode with quadruple pixels (4x4)\n");
  printf("\t/rgb n            to reduce RGB precision from 256 to n levels\n");
  printf("\t/skin   to use an alternate file with the menu graphics\n");
  printf("\t/mode  to set a video mode\n\n");
  printf("Available video modes:\n\n");
  for (mode_index = 0; mode_index < Nb_video_modes; mode_index += 12)
  {
	int k;
	for (k = 0; k < 6; k++)
	{
		if (mode_index + k >= Nb_video_modes) break;
		printf("%12s",Mode_label(mode_index + k));
	}
	puts("");
  }
}
// ---------------------------- Sortie impromptue ----------------------------
void Error_function(int error_code, const char *filename, int line_number, const char *function_name)
{
  T_Palette temp_palette;
  int       index;
  printf("Error number %d occured in file %s, line %d, function %s.\n", error_code, filename,line_number,function_name);
  if (error_code==0)
  {
    // L'erreur 0 n'est pas une vraie erreur, elle fait seulement un flash rouge de l'écran pour dire qu'il y a un problème.
    // Toutes les autres erreurs déclenchent toujours une sortie en catastrophe du programme !
    memcpy(temp_palette,Main_palette,sizeof(T_Palette));
    for (index=0;index<=255;index++)
      temp_palette[index].R=255;
    Set_palette(temp_palette);
    SDL_Delay(500);
    Set_palette(Main_palette);
  }
  else
  {
    switch (error_code)
    {
      case ERROR_GUI_MISSING         : printf("Error: File containing the GUI graphics is missing!\n");
                                       printf("This program cannot run without this file.\n");
                                       break;
      case ERROR_GUI_CORRUPTED       : printf("Error: File containing the GUI graphics couldn't be parsed!\n");
                                       printf("This program cannot run without a correct version of this file.\n");
                                       break;
      case ERROR_INI_MISSING         : printf("Error: File gfx2def.ini is missing!\n");
                                       printf("This program cannot run without this file.\n");
                                       break;
      case ERROR_MEMORY              : printf("Error: Not enough memory!\n\n");
                                       printf("You should try exiting other programs to free some bytes for Grafx2.\n\n");
                                       break;
      case ERROR_FORBIDDEN_MODE      : printf("Error: The requested video mode has been disabled from the resolution menu!\n");
                                       printf("If you want to run the program in this mode, you'll have to start it with an\n");
                                       printf("enabled mode, then enter the resolution menu and enable the mode you want.\n");
                                       printf("Check also if the 'Default_video_mode' parameter in gfx2.ini is correct.\n");
                                       break;
      case ERROR_COMMAND_LINE     : printf("Error: Invalid parameter or file not found.\n\n");
                                       Display_syntax();
                                       break;
      case ERROR_SAVING_CFG     : printf("Error: Write error while saving settings!\n");
                                       printf("Settings have not been saved correctly, and the gfx2.cfg file may have been\n");
                                       printf("corrupt. If so, please delete it and Grafx2 will restore default settings.\n");
                                       break;
      case ERROR_MISSING_DIRECTORY : printf("Error: Directory you ran the program from not found!\n");
                                       break;
      case ERROR_INI_CORRUPTED       : printf("Error: File gfx2.ini is corrupt!\n");
                                       printf("It contains bad values at line %d.\n",Line_number_in_INI_file);
                                       printf("You can re-generate it by deleting the file and running GrafX2 again.\n");
                                       break;
      case ERROR_SAVING_INI     : printf("Error: Cannot rewrite file gfx2.ini!\n");
                                       break;
      case ERROR_SORRY_SORRY_SORRY  : printf("Error: Sorry! Sorry! Sorry! Please forgive me!\n");
                                       break;
    }
    SDL_Quit();
    exit(error_code);
  }
}
// --------------------- Analyse de la ligne de commande ---------------------
void Analyze_command_line(int argc,char * argv[])
{
  char *buffer ;
  int index;
  File_in_command_line=0;
  Resolution_in_command_line=0;
  
  Current_resolution=Config.Default_resolution;
  
  for (index=1; index 256)
        {
          Error(ERROR_COMMAND_LINE);
          Display_syntax();
          exit(0);
        }
        Set_palette_RGB_scale(scale);
      }
      else
      {
        Error(ERROR_COMMAND_LINE);
        Display_syntax();
        exit(0);
      }
    }
    else if ( !strcmp(argv[index],"/skin") )
    {
      // GUI skin file
      index++;
      if (indexpixels))[(y*32+x)] != 255)
            icon_mask[(y*32+x)/8] |=0x80>>(x&7);
      SDL_WM_SetIcon(icon,icon_mask);
      free(icon_mask);
      SDL_FreeSurface(icon);
    }
  }
  // Texte
  Init_text();
  // On initialise tous les modes vidéo
  Set_all_video_modes();
  Pixel_ratio=PIXEL_SIMPLE;
  // On initialise les données sur l'état du programme:
  // Donnée sur la sortie du programme:
  Quit_is_required=0;
  Quitting=0;
  // Données sur l'état du menu:
  Pixel_in_menu=Pixel_in_toolbar;
  Menu_is_visible=1;
  Menu_height=MENU_HEIGHT;
  // Données sur les couleurs et la palette:
  First_color_in_palette=0;
  // Données sur le curseur:
  Cursor_shape=CURSOR_SHAPE_TARGET;
  Cursor_hidden=0;
  // Données sur le pinceau:
  Paintbrush_X=0;
  Paintbrush_Y=0;
  Paintbrush_shape=PAINTBRUSH_SHAPE_ROUND;
  Paintbrush_hidden=0;
  Pixel_load_function=Pixel_load_in_current_screen;
  // On initialise tout ce qui concerne les opérations et les effets
  Operation_stack_size=0;
  Selected_freehand_mode=OPERATION_CONTINUOUS_DRAW;
  Selected_line_mode         =OPERATION_LINE;
  Selected_curve_mode        =OPERATION_3_POINTS_CURVE;
  Effect_function=No_effect;
    // On initialise les infos de la loupe:
  Main_magnifier_mode=0;
  Main_magnifier_factor=DEFAULT_ZOOM_FACTOR;
  Main_separator_proportion=INITIAL_SEPARATOR_PROPORTION;
  Spare_separator_proportion=INITIAL_SEPARATOR_PROPORTION;
    // On initialise les infos du mode smear:
  Smear_mode=0;
  Smear_brush_width=PAINTBRUSH_WIDTH;
  Smear_brush_height=PAINTBRUSH_HEIGHT;
    // On initialise les infos du mode smooth:
  Smooth_mode=0;
    // On initialise les infos du mode shade:
  Shade_mode=0;     // Les autres infos du Shade sont chargées avec la config
  Quick_shade_mode=0; // idem
    // On initialise les infos sur les dégradés:
  Gradient_pixel =Display_pixel; // Les autres infos sont chargées avec la config
    // On initialise les infos de la grille:
  Snap_mode=0;
  Snap_width=8;
  Snap_height=8;
  Snap_offset_X=0;
  Snap_offset_Y=0;
    // On initialise les infos du mode Colorize:
  Colorize_mode=0;          // Mode colorize inactif par défaut
  Colorize_opacity=50;      // Une interpolation de 50% par défaut
  Colorize_current_mode=0; // Par défaut, la méthode par interpolation
  Compute_colorize_table();
    // On initialise les infos du mode Tiling:
  Tiling_mode=0;  //   Pas besoin d'initialiser les décalages car ça se fait
                  // en prenant une brosse (toujours mis à 0).
    // On initialise les infos du mode Mask:
  Mask_mode=0;
    // Infos du Spray
  Airbrush_mode=1; // Mode Mono
  Airbrush_size=31;
  Airbrush_delay=1;
  Airbrush_mono_flow=10;
  memset(Airbrush_multi_flow,0,256);
  srand(time(NULL)); // On randomize un peu tout ça...
  // Initialisation des boutons
  Init_buttons();
  // Initialisation des opérations
  Init_operations();
  // Initialize the brush container
  Init_brush_container();
  Windows_open=0;
  
  // Charger la configuration des touches
  Set_config_defaults();
  switch(Load_CFG(1))
  {
    case ERROR_CFG_MISSING:
      // Pas un problème, on a les valeurs par défaut.
      break;
    case ERROR_CFG_CORRUPTED:
      DEBUG("Corrupted CFG file.",0);
      break;
    case ERROR_CFG_OLD:
      DEBUG("Unknown CFG file version, not loaded.",0);
      break;
  }
  // Charger la configuration du .INI
  temp=Load_INI(&Config);
  if (temp)
    Error(temp);
  Analyze_command_line(argc,argv);
  // Load sprites, palette etc.
  strcpy(Gui_skin_file,Config.Skin_file);
  Gfx = Load_graphics(Gui_skin_file);
  if (Gfx == NULL)
  {
    Gfx = Load_graphics("skin_modern.png");
    if (Gfx == NULL)
    {
      printf("%s", Gui_loading_error_message);
      Error(ERROR_GUI_MISSING);
    }
  }
  // Infos sur les trames (Sieve)
  Sieve_mode=0;
  Copy_preset_sieve(0);
  // Transfert des valeurs du .INI qui ne changent pas dans des variables
  // plus accessibles:
  Gfx->Default_palette[MC_Black]=Fav_menu_colors[0]=Config.Fav_menu_colors[0];
  Gfx->Default_palette[MC_Dark] =Fav_menu_colors[1]=Config.Fav_menu_colors[1];
  Gfx->Default_palette[MC_Light]=Fav_menu_colors[2]=Config.Fav_menu_colors[2];
  Gfx->Default_palette[MC_White]=Fav_menu_colors[3]=Config.Fav_menu_colors[3];
  Compute_optimal_menu_colors(Gfx->Default_palette);
  Fore_color=MC_White;
  Back_color=MC_Black;
  // Font
  if (!(Menu_font=Load_font(Config.Font_file)))
    if (!(Menu_font=Load_font("font_Classic.png")))
      {
        printf("Unable to open the default font file: %s\n", "font_Classic.png");
        Error(ERROR_GUI_MISSING);
      }
  memcpy(Main_palette, Gfx->Default_palette, sizeof(T_Palette));
  // Allocation de mémoire pour la brosse
  if (!(Brush         =(byte *)malloc(   1*   1))) Error(ERROR_MEMORY);
  if (!(Smear_brush   =(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE))) Error(ERROR_MEMORY);
  // Pinceau
  if (!(Paintbrush_sprite=(byte *)malloc(MAX_PAINTBRUSH_SIZE*MAX_PAINTBRUSH_SIZE))) Error(ERROR_MEMORY);
  *Paintbrush_sprite=1;
  Paintbrush_width=1;
  Paintbrush_height=1;
  starting_videomode=Current_resolution;
  Horizontal_line_buffer=NULL;
  Screen_width=Screen_height=Current_resolution=0;
  Init_mode_video(
    Video_mode[starting_videomode].Width,
    Video_mode[starting_videomode].Height,
    Video_mode[starting_videomode].Fullscreen,
    Pixel_ratio);
  // Windows only: move back the window to its original position.
  #if defined(__WIN32__)
  if (!Video_mode[starting_videomode].Fullscreen)
  {
    if (Config.Window_pos_x != 9999 && Config.Window_pos_y != 9999)
    {
      //RECT r;
      static SDL_SysWMinfo pInfo;
      SDL_VERSION(&pInfo.version);
      SDL_GetWMInfo(&pInfo);
      //GetWindowRect(pInfo.window, &r);
      SetWindowPos(pInfo.window, 0, Config.Window_pos_x, Config.Window_pos_y, 0, 0, SWP_NOSIZE);
    }
  }
  #endif
  
  Main_image_width=Screen_width/Pixel_width;
  Main_image_height=Screen_height/Pixel_height;
  Spare_image_width=Screen_width/Pixel_width;
  Spare_image_height=Screen_height/Pixel_height;
  // Allocation de mémoire pour les différents écrans virtuels (et brosse)
  if (Init_all_backup_lists(Config.Max_undo_pages+1,Screen_width,Screen_height)==0)
    Error(ERROR_MEMORY);
  // On remet le nom par défaut pour la page de brouillon car il été modifié
  // par le passage d'un fichier en paramètre lors du traitement précédent.
  // Note: le fait que l'on ne modifie que les variables globales 
  // Brouillon_* et pas les infos contenues dans la page de brouillon 
  // elle-même ne m'inspire pas confiance mais ça a l'air de marcher sans 
  // poser de problèmes, alors...
  if (File_in_command_line)
  {
    strcpy(Spare_file_directory,Spare_current_directory);
    strcpy(Spare_filename,"NO_NAME.GIF");
    Spare_fileformat=DEFAULT_FILEFORMAT;
  }
  // Nettoyage de l'écran virtuel (les autres recevront celui-ci par copie)
  memset(Main_screen,0,Main_image_width*Main_image_height);
  // Initialisation de diverses variables par calcul:
  Compute_magnifier_data();
  Compute_limits();
  Compute_paintbrush_coordinates();
  // On affiche le menu:
  Display_menu();
  Display_paintbrush_in_menu();
  Display_sprite_in_menu(BUTTON_PAL_LEFT,18+(Config.Palette_vertical!=0));
  // On affiche le curseur pour débutter correctement l'état du programme:
  Display_cursor();
  Spare_image_is_modified=0;
  Main_image_is_modified=0;
  // Gestionnaire de signaux, quand il ne reste plus aucun espoir
  Init_sighandler();
  // Le programme débute en mode de dessin à la main
  Select_button(BUTTON_DRAW,LEFT_SIDE);
  // On initialise la brosse initiale à 1 pixel blanc:
  Brush_width=1;
  Brush_height=1;
  Capture_brush(0,0,0,0,0);
  *Brush=MC_White;
  return(1);
}
// ------------------------- Fermeture du programme --------------------------
void Program_shutdown(void)
{
  int      return_code;
  // Windows only: Recover the window position.
  #if defined(__WIN32__)
  {
    RECT r;
    static SDL_SysWMinfo pInfo;
    
    SDL_GetWMInfo(&pInfo);
    GetWindowRect(pInfo.window, &r);
    Config.Window_pos_x = r.left;
    Config.Window_pos_y = r.top;
  }
  #else
  // All other targets: irrelevant dimensions.
  // Do not attempt to force them back on next program run.
    Config.Window_pos_x = 9999;
    Config.Window_pos_y = 9999;
  #endif
  // On libère le buffer de gestion de lignes
  if(Horizontal_line_buffer) free(Horizontal_line_buffer);
  // On libère le pinceau spécial
  if (Paintbrush_sprite) free(Paintbrush_sprite);
  // On libère les différents écrans virtuels et brosse:
  if(Brush) free(Brush);
  Set_number_of_backups(0);
  if(Spare_screen) free(Spare_screen);
  if(Main_screen) free(Main_screen);
  // Free the skin (Gui graphics) data
  if (Gfx)
  {
    free(Gfx);
    Gfx=NULL;
  }
  
  // On prend bien soin de passer dans le répertoire initial:
  if (chdir(Initial_directory)!=-1)
  {
    // On sauvegarde les données dans le .CFG et dans le .INI
    if (Config.Auto_save)
    {
      return_code=Save_CFG();
      if (return_code)
        Error(return_code);
      return_code=Save_INI(&Config);
      if (return_code)
        Error(return_code);
    }
  }
  else
    Error(ERROR_MISSING_DIRECTORY);
    
  SDL_Quit();
}
// -------------------------- Procédure principale ---------------------------
int main(int argc,char * argv[])
{
  int phoenix_found=0;
  int phoenix2_found=0;
  char phoenix_filename1[MAX_PATH_CHARACTERS];
  char phoenix_filename2[MAX_PATH_CHARACTERS];
  if(!Init_program(argc,argv))
  {
	Program_shutdown();
    return 0;
  }
  // Test de recuperation de fichiers sauvés
  strcpy(phoenix_filename1,Config_directory);
  strcat(phoenix_filename1,"phoenix.img");
  strcpy(phoenix_filename2,Config_directory);
  strcat(phoenix_filename2,"phoenix2.img");
  if (File_exists(phoenix_filename1))
    phoenix_found=1;
  if (File_exists(phoenix_filename2))
    phoenix2_found=1;
  if (phoenix_found || phoenix2_found)
  {
    if (phoenix2_found)
    {
      strcpy(Main_file_directory,Config_directory);
      strcpy(Main_filename,"phoenix2.img");
      chdir(Main_file_directory);
      Button_Reload();
      Main_image_is_modified=1;
      Warning_message("Spare page recovered");
      // I don't really like this, but...
      remove(phoenix_filename2);
      Button_Page();
    }
    if (phoenix_found)
    {
      strcpy(Main_file_directory,Config_directory);
      strcpy(Main_filename,"phoenix.img");
      chdir(Main_file_directory);
      Button_Reload();
      Main_image_is_modified=1;
      Warning_message("Main page recovered");
      // I don't really like this, but...
      remove(phoenix_filename1);
    }
  }
  else
  {
    if (Config.Opening_message && (!File_in_command_line))
      Button_Message_initial();
  
    if (File_in_command_line)
    {
      Button_Reload();
      Resolution_in_command_line=0;
    }
  }
  Main_handler();
  Program_shutdown();
  return 0;
}