/* Copyright (c) 2020-2023 The Creators of Simphone

   See the file COPYING.LESSER.txt for copying permission.
*/

#ifndef _FILE_H_
#define _FILE_H_

#include "simtypes.h"

#ifndef SIM_FILE_BUFFER_SIZE
#define SIM_FILE_BUFFER_SIZE 32768
#endif

#define FILE_LOG_CORE "simcore.log"

/* XML history file */
#define FILE_DIRECTORY_HISTORY "history" FILE_DIRECTORY_SLASH
#define FILE_EXTENSION_HISTORY ".html"

/* table keys in mainline file */
#define FILE_KEY_MAIN_NODES "nodes"         /* good DHT nodes */
#define FILE_KEY_MAIN_TORRENTS "torrents"   /* torrent DHT nodes that were already added to FILE_KEY_MAIN_NODES */
#define FILE_KEY_MAIN_BLACKLIST "blacklist" /* blacklisted ip addresses and their search identifiers */

/* truncate a file to its current position */
int sim_file_truncate (int fd);

#ifndef _WIN32
#define FILE_DIRECTORY_SLASH "/"
void *sim_file_fopen_utf (const char *pathname, const char *mode);
#define sim_file_fopen(pathname, mode) sim_file_fopen_utf (pathname, mode)
#define sim_file_close close
#define SIM_FILE_CLOSE(fd) (close (*(fd)), *(fd) = -1)
#else
#define FILE_DIRECTORY_SLASH "\\"
void *sim_file_fopen_utf (const char *pathname, const unsigned short *mode);
#define sim_file_fopen(pathname, MODE) sim_file_fopen_utf (pathname, L##MODE)
#define sim_file_close _close
#define SIM_FILE_CLOSE(fd) (_close (*(fd)), *(fd) = -1)
#endif

/* manipulate files with UTF-8 file names */
int sim_file_rename (const char *oldname, const char *newname);
int sim_file_remove (const char *pathname);
int sim_file_mkdir (const char *pathname);
int sim_file_lstat (const char *pathname, void *output);
int sim_file_stat (const char *pathname, void *output);

/* open a file using the specified mode bits and return file descriptor.
   if size is not NULL, also check that the file is seekable and return its *size */
int sim_file_open (const char *pathname, int bits, simnumber *size);

/* mode flags for sim_file_open and sim_file_create */
#define FILE_BIT_READ 1
#define FILE_BIT_WRITE 2
#define FILE_BIT_TEXT 4
#define FILE_BIT_APPEND 8
#define FILE_BIT_CREATE 16
#define FILE_BIT_TRUNCATE 32
#define FILE_BIT_EXEC 64

/* create a file in user directory and open it using the specified mode bits.
   return file descriptor. also creates parent directories if not already created */
int sim_file_create (const char *filename, int bits);

/* create a soft link from oldname to newname. return true if successful */
simbool sim_file_create_link (const char *oldname, const char *newname);

/* create a hard link from oldname to newname */
int sim_file_link (const char *oldname, const char *newname);

/* return the target of a soft link as a SIMSTRING or nil if failed. caller must call string_free */
simtype sim_file_read_link (const char *pathname);

/* read and write bytes from file descriptor */
int sim_file_read (int fd, void *buffer, unsigned length);
int sim_file_write (int fd, const void *buffer, unsigned length);

/* directory for sim_file_name_new */
#define FILE_DIRECTORY_LOGIN (-1)
#define FILE_DIRECTORY_APP 0
#define FILE_DIRECTORY_USER 1

/* return name of file in login/user directory or in application home directory or nil if no user directory.
   caller must call string_free */
simtype sim_file_name_new (const char *filename, int directory);

/* return full pathname; must be zero-terminated. returned length includes terminating zero. caller must call string_free */
simtype sim_file_name_copy (const simtype pathname);
#define FILE_NAME_COPY(filename) string_copy_len ((filename).str, (filename).len + 1)

/* check if file transfer name does NOT match any of the extensions */
simbool sim_file_name_check (const char *filename, const char *extensions[]);

/* return a copy of filename with any illegal characters replaced by underscores; input length must include terminating zero.
   caller must call string_free */
simtype sim_file_name_parse (const simtype filename, const char *extensions[]);

/* return file name of pathname as SIMPOINTER. length must not include terminating zero */
simtype sim_file_name_split (const char *pathname, unsigned length);

/* check amount of available and total bytes and inodes of a mounted filesystem */
int sim_file_get_stat (const simtype pathname, simunsigned *byte, simunsigned *bytes,
                       simunsigned *node, simunsigned *nodes);

/* return unique identifier of open file. caller must call string_buffer_free */
simtype sim_file_get_id (int fd);

/* return file attribute or -1 if failed. optionally set nanosecond ctime, mtime and atime to *filetimes */
int sim_file_get_type (int fd, simnumber *filetimes);

/* set *filetime to the file's ctime in nanoseconds */
int sim_file_get_time (const char *pathname, simnumber *filetime);

/* set *size to the number of file bytes if SIM_OK */
int sim_file_get_size (int fd, simnumber *size);

/* if filetimes is not NULL, set nanosecond ctime, mtime and atime to *filetimes.
   if size is not NULL, also return file *size */
int sim_file_size (const char *pathname, simnumber *size, simnumber *filetimes);

/* set file access and modification times. input is an array of struct timeval */
int sim_file_utimes (const char *pathname, void *input);

/* set file access and modification times. input is an array of struct timeval */
int sim_file_set_times (int fd, void *input);

/* set location and key for encryption and decryption of files in user directory */
const char *file_set_user (const char *user);
simtype file_set_password (simtype key, simbool password); /* consumes key unless nil */

/* return 0 if auto-login, 1 if password login, -1 if seed not available */
int file_check_password (void);

/* check if file exists (in user directory) */
simbool sim_file_check_exists (const char *filename);

/* securely remove file outside user directory; .old file is not removed */
int file_wipe (simcontact contact, const char *pathname, int (*callback) (int, const void *, unsigned));

/* copy file inside user directory */
int file_copy (const char *oldfilename, const char *newfilename);

/* lock or unlock files in user directory */
int file_lock (simbool lock);

/* type for file_save and file_load */
#define FILE_TYPE_PLAINTEXT 0 /* not encrypted */
#define FILE_TYPE_KEY 1       /* encrypted with password */
#define FILE_TYPE_ENCRYPTED 2 /* encrypted with key */
#define FILE_TYPE_TEMPORARY 4 /* do not save backup file */

/* save file to user directory. use nil table to delete the file securely. type is FILE_TYPE_xxx */
int file_save (simtype table, const char *filename, int type);

/* load file from user directory into *table. return an empty *table if file does not exist */
int _file_load (const char *filename, int type, simtype *table, const char *file, unsigned line);
#define file_load(filename, type, table) _file_load (filename, type, table, __FUNCTION__, __LINE__)

/* load new DHT nodes from .torrent files */
void file_load_torrents (simtype mainline);

/* load last number of lines from log file into log buffer */
int sim_file_load_log (simnumber lines, unsigned maxsize);

/* internally used. maxsize is a memory limit in bytes (0 = unlimited) */
int sim_file_load_history (const char *pathname, simnumber lines, unsigned maxsize, const char *nick,
                           simtype *messages, unsigned *count);

/* load last number of messages from history file into buffer. clear buffer if lines is zero */
int file_load_msg_ (simcontact contact, simnumber lines);

/* convert string to 64-bit unsigned decimal. return false if string is invalid */
simbool sim_convert_string_to_simunsigned (const char *string, simnumber *number);

/* error checking */
#define FILE_CASE_ERROR(length, error) ((int) (length) == -1 && errno ? errno : (error))

/* debug logging */
void file_log_password (const char *module, int level, simbool numeric);

#endif
