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

#define	US_LEN	16

typedef struct	safeover_s
{
	ioport_base_t	vtbl;
	vhandle_t		tmp;
	ioport_base_p	tmp_vtbl;
	char			dest_name[_MAX_PATH];
	vboolean		cancel;
}	safeover_t, *safeover_p;

V_LOCAL vsize_t V_CALL
sov_write(vhandle_t handle,void *buf,vsize_t len)
{
	safeover_p self = (safeover_p)handle;
	return self->tmp_vtbl->write(self->tmp,buf,len);
}

V_LOCAL vsize_t V_CALL
sov_tell(vhandle_t handle)
{
	safeover_p self = (safeover_p)handle;
	return self->tmp_vtbl->tell(self->tmp);
}

V_LOCAL vsize_t V_CALL
sov_seek(vhandle_t handle,vssize_t offset,int whence)
{
	safeover_p self = (safeover_p)handle;
	return self->tmp_vtbl->seek(self->tmp,offset,whence);
}

V_LOCAL	int V_CALL
sov_ioctl(vhandle_t handle,unsigned code,void *parameters)
{
	safeover_p self = (safeover_p)handle;
	if(code == pctl_delete_on_close)
    {
       	if(!parameters)
           	return -1;
    	self->cancel = *((vboolean*)parameters);
    	return 0;
    }
    return self->tmp_vtbl->ioctl(self->tmp,code,parameters);
}

V_LOCAL u32_t V_CALL
sov_destroy(vhandle_t handle)
{
	CALLER_NAME(sov_destroy)
	safeover_p self = (safeover_p)handle;
	char tmp_name[_MAX_PATH];
	UNREF_EXIT((ioport_base_p)self);
	self->tmp_vtbl->ioctl(self->tmp,pctl_get_name,tmp_name);
	fileClose(self->tmp);
	if(self->cancel)
		archDeleteFile(tmp_name);
	else
	{
		if(fileCopy(self->dest_name,tmp_name,true))
			archDeleteFile(tmp_name);
#ifdef	CHECKED_BUILD
		else
		{
			conlPrintf(LOG_ERROR,
				"%s(): unable to rename \"%s\" to \"%s\".\n",
				caller_name, tmp_name, self->dest_name);
		}
#endif	/* CHECKED_BUILD */
	}
	memFreeObject(self);
	return 0;
}

vhandle_t V_GLOBAL
fileOpenSafeOverwrite(char *file_name)
{
	CALLER_NAME(fileOpenSafeOverwrite)
	safeover_p self;
	vhandle_t tmp;
	char tmp_name[_MAX_PATH], tail[US_LEN + 1];
	/* check for space for unique tail */
	if(!file_name || !*file_name)
	{
		ERROR_GENERIC("empty file name");
		return LIBV_INVALID_HANDLE;
	}
	if(strlen(file_name) >= (_MAX_PATH - US_LEN))
	{
		ERROR_GENERIC_2ARG("file name too long: ",file_name);
		return LIBV_INVALID_HANDLE;
	}
	strcpy(tmp_name,file_name);
	archUniqueString(tail,US_LEN);
	strcat(tmp_name,tail);
	tmp = fileOpenWrite(tmp_name);
	if(tmp == LIBV_INVALID_HANDLE)
	{
		int trial;
		char tmp_dir[_MAX_PATH];
		archGetTempDirectory(tmp_dir,_MAX_PATH - 1);
		strAppendSlash(tmp_dir);
		if(strlen(tmp_dir) >= (_MAX_PATH - US_LEN))
		{
			ERROR_GENERIC_2ARG("temporary directory name too long: ",tmp_dir);
			return LIBV_INVALID_HANDLE;
		}
		for(trial = 0;trial < 4096;trial++)
		{
			strcpy(tmp_name,tmp_dir);
			strcat(tmp_name,tail);
			if(!archFileExists(tmp_name) && !archDirectoryExists(tmp_name))
				tmp = fileOpenWrite(tmp_name);
			if(tmp != LIBV_INVALID_HANDLE)
				break;
			archUniqueString(tail,US_LEN);
		}
		if(tmp == LIBV_INVALID_HANDLE)
			return tmp;
	}
	self = (safeover_p)ioAllocPort(sizeof(safeover_t));
	self->tmp = tmp;
	strcpy(self->dest_name,file_name);
	self->tmp_vtbl = (ioport_base_p)tmp;
	self->vtbl.base.destroy = sov_destroy;
	self->vtbl.write = sov_write;
	self->vtbl.tell = sov_tell;
	self->vtbl.seek = sov_seek;
	self->vtbl.ioctl = sov_ioctl;
	return self;
}

void V_GLOBAL
fileCancelSafeOverwrite(vhandle_t handle)
{
	vboolean flag = true;
	fileControl(handle,pctl_delete_on_close,&flag);
	fileClose(handle);
}

#undef US_LEN
