/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



/*****************************************************************/
/* Includes                                                      */
/*****************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "system.h"
#include <osl/file.h>
#include <osl/thread.h>
#include <rtl/ustrbuf.h>
#include <osl/diagnose.h>

#ifndef _FILE_URL_H_
#include "file_url.h"
#endif
 
/*****************************************************************/
/* osl_getTempFirURL                                             */
/*****************************************************************/

oslFileError SAL_CALL osl_getTempDirURL( rtl_uString** pustrTempDir )
{
    const char *pValue = getenv( "TEMP" );

    if ( !pValue )
	{
        pValue = getenv( "TMP" );
#if defined(SOLARIS) || defined (LINUX) || defined (FREEBSD) || defined (MACOSX)
		if ( !pValue )
			pValue = P_tmpdir;
#endif
	}

	if ( pValue )
	{
		oslFileError error;
		rtl_uString	*ustrTempPath = NULL;

		rtl_string2UString( &ustrTempPath, pValue, strlen( pValue ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
        OSL_ASSERT(ustrTempPath != NULL);
		error = osl_getFileURLFromSystemPath( ustrTempPath, pustrTempDir );
		rtl_uString_release( ustrTempPath );
		
		return error;
	}
	else
		return osl_File_E_NOENT;
}

/******************************************************************
 * Generates a random unique file name. We're using the scheme   
 * from the standard c-lib function mkstemp to generate a more   
 * or less random unique file name                               
 *                                                               
 * @param rand_name                                              
 *        receives the random name                               
 ******************************************************************/

static const char LETTERS[]        = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static const int  COUNT_OF_LETTERS = sizeof(LETTERS)/sizeof(LETTERS[0]) - 1;

#define RAND_NAME_LENGTH 6

static void osl_gen_random_name_impl_(rtl_uString** rand_name)
{	   
	static uint64_t value; 
	
	char     buffer[RAND_NAME_LENGTH];
	struct   timeval tv;
	uint64_t v;
	int      i;
	
	gettimeofday(&tv, NULL);
	
	value += ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
	
	v = value;
	
	for (i = 0; i < RAND_NAME_LENGTH; i++)
	{
		buffer[i] = LETTERS[v % COUNT_OF_LETTERS];
		v        /= COUNT_OF_LETTERS;
	}
	
	rtl_string2UString(
			rand_name, 
			buffer,
			RAND_NAME_LENGTH,
			RTL_TEXTENCODING_ASCII_US,
			OSTRING_TO_OUSTRING_CVTFLAGS);
    OSL_ASSERT(*rand_name != NULL);
}

/*****************************************************************
 * Helper function                                               
 * Either use the directory provided or the result of            
 * osl_getTempDirUrl and return it as system path and file url   
 ****************************************************************/

static oslFileError osl_setup_base_directory_impl_(
	rtl_uString*  pustrDirectoryURL,
	rtl_uString** ppustr_base_dir)
{
	rtl_uString* dir_url = 0;
	rtl_uString* dir     = 0;
    oslFileError error   = osl_File_E_None;                     
    
	if (pustrDirectoryURL)
		rtl_uString_assign(&dir_url, pustrDirectoryURL);
	else
		error = osl_getTempDirURL(&dir_url);
		
    if (osl_File_E_None == error)
	{
        error = osl_getSystemPathFromFileURL_Ex(dir_url, &dir, FURL_DENY_RELATIVE);       						
		rtl_uString_release(dir_url);
	}
        
	if (osl_File_E_None == error)
	{
		rtl_uString_assign(ppustr_base_dir, dir);
		rtl_uString_release(dir);
	}
	
    return error;	
}

/*****************************************************************
 * osl_setup_createTempFile_impl
 * validate input parameter, setup variables
 ****************************************************************/
 
 static oslFileError osl_setup_createTempFile_impl_(
 	rtl_uString*   pustrDirectoryURL,
	oslFileHandle* pHandle,
	rtl_uString**  ppustrTempFileURL,
	rtl_uString**  ppustr_base_dir,
	sal_Bool*	   b_delete_on_close)
 {
 	oslFileError osl_error;
	
	OSL_PRECOND(((0 != pHandle) || (0 != ppustrTempFileURL)), "Invalid parameter!");
	
	if ((0 == pHandle) && (0 == ppustrTempFileURL))
	{
		osl_error = osl_File_E_INVAL;
	}
	else
	{
		osl_error = osl_setup_base_directory_impl_(
			pustrDirectoryURL, ppustr_base_dir);
			
		*b_delete_on_close = (0 == ppustrTempFileURL);
	}
	
	return osl_error;
 }
 
/*****************************************************************
 * Create a unique file in the specified directory and return    
 * it's name                                                     
 ****************************************************************/

static oslFileError osl_create_temp_file_impl_(
	const rtl_uString* pustr_base_directory, 
	oslFileHandle* file_handle,
	rtl_uString** ppustr_temp_file_name)
{
	rtl_uString*        rand_name        = 0;
	sal_uInt32          len_base_dir     = 0;
	rtl_uString*        tmp_file_path    = 0;
	rtl_uString*        tmp_file_url     = 0;
	sal_Int32           capacity         = 0;
	oslFileError        osl_error        = osl_File_E_None;
	sal_Int32           offset_file_name;
	const sal_Unicode*  puchr;
		
	OSL_PRECOND(pustr_base_directory, "Invalid Parameter");
	OSL_PRECOND(file_handle, "Invalid Parameter");
	OSL_PRECOND(ppustr_temp_file_name, "Invalid Parameter");
	
	len_base_dir = rtl_uString_getLength(pustr_base_directory);
	
	rtl_uStringbuffer_newFromStr_WithLength(
		&tmp_file_path, 
		rtl_uString_getStr((rtl_uString*)pustr_base_directory),
		len_base_dir);
						
	rtl_uStringbuffer_ensureCapacity(
		&tmp_file_path, 
		&capacity, 
		(len_base_dir + 1 + RAND_NAME_LENGTH));
		
	offset_file_name = len_base_dir;
		
	puchr = rtl_uString_getStr(tmp_file_path);
	
	/* ensure that the last character is a '\' */
	
	if ((sal_Unicode)'\\' != puchr[len_base_dir - 1])
	{		
		rtl_uStringbuffer_insert_ascii(
			&tmp_file_path, 
			&capacity, 
			len_base_dir,
			"\\",
			1);
		
		offset_file_name++;					
	}
		 
	while(1) /* try until success */
	{							
		osl_gen_random_name_impl_(&rand_name);
					
		rtl_uStringbuffer_insert(
			&tmp_file_path,
			&capacity,
			offset_file_name,
			rtl_uString_getStr(rand_name),
			rtl_uString_getLength(rand_name));

		osl_error = osl_getFileURLFromSystemPath(
		    tmp_file_path, &tmp_file_url);				
		
		if (osl_File_E_None == osl_error)
		{
		    /* RW permission for the user only! */
		    mode_t old_mode = umask(077);
		    
		    osl_error = osl_openFile(
			    tmp_file_url, 
			    file_handle, 
			    osl_File_OpenFlag_Read | 
			    osl_File_OpenFlag_Write | 
			    osl_File_OpenFlag_Create);
			    
			umask(old_mode);
		}
		
		/* in case of error osl_File_E_EXIST we simply try again else we give up */	
		
		if ((osl_File_E_None == osl_error) || (osl_error != osl_File_E_EXIST))
		{
			if (rand_name)
				rtl_uString_release(rand_name);
			
			if (tmp_file_url)
				rtl_uString_release(tmp_file_url);
								
			break;
		}					
	} /* while(1) */
	
	if (osl_File_E_None == osl_error)
		rtl_uString_assign(ppustr_temp_file_name, tmp_file_path);
	
	if (tmp_file_path)
		rtl_uString_release(tmp_file_path);
		
	return osl_error;
}

/*****************************************************************
 * osl_createTempFile                                            
 *****************************************************************/

oslFileError SAL_CALL osl_createTempFile(
	rtl_uString*   pustrDirectoryURL,
	oslFileHandle* pHandle, 
	rtl_uString**  ppustrTempFileURL)
{
	rtl_uString*  base_directory     = 0;	
	rtl_uString*  temp_file_name     = 0;
	oslFileHandle temp_file_handle;
	sal_Bool      b_delete_on_close;
	oslFileError  osl_error;
	
	osl_error = osl_setup_createTempFile_impl_(
		pustrDirectoryURL, 
		pHandle,
		ppustrTempFileURL,		
		&base_directory,
		&b_delete_on_close);
	
	if (osl_File_E_None != osl_error)		
		return osl_error;
		
	osl_error = osl_create_temp_file_impl_(
	    base_directory, &temp_file_handle, &temp_file_name);
								
	if (osl_File_E_None == osl_error) 
	{		
		rtl_uString* temp_file_url = 0;		
		
		/* assuming this works */
		osl_getFileURLFromSystemPath(temp_file_name, &temp_file_url);
												
		if (b_delete_on_close)
		{			
			osl_error = osl_removeFile(temp_file_url);
			
			if (osl_File_E_None == osl_error)
				*pHandle = temp_file_handle;
			else
				osl_closeFile(temp_file_handle);
		}
		else 
		{		
			if (pHandle)
				*pHandle = temp_file_handle;				
			else
				osl_closeFile(temp_file_handle);	
				                     
			rtl_uString_assign(ppustrTempFileURL, temp_file_url);                                       
        }
		
		if (temp_file_url)
			rtl_uString_release(temp_file_url);
				
        if (temp_file_name)
            rtl_uString_release(temp_file_name);
	}
    
    if (base_directory)
    	rtl_uString_release(base_directory);      
        
    return osl_error;
}
