#define MODULE_LOG_PREFIX "files"

#include "globals.h"

#include "oscam-files.h"
#include "oscam-lock.h"
#include "oscam-string.h"

extern CS_MUTEX_LOCK readdir_lock;
extern char cs_tmpdir[200];

/* Gets the tmp dir */
char *get_tmp_dir(void)
{
	if(cs_tmpdir[0])
	{
		return cs_tmpdir;
	}
#if defined(__CYGWIN__)

	char *d = getenv("TMPDIR");

	if(!d || !d[0])
	{
		d = getenv("TMP");
	}

	if(!d || !d[0])
	{
		d = getenv("TEMP");
	}

	if(!d || !d[0])
	{
		getcwd(cs_tmpdir, sizeof(cs_tmpdir) - 1);
	}

	cs_strncpy(cs_tmpdir, d, sizeof(cs_tmpdir));
	char *p = cs_tmpdir;
	while(*p) { p++; }
	p--;
	if(*p != '/' && *p != '\\')
	{
		cs_strncat(cs_tmpdir, "/", sizeof(cs_tmpdir));
	}
	cs_strncat(cs_tmpdir, "_oscam", sizeof(cs_tmpdir));
#else
	cs_strncpy(cs_tmpdir, "/tmp/.oscam", sizeof(cs_tmpdir));
#endif
	mkdir(cs_tmpdir, S_IRWXU);
	return cs_tmpdir;
}

char *get_tmp_dir_filename(char *dest, size_t destlen, const char *filename)
{
	char *tmp_dir = get_tmp_dir();
	const char *slash = "/";
	if(tmp_dir[cs_strlen(tmp_dir) - 1] == '/')
	{
		slash = "";
	}
	snprintf(dest, destlen, "%s%s%s", tmp_dir, slash, filename);
	return dest;
}

/* Return 1 if the file exists, else 0 */
bool file_exists(const char *filename)
{
	return access(filename, R_OK) == 0;
}

/* Finds a file in the system PATH.
   Returns a newly allocated string with the full path, or NULL if not found.
   Caller must free() the returned string. */
char *find_in_path(const char *filename)
{
	// If filename already has '/', don't search PATH
	if (strchr(filename, '/')) {
		return strdup(filename);
	}

	char *paths; const char *path = getenv("PATH"); if (!path || !(paths = strdup(path))) return NULL;

	char *saveptr = NULL;
	char *dir = strtok_r(paths, ":", &saveptr);
	char fullpath[PATH_MAX];
	char *result = NULL;

	while (dir) {
		if (*dir == '\0') dir = ".";
		snprintf(fullpath, sizeof(fullpath), "%s/%s", dir, filename);
		if (file_exists(fullpath)) {
			result = strdup(fullpath);
			break;
		}
		dir = strtok_r(NULL, ":", &saveptr);
	}

	free(paths);
	return result;
}

/* Copies a file from srcfile to destfile. If an error occured before writing,
   -1 is returned, else -2. On success, 0 is returned.*/

int32_t file_copy(char *srcfile, char *destfile)
{
	FILE *src, *dest;
	int32_t ch;

	src = fopen(srcfile, "r");
	if(!src)
	{
		cs_log("Error opening file %s for reading (errno=%d %s)!", srcfile, errno, strerror(errno));
		return -1;
	}

	dest = fopen(destfile, "w");
	if(!dest)
	{
		cs_log("Error opening file %s for writing (errno=%d %s)!", destfile, errno, strerror(errno));
		fclose(src);
		return -1;
	}

	while(1)
	{
		ch = fgetc(src);
		if(ch == EOF)
		{
			break;
		}
		else
		{
			fputc(ch, dest);
			if(ferror(dest))
			{
				cs_log("Error while writing to file %s (errno=%d %s)!", destfile, errno, strerror(errno));
				fclose(src);
				fclose(dest);
				return -2;
			}
		}
	}
	fclose(src);
	if(fsync(fileno(dest)) == -1) // Dodano fsync, aby wymusić zapis na dysk
	{
		cs_log("ERROR: file_copy - fsync failed for %s (errno=%d %s)!", destfile, errno, strerror(errno));
	}
	fclose(dest);
	return (0);
}

/* Overwrites destfile with temp_file. If forceBakOverWrite = 0,
   the bakfile will not be overwritten if it exists, else it will be.*/

int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile, int32_t forceBakOverWrite)
{
	int32_t rc;

	if(file_exists(destfile))
	{
		if(forceBakOverWrite != 0 || !file_exists(bakfile))
		{
			if(file_copy(destfile, bakfile) < 0)
			{
				cs_log("ERROR: safe_overwrite_with_bak - Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile);
				if(unlink(temp_file) < 0)
				{
					cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
				}
				return 1;
			}
		}
	}

	rc = file_copy(temp_file, destfile);
	if(rc < 0)
	{
		cs_log("ERROR: safe_overwrite_with_bak - An error occured while writing the new config file %s. Return code: %d, errno: %d (%s).", destfile, rc, errno, strerror(errno));
		if(rc == -2)
		{
			cs_log("ERROR: safe_overwrite_with_bak - The config will be missing or only partly filled upon next startup as this is a non-recoverable error! Please restore from backup or try again.");
		}
		if(unlink(temp_file) < 0)
		{
			cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
		}
		return 1;
	}

	if(unlink(temp_file) < 0)
	{
		cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno));
	}
	return 0;
}

#ifdef MODULE_GBOX
char *get_gbox_filename(char *dest, size_t destlen, const char *filename)
{
	char *tmp_dir = get_tmp_dir();
	const char *slash = "/";

	if(cfg.gbox_tmp_dir != NULL)
	{
		if(cfg.gbox_tmp_dir[cs_strlen(cfg.gbox_tmp_dir) - 1] == '/')
		{
			slash = "";
		}
		snprintf(dest, destlen, "%s%s%s", cfg.gbox_tmp_dir, slash, filename);
	}
	else
	{
		if(tmp_dir[cs_strlen(tmp_dir) - 1] == '/') { slash = ""; }
		snprintf(dest, destlen, "%s%s%s", tmp_dir, slash, filename);
	}
	return dest;
}
#endif
