/*****************************************************************************

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

*****************************************************************************/



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#define	WIN32_LEAN_AND_MEAN
#define	NOMINMAX

#include	<cstdio>
#include	<ctime>
#include	<cstdlib>
#include	<cassert>

#include	<direct.h>
#include	<io.h>
#include <windows.h>
#include <windowsx.h>

#include	"archi.h"
#include	"base.h"
#include	"ConfigFile.h"
#include	"ConfigKey.h"
#include	"ConfigKeyValue.h"
#include	"file.h"
#include	"file_ct.h"
#include	"fnames.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"keyboard.h"
#include	"List.h"
#include	"log.h"
#include	"player.h"
#include	"String.h"



/*\\\ CONSTANTES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ TYPES & STRUCTURES PRIVES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PROTOTYPES PRIVES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* C'est le caractere de separation de chemin */
const char	FILE_path_separator_0 [] = "\\";

/* Noms de fichiers complets (path, name, ext) */
/* Le nom du fichier (name + ext) peut etre vide selon l'humeur. */
String	FILE_module_filename;
String	FILE_sample_filename;
String	FILE_instrument_filename;
String	FILE_prefs_filename;
String	FILE_auto_module_filename;
String	FILE_misc_filename;

/* Noms de chemin (path) */
String	FILE_program_pathname;
String	FILE_auto_sample_pathname;

/* Masques avec jockers (names, ext) */
String	FILE_module_maskname;
String	FILE_song_maskname;
String	FILE_sample_maskname;
String	FILE_instrument_maskname;
String	FILE_prefs_maskname;
String	FILE_auto_sample_maskname;



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Liste de fichier dans un repertoire */
struct ::_wfinddata64_t	FILE_disk_buffer;	/* Buffer pour la liste d'une directory */
intptr_t	FILE_directory_handle = -1;	/* Handle utilise lors de la liste d'une directory (-1 = aucun actuellement) */

::OSVERSIONINFOW	FTLE_windows_version;	/* Informations sur la version de Windows */
::HINSTANCE	FILE_kernel32_dll_handle;	/* Handle de KERNEL32.DLL */

/* Adresse de la fonction de recherche d'espace disque pour Win NT ou OSR 2 */

typedef	WINBASEAPI BOOL (WINAPI *FILE_GetDiskFreeSpaceExPtrType) (
    ::LPCWSTR lpDirectoryName,
    ::PULARGE_INTEGER lpFreeBytesAvailableToCaller,
    ::PULARGE_INTEGER lpTotalNumberOfBytes,
    ::PULARGE_INTEGER lpTotalNumberOfFreeBytes
);
FILE_GetDiskFreeSpaceExPtrType	FILE_GetDiskFreeSpaceEx_ptr = NULL;



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



FILE *	FILE_fopen (const char *filename_0, const char *mode_0)
{
#if defined (_MSC_VER)
	const std::wstring   filename = BASE_conv_utf8_to_utf16 (filename_0);
	const std::wstring   mode     = BASE_conv_utf8_to_utf16 (mode_0);
	return _wfopen (filename.c_str (), mode.c_str ());

#else
	return fopen (filename_0, mode_0);

#endif
}



int	FILE_fseek (FILE *f_ptr, int64_t offset, int origin)
{
#if defined (_MSC_VER)
	return _fseeki64 (f_ptr, offset, origin);

#else
	return fseek (f_ptr, long (offset), origin);

#endif
}



int64_t	FILE_ftell (FILE *f_ptr)
{
#if defined (_MSC_VER)
	return _ftelli64 (f_ptr);

#else
	return ftell (f_ptr);

#endif
}



/*==========================================================================*/
/*      Nom: FILE_read_string_in_file                                       */
/*      Description: Lit une chaine entouree de guillemets "" dans un       */
/*                   fichier, et precedee de sa longueur maximum.           */
/*      Parametres en entree:                                               */
/*        - buffer_0: buffer servant de reception a la chaine (0 final).    */
/*                    L'espace doit etre prevu suffisemment grand.          */
/*        - file_ptr: pointeur sur les informations du fichier.             */
/*      Retour: la longueur maximum de la chaine.                           */
/*==========================================================================*/

int	FILE_read_string_in_file (char *buffer_0, FILE *file_ptr)
{
	assert (buffer_0 != 0);
	assert (file_ptr != 0);

	int				string_len = 0;
	const int		ret_val = fscanf (file_ptr, "%d ", &string_len);
	if (ret_val != 1)
	{
		LOG_printf ("FILE_read_string_in_file: Cannot read string len.");
		assert (false);
	}

	int				char_cpt = -1;
	while (char_cpt < string_len)
	{
		char				c = fgetc (file_ptr);
		if (char_cpt == -1)
		{
			if (c == '\"')
			{
				char_cpt = 0;
			}
		}
		else
		{
			buffer_0 [char_cpt] = c;
			char_cpt ++;
		}
	}
	fgetc (file_ptr);	/* Saute la deuxieme apostrophe */
	buffer_0 [char_cpt] = '\0';

	return (string_len);
}



/****************************************************************************/
/*                                                                          */
/*      INITIALISATIONS                                                     */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: FILE_get_program_path                                          */
/*      Description: Recupere le chemin du programme. Cette fonction doit   */
/*                   etre appelee avant toute fonction de sortie du fichier */
/*                   de log.                                                */
/*      Retour: 0 si tout va bien.                                          */
/*==========================================================================*/

signed int FILE_get_program_path (void)
{
   std::vector <wchar_t>   tmp (MAX_PATH+1);
   bool           cont_flag = true;
   do
   {
		const long		len =
			::GetModuleFileNameW (0, &tmp [0], ::DWORD (tmp.size ()));
		const unsigned long	err_code = ::GetLastError ();
      if (   len >= long (tmp.size ())
		    && err_code == ERROR_INSUFFICIENT_BUFFER)
      {
         tmp.resize (len);
      }
      else
      {
         cont_flag = false;
         if (len != 0)
			{
				tmp [len] = 0;
			}
			else
         {
				LOG_printf ("FILE_init_paths: Error: couldn't retrieve the current application path.\n");
				return (-1);
         }
      }
   }
   while (cont_flag);

	FILE_program_pathname = BASE_conv_utf16_to_utf8 (&tmp [0]).c_str ();
	FILE_program_pathname = FNAM_get_full_path (FILE_program_pathname.c_str ());

	strcpy (LOG_pathname_0, (FILE_program_pathname + LOG_FILENAME_0).c_str ());

	return (0);
}



/*==========================================================================*/
/*      Nom: FILE_init                                                      */
/*      Description: Initialise les differents chemins.                     */
/*      Retour: 0 si tout va bien.                                          */
/*==========================================================================*/

signed int FILE_init (void)
{
/*______________________________________________
 *
 * Noms de fichiers (complets)
 *______________________________________________
 */

	/* Module */
	FILE_generate_new_module_name ();

	/* Sample */
	FILE_sample_filename = FILE_program_pathname + "NoName00.wav";

	/* Instruments */
	FILE_instrument_filename = FILE_program_pathname + "NoName00.ins";

	/* Prefs */
	FILE_prefs_filename = FILE_program_pathname + "pref.inf";

	/* Autoloading module */
	FILE_auto_module_filename = FILE_program_pathname;

	/* Miscellaneous */
	FILE_misc_filename = FILE_program_pathname;

/*______________________________________________
 *
 * Noms de chemins
 *______________________________________________
 */

	/* Autoloading sample */
	FILE_auto_sample_pathname = FILE_program_pathname;

/*______________________________________________
 *
 * Masques
 *______________________________________________
 */

	FILE_module_maskname = "*.GT2";
	FILE_song_maskname = "*.SNG";
	FILE_sample_maskname = "*.WAV";
	FILE_instrument_maskname = "*.INS";
	FILE_prefs_maskname = "*.INF";
	FILE_auto_sample_maskname = "*.WAV";

/*______________________________________________
 *
 * Recuperation de la fonction de recherche de
 * l'espace libre sur le disque
 *______________________________________________
 */

	/* Quelle est la version de Vindoze sur laquelle on travaille ? */
	FTLE_windows_version.dwOSVersionInfoSize = sizeof (FTLE_windows_version);
	if (! GetVersionExW (&FTLE_windows_version))
	{
		LOG_printf ("FILE_init: Error: couldn't get Windows version.\n");
		return (-1);
	}

	/* Chargement de la fonction de recherche de l'espace disque restant */
	if (   FTLE_windows_version.dwPlatformId == VER_PLATFORM_WIN32_NT
	    || (   FTLE_windows_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
	        && (FTLE_windows_version.dwBuildNumber & 0xFFFF) > 1000))
	{
		FILE_kernel32_dll_handle = LoadLibraryA ("KERNEL32.DLL");
		if (FILE_kernel32_dll_handle == NULL)
		{
			LOG_printf ("FILE_init: Warning: couldn't load KERNEL32.DLL.\n");
		}

		FILE_GetDiskFreeSpaceEx_ptr =
			(FILE_GetDiskFreeSpaceExPtrType) ::GetProcAddress (FILE_kernel32_dll_handle, "GetDiskFreeSpaceExW");
		if (FILE_GetDiskFreeSpaceEx_ptr == NULL)
		{
			LOG_printf ("FILE_init: Warning: couldn't get GetDiskFreeSpaceExW routine address.\n");
		}
	}

	return (0);
}



void	FILE_restore (void)
{
}



/*==========================================================================*/
/*      Nom: FILE_generate_new_module_name                                  */
/*      Description: Genere un nom de module. Fonction utilisee lors de     */
/*                   la creation d'un nouveau module "from scratch".        */
/*==========================================================================*/

void	FILE_generate_new_module_name (void)
{
	static int	mod_count = 0;
	char		filename_0 [31+1];

	sprintf (filename_0, "NoName%02d.gt2", mod_count);
	FILE_module_filename = FILE_program_pathname + filename_0;
	mod_count ++;
}



/*==========================================================================*/
/*      Nom: FILE_load_init_prefs                                           */
/*      Description: Charge le fichier des preferences                      */
/*      Retour: 0 si aucune erreur.                                         */
/*==========================================================================*/

signed int	FILE_load_init_prefs (void)
{
	signed int	ret_val;

	/* Si le fichier de prefs n'existe pas, on se barre. */
	if (! FILE_file_exist (FILE_prefs_filename.c_str ()))
	{
		return (0);
	}

	LOG_printf ("Loading prefs:\n");

	ConfigFile	cfg_file (FILE_prefs_filename.c_str ());
	std::list <ConfigKey>	key_list;
	if (cfg_file.read (key_list))
	{
		LOG_printf ("FILE_load_init_prefs: Error: couldn't load preference file.\n");
	}

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();
		const String	key_str = key_val.get_string ();

		/* Cle anonyme: interdit */
		if (BASE_compare_string (key_name.c_str (), "") == 0)
		{
			LOG_printf ("INTR_read_display_conf: Warning: Unexpected unassigned value.\n");
		}

		/* Keyboard */
		else if (BASE_compare_string (key_name.c_str (), "keyboard") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"keyboard\") argument must be a list.\n");
			}
			else
			{
				ret_val = KEYB_read_config_list (key_val.get_list ());
			}
		}

		/* Display */
		else if (BASE_compare_string (key_name.c_str (), "display") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"display\") argument must be a list.\n");
			}
			else
			{
				ret_val = INTR_read_display_conf (key_val.get_list ());
			}
		}

		/* Module editing */
		else if (BASE_compare_string (key_name.c_str (), "module_editing") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"module_editing\") argument must be a list.\n");
			}
			else
			{
				ret_val = GTK_read_module_editing_conf (key_val.get_list ());
			}
		}

		/* Player */
		else if (BASE_compare_string (key_name.c_str (), "player") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"player\") argument must be a list.\n");
			}
			else
			{
				Player &			player = Player::use_instance ();
				ret_val = player.read_conf (key_val.get_list ());
			}
		}

		/* Cle inconnue */
		else
		{
			LOG_printf ("FILE_load_init_prefs: Warning: syntax error \"%s\".\n",
			            key_name.c_str ());
		}

		if (ret_val)
		{
			break;
		}
	}

	if (ret_val)
	{
		return (-1);
	}

	LOG_printf ("\tOK.\n");

	return (0);
}



/*==========================================================================*/
/*      Nom: FILE_autoloading                                               */
/*      Description: Autochargement des samples, modules, etc...            */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILE_autoloading (void)
{
/*
	LOG_printf ("Autoloading:\n");
*/

	/*** A finir ***/

/*
	LOG_printf ("\tOK.\n");
*/

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*      DISK                                                                */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: FILE_drive_connected                                           */
/*      Description: Donne l'activite d'un lecteur                          */
/*      Parametres en entree:                                               */
/*        - drive: numero du drive interroge (A=0, B=1, ...)                */
/*      Retour: true si le lecteur est actif, false sinon.                  */
/*==========================================================================*/

bool	FILE_drive_connected (int drive)
{
	assert (drive >= 0);

	const LWORD		drive_map = ::GetLogicalDrives ();
	const bool		drive_flag = ((drive_map & (1 << drive)) != 0);

	return (drive_flag);
}



/*==========================================================================*/
/*      Nom: FILE_get_current_drive                                         */
/*      Description: Renvoie le numero du lecteur courant: A=0, B=1,...     */
/*      Retour: Numero du lecteur.                                          */
/*==========================================================================*/

int	FILE_get_current_drive (void)
{
	return (_getdrive () - 1);
}



/*==========================================================================*/
/*      Nom: FILE_get_first_file                                            */
/*      Description: Recopie le bloc d'informations du premier fichier ou   */
/*                   dossier d'un repertoire.                               */
/*      Parametres en entree:                                               */
/*        - path_0: pointe sur le chemin complet du repertoire, suivi du    */
/*                  filtre de noms. Se termine par 0.                       */
/*      Parametres en sortie:                                               */
/*        - ptr_dir_entry: pointeur sur la structure d'informations du      */
/*                         fichier.                                         */
/*        - filename_0: pointeur sur un espace reserve pour mettre le nom   */
/*                      du fichier. La place prevue doit etre suffisante.   */
/*      Retour: true si un fichier a ete trouve, false sinon ou si erreur.  */
/*==========================================================================*/

bool	FILE_get_first_file (const char *path_0, FILE_DIR_ENTRY *ptr_dir_entry, char *filename_0)
{
	assert (path_0 != 0);
	assert (ptr_dir_entry != 0);
	assert (filename_0 != 0);

	const std::wstring   path = BASE_conv_utf8_to_utf16 (path_0);

	if (FILE_directory_handle != -1)
	{
		_findclose (FILE_directory_handle);
		FILE_directory_handle = -1;
	}

	FILE_directory_handle = _wfindfirst64 (path.c_str (), &FILE_disk_buffer);
	if (FILE_directory_handle == -1)
	{
		return (false);
	}

	std::string    name = BASE_conv_utf16_to_utf8 (FILE_disk_buffer.name);
	strcpy (filename_0, name.c_str ());
	ptr_dir_entry->name_0 = filename_0;
	ptr_dir_entry->attrib = FILE_disk_buffer.attrib;
	ptr_dir_entry->length = FILE_disk_buffer.size;
	ptr_dir_entry->selected = false;

	struct tm *		tm_ptr = _localtime64 (&FILE_disk_buffer.time_write);
	if (tm_ptr != NULL)
	{
		ptr_dir_entry->minute = (BYTE) tm_ptr->tm_min;
		ptr_dir_entry->hour = (BYTE) tm_ptr->tm_hour;
		ptr_dir_entry->day = (BYTE) tm_ptr->tm_mday;
		ptr_dir_entry->month = (BYTE) tm_ptr->tm_mon + 1;
		ptr_dir_entry->year = (WORD) (tm_ptr->tm_year + 1900);
	}
	else
	{
		ptr_dir_entry->minute = (BYTE) 0;
		ptr_dir_entry->hour = (BYTE) 0;
		ptr_dir_entry->day = (BYTE) 0;
		ptr_dir_entry->month = (BYTE) 0;
		ptr_dir_entry->year = (WORD) 0;
	}

	return (true);
}



/*==========================================================================*/
/*      Nom: FILE_get_next_file                                             */
/*      Description: Recopie le bloc d'informations du prochain fichier ou  */
/*                   dossier d'un repertoire.                               */
/*      Parametres en sortie:                                               */
/*        - ptr_dir_entry: pointeur sur la structure d'informations du      */
/*                         fichier.                                         */
/*        - filename_0: pointeur sur un espace reserve pour mettre le nom   */
/*                      du fichier. La place prevue doit etre suffisante.   */
/*      Retour: true si un fichier a ete trouve, false sinon ou si erreur.  */
/*==========================================================================*/

bool	FILE_get_next_file (FILE_DIR_ENTRY *ptr_dir_entry, char *filename_0)
{
	assert (ptr_dir_entry != 0);
	assert (filename_0 != 0);

	int				result = _wfindnext64 (FILE_directory_handle, &FILE_disk_buffer);
	if (result != 0)
	{
		_findclose (FILE_directory_handle);
		FILE_directory_handle = -1;
		return (false);
	}

	std::string    name = BASE_conv_utf16_to_utf8 (FILE_disk_buffer.name);
	strcpy (filename_0, name.c_str ());
	ptr_dir_entry->name_0   = filename_0;
	ptr_dir_entry->attrib   = FILE_disk_buffer.attrib;
	ptr_dir_entry->length   = FILE_disk_buffer.size;
	ptr_dir_entry->selected = false;

	struct tm *		tm_ptr  = _localtime64 (&FILE_disk_buffer.time_write);
	if (tm_ptr != NULL)
	{
		ptr_dir_entry->minute = (BYTE) tm_ptr->tm_min;
		ptr_dir_entry->hour   = (BYTE) tm_ptr->tm_hour;
		ptr_dir_entry->day    = (BYTE) tm_ptr->tm_mday;
		ptr_dir_entry->month  = (BYTE) tm_ptr->tm_mon + 1;
		ptr_dir_entry->year   = (WORD) (tm_ptr->tm_year + 1900);
	}
	else
	{
		ptr_dir_entry->minute = (BYTE) 0;
		ptr_dir_entry->hour   = (BYTE) 0;
		ptr_dir_entry->day    = (BYTE) 0;
		ptr_dir_entry->month  = (BYTE) 0;
		ptr_dir_entry->year   = (WORD) 0;
	}

	return (true);
}



/*==========================================================================*/
/*      Nom: FILE_get_free_disk_space                                       */
/*      Description: Donne la memoire libre sur le disque.                  */
/*      Parametres en entree:                                               */
/*        - drive: numero du lecteur a inspecter (0-...)                    */
/*      Parametres en sortie:                                               */
/*        - free: pointeur sur la memoire libre du disque                   */
/*        - total: pointeur sur la memoire totale du disque                 */
/*      Retour: 0 si tout s'est bien passe                                  */
/*==========================================================================*/

signed int	FILE_get_free_disk_space (int drive, int64_t *free, int64_t *total)
{
	assert (drive >= 0);
	assert (free != 0);
	assert (total != 0);

	const int      dir_maxlen = 99+1;
	wchar_t        dir_0 [dir_maxlen];
	::ULWORD       cluster_size;
	::DWORD        sector_per_cluster;
	::DWORD        bytes_per_sector;
	::DWORD        avail_clusters;
	::DWORD        total_clusters;
	::ULARGE_INTEGER  bytes_free;
	::ULARGE_INTEGER  bytes_total;

	*free = 0;
	*total = 1;

	const char     drive_c (drive + 'A');
	swprintf (dir_0, dir_maxlen, L"%c:\\", wchar_t (drive_c));

	/* NT, Win95 OSR2, Win98 et sup */
	if (FILE_GetDiskFreeSpaceEx_ptr != NULL)
	{
		if (FILE_GetDiskFreeSpaceEx_ptr (dir_0, &bytes_free, &bytes_total, NULL) == 0)
		{
			LOG_printf ("FILE_get_free_disk_space: Error: couldn't get free disk space on drive %c: using GetDiskFreeSpaceEx routine.\n", drive_c);
			return (-1);
		}

		*free  = (int64_t (bytes_free.HighPart ) << 32) + bytes_free.LowPart;
		*total = (int64_t (bytes_total.HighPart) << 32) + bytes_total.LowPart;
	}

	/* Win95 de base */
	else
	{
		if (::GetDiskFreeSpaceW (
			dir_0,
			&sector_per_cluster,
			&bytes_per_sector,
			&avail_clusters,
			&total_clusters
		) == 0)
		{
			LOG_printf ("FILE_get_free_disk_space: Error: couldn't get free disk space on drive %c: using GetDiskFreeSpace routine.\n", drive_c);
			return (-1);
		}
		cluster_size = sector_per_cluster * bytes_per_sector;
		*free  = int64_t (avail_clusters) * cluster_size;
		*total = int64_t (total_clusters) * cluster_size;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: FILE_get_file_length                                           */
/*      Description: Donne la longueur d'un fichier.                        */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur de fichier. Son contenu peut etre modifie    */
/*                    compte tenu des differentes implementations de la     */
/*                    routine.                                              */
/*      Retour: La longueur du fichier.                                     */
/*==========================================================================*/

int64_t	FILE_get_file_length (FILE *file_ptr)
{
	assert (file_ptr != 0);

	int				handle = _fileno (file_ptr);

	return (_filelengthi64 (handle));
}



int64_t	FILE_get_file_length (const char *filename_0)
{
	assert (filename_0 != 0);
	assert (filename_0 [0] != '\0');

	FILE *			file_ptr = FILE_fopen (filename_0, "rb");
	if (file_ptr == NULL)
	{
		LOG_printf ("FILE_get_file_length: Error: couldn't open file %s.\n",
		            filename_0);
		return (-1);
	}

	const int64_t	length = FILE_get_file_length (file_ptr);

	fclose (file_ptr);

	return (length);
}



/*==========================================================================*/
/*      Nom: FILE_change_file_length                                        */
/*      Description: Change la longueur d'un fichier.                       */
/*      Parametres en entree:                                               */
/*        - length: Nouvelle longueur du fichier                            */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur de fichier. Son contenu peut etre modifie    */
/*                    compte tenu des differentes implementations de la     */
/*                    routine.                                              */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILE_change_file_length (FILE *file_ptr, int64_t length)
{
	assert (file_ptr != 0);
	assert (length >= 0);

	const int		handle = _fileno (file_ptr);

	return (_chsize_s (handle, length));
}



/*==========================================================================*/
/*      Nom: FILE_file_exist                                                */
/*      Description: Cherche si un fichier existe deja.                     */
/*      Parametres en entree:                                               */
/*        - filename_0: nom du fichier (avec ou sans chemin) a tester       */
/*      Retour: true si le fichier a ete trouve, false sinon ou si erreur.  */
/*==========================================================================*/

bool	FILE_file_exist (const char *filename_0)
{
	assert (filename_0 != 0);
	assert (filename_0 [0] != '\0');

	std::wstring   filename = BASE_conv_utf8_to_utf16 (filename_0);

	if (_waccess (filename.c_str (), 0) == 0)
	{
		return (true);
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: FILE_back_up                                                   */
/*      Description: Cree un backup d'un fichier. Les backups multiples     */
/*                   sont supportes. Les backup sont appeles .bak, .bak2,   */
/*                   .bak3, etc.                                            */
/*      Parametres en entree:                                               */
/*        - filename_0: chemin complet du fichier a backuper.               */
/*        - nbr_backup: nombre de backups a gerer (1 = normal, 0 = pas de   */
/*                      backup, 2 a 9 = backups multiples.                  */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILE_backup (const char *filename_0, int nbr_backups)
{
	assert (filename_0 != 0);
	assert (filename_0 [0] != '\0');
	assert (nbr_backups >= 0);

	char		nbr3_0 [99+1];
	String	filename;
	String	older_filename;

	/* Trois chiffres max */
	nbr_backups = MIN (nbr_backups, 999);

	if (nbr_backups <= 0)
	{
		return (0);
	}

	/* Detruit le tout dernier backup */
	older_filename = String (filename_0) + ".bak";
	if (nbr_backups > 1)
	{
		sprintf (nbr3_0, "%d", nbr_backups);
		older_filename += nbr3_0;
	}
	if (FILE_file_exist (older_filename.c_str ()))
	{
		const std::wstring   older_filename_utf16 =
			BASE_conv_utf8_to_utf16 (older_filename.c_str ());
		if (_wremove (older_filename_utf16.c_str ()) != 0)
		{
			LOG_printf ("FILE_back_up: Error: couldn't delete oldest backup of file %s.\n",
			            filename_0);
			return (-1);
		}
	}

	/* Renomme tous les anciens backups */
	for (int cnt = nbr_backups - 1; cnt >= 0; -- cnt)
	{
		filename = filename_0;
		if (cnt > 0)
		{
			filename += ".bak";
			if (cnt > 1)
			{
				sprintf (nbr3_0, "%d", cnt);
				filename += nbr3_0;
			}
		}

		if (FILE_file_exist (filename.c_str ()))
		{
			const std::wstring   filename_utf16 =
				BASE_conv_utf8_to_utf16 (filename.c_str ());
			const std::wstring   older_filename_utf16 =
				BASE_conv_utf8_to_utf16 (older_filename.c_str ());
			if (_wrename (filename_utf16.c_str (), older_filename_utf16.c_str ()) != 0)
			{
				LOG_printf ("FILE_back_up: Error: couldn't rename file %s into %s.\n",
				            filename.c_str (), older_filename.c_str ());
				return (-1);
			}
		}
	
		older_filename = filename;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/
