#include <../source/TAL/objbase.h>
#include <../source/vfs/np_priv.h>
#include <../source/vfs/fsops.h>
#include <../arch/arch.h>

#define	US_LEN	16

/*
 * FIXME:
 * 1. Implement archGetWriteableDirectory().
 * 2. Create temp file at open(), rename at destroy() ?
 */

V_LOCAL u32_t V_CALL
safe_overwrite_destroy(vhandle_t handle)
{
#ifdef	CHECKED_BUILD
	static char *caller = "safe_overwrite::destroy";
#endif	/* CHECKED_BUILD */
	int count, count_max = 16;
	arch_file_t tmp_out = ARCH_INVALID_FILE;
	libv_object_p self = (libv_object_p)handle;
	char dest_dir[_MAX_PATH];
	char dest_name[_MAX_PATH];
	char temp_name[_MAX_PATH];
	void_p ptr;
	vsize_t nb;
	/*
	 * There is an inherited destructor,
	 * so we must check self->ref_count before decrement.
	 */
	if(self->ref_count > 1)
		/* no further actions; decrement and return */
		return --self->ref_count;
	/* Get target file name */
	fileControl(self,pctl_get_name,dest_name);
	/* Open temporary file */
	fsSplitPath(dest_name,dest_dir,NULL,NULL);
	if(!dest_dir[0])
	{
		dest_dir[0] = '.';
		dest_dir[1] = 0;
	}
	if(lfsSetDir(dest_dir,true))
	{
		strCopy(temp_name,dest_name,_MAX_PATH);
		for(count=0;count < count_max;count++)
		{
			archUniqueString(temp_name + strlen(dest_name),US_LEN);
			if(!archFileExists(temp_name))
			{
				tmp_out = archFileOpen(temp_name,file_mode_write,NULL);
				if(tmp_out != ARCH_INVALID_FILE)
					break;
#ifdef	CHECKED_BUILD
				else
					conlPrintf(LOG_ERROR,"%s(%s): unable to open temporary "
						"file '%s' (trial %u of %u).\n",
						caller,dest_name,temp_name,count+1,count_max);
#endif	/* CHECKED_BUILD */
			}
#ifdef	CHECKED_BUILD
			else
				conlPrintf(LOG_TRACE,"%s(%s): temporary file '%s' "
					"already exists (trial %u of %u).\n",
					caller,dest_name,temp_name,count+1,count_max);
#endif	/* CHECKED_BUILD */
		}
	}
	if(tmp_out == ARCH_INVALID_FILE)
	{
		/* We have to save the information _anyway_ */
		u32_t extra_len;
		char *endptr;
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"%s(%s): unable to open file in '%s', "
			"trying to open it in temporary directory.\n",
			caller,dest_name,dest_dir);
#endif	/* CHECKED_BUILD */
		if(!archGetTempDirectory(temp_name,_MAX_PATH) ||
			!archDirectoryExists(temp_name) ||
			(extra_len = strlen(temp_name)) > (_MAX_PATH - 32))
		{
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_WARNING,
				"%s(): error getting system temporary directory.\n",
				caller);
#endif	/* CHECKED_BUILD */
			strCopy(temp_name,"/tmp/",_MAX_PATH);
			if(!archDirectoryExists(temp_name))
				strCopy(temp_name,"/temp/",_MAX_PATH);
			if(!archDirectoryExists(temp_name))
				strCopy(temp_name,"/./",_MAX_PATH);
			extra_len = strlen(temp_name);
		}
		else
		{
			strAppendSlash(temp_name);
			extra_len = strlen(temp_name);
		}
		endptr = temp_name + extra_len;
		extra_len = _MAX_PATH - extra_len;
		if(extra_len > 64)
			extra_len = 64;
		for(count=0;count < count_max;count++)
		{
			archUniqueString(endptr,extra_len);
			tmp_out = archFileOpen(temp_name,file_mode_write,NULL);
			if(tmp_out != ARCH_INVALID_FILE)
			{
#ifdef	CHECKED_BUILD
				conlPrintf(LOG_ERROR,
					"%s(%s): saving to '%s'.\n",caller,dest_name,temp_name);
#endif	/* CHECKED_BUILD */
				break;
			}
#ifdef	CHECKED_BUILD
			else
				conlPrintf(LOG_ERROR,"%s(%s): unable to open temporary "
					"file '%s' (trial %u of %u).\n",
					caller,dest_name,temp_name,count+1,count_max);
#endif	/* CHECKED_BUILD */
		}
	}
	if(tmp_out == ARCH_INVALID_FILE)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"%s(%s): default logic failed and "
			"no archGetWriteableDirectory(). \n"
			"You information will be lost, sorry.\n",
			caller,dest_name);
#endif	/* CHECKED_BUILD */
	}
	else
	{
		fileControl(self,pctl_pipe_head,&ptr);
		fileControl(self,pctl_pipe_head_len,&nb);
		if(nb)
			archFileWrite(tmp_out,ptr,nb);
		fileControl(self,pctl_pipe_tail,&ptr);
		fileControl(self,pctl_pipe_tail_len,&nb);
		if(nb)
			archFileWrite(tmp_out,ptr,nb);
		archFileClose(tmp_out);
	}
	fileSeek(self,0,SEEK_SET); /* clear the pipe */
	if(archFileExists(dest_name))
		archDeleteFile(dest_name);
	if(!archMoveFile(temp_name,dest_name))
	{
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"%s(%s): unable to rename file '%s'.\n",
				caller,dest_name,temp_name);
#endif	/* CHECKED_BUILD */
	}
	return self->inherited_destroy(handle);
}

vhandle_t V_GLOBAL
fileOpenSafeOverwrite(char *file_name)
{
#ifdef	__DOS32__
	return fileOpenWrite(file_name); /* for what to be safe ? :) */
#else
	vhandle_t handle;
	if(!archFileExists(file_name))
	{
		handle = fileOpenWrite(file_name);
		if(handle != LIBV_INVALID_HANDLE)
			return handle;
	}
	/* check for space for unique tail */
	if(file_name && strlen(file_name) > (_MAX_PATH - US_LEN))
	{
		conlPrintf(LOG_ERROR,"fileOpenSafeOverwrite(%s): file name too long.\n",file_name);
		return LIBV_INVALID_HANDLE;
	}
	handle = ioCreatePrivatePipe(file_name);
	if(handle != LIBV_INVALID_HANDLE)
	{
		libv_object_p self = (libv_object_p)handle;
		self->inherited_destroy = self->destroy;
		self->destroy = safe_overwrite_destroy;
	}
	return handle;
#endif
}

#undef US_LEN
