import java.io.*;
import java.util.*;

/**
 * FileManager.
 *
 * This class handles file management functions such as copying, moving, deleting
 * of files and directories. It also handles writing Strings/String arrays to file
 * (in overwrite or append mode, for example for use in log files).
 *
 * Copyright (C) 1999  Shazron Abdullah
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *	@author Shazron Abdullah
 *  @version JDK 1.1.7
 */
public class FileManager
{	
	public static Random _random = null;

	/**
	 * Moves a file or a directory into another directory.
	 *
	 * @param source a file or directory
	 * @param dest a directory
	 * @return true if the move was successful, false if not
	 */
	public static boolean moveFile(String source, String dest) 
	{
		File sourceFile = new File(source);
		
		if (!sourceFile.exists())
			return false;

		return sourceFile.renameTo(new File(dest));
	}


	/**
	 *  Renames a file to a new name.
	 *
	 *  @param source a file 
	 *	@param newName the new name of the file or directory
	 *  @return true if the renaming was successful, false if not
	 */
	public static boolean renameFile(String source, String newName) 
	{
		File sourceFile = new File(source);
		
		if (!sourceFile.exists() || sourceFile.isDirectory())
			return false;

		String newfileName = sourceFile.getParent() +  File.separator + newName;
		
		return sourceFile.renameTo(new File(newfileName));
	}

	/**
	 *  Rename a directory to a new name. <BR>
	 *
	 *	Apparently renaming a dir does not work with java.io.File.renameTo(String)
	 *  so I wrote this function instead.
	 *  
	 *	Basically the algorithm goes like this: 
	 *	(1) Make a new directory in the same root as the directory to rename
	 *	(2)	Copy the files in the old directory into the new directory
	 *	(3) Delete the old directory
	 *
	 *  @param source a directory with full path
	 *	@param newName the new name of the directory
	 *  @return true if the renaming was successful, false if not
	 */
	public static boolean renameDir(String source, String newName) 
	{
		File sourceFile = new File(source);
		
		if (!sourceFile.exists() && !sourceFile.isDirectory())
			return false;

		String newDirName = sourceFile.getParent() +  File.separator + newName;

		// (1) Impl
		File newDir = new File(newDirName);
		boolean dirCreateSuccess = newDir.mkdir();
		if (!dirCreateSuccess) { return false; }

		// (2) Impl
		boolean copySuccess = copyFile(source, newDirName, true);
		if (!copySuccess) { return false; }

		// (3) Impl
		boolean deleteDirSuccess = deleteFile(source);
		if (!deleteDirSuccess) { return false; }

		// if everything got here ok, we're home free
		return true;
	}

/* *******************************************************************************
	public static boolean renameFile(String source, String newName) 
	{
		File sourceFile = new File(source);
		
		if (!sourceFile.exists())
			return false;

		String newfileName = sourceFile.getParent() +  File.separator + newName;
		System.out.println("newfileName : " + newfileName);
		

		try
		{
			if (!sourceFile.isDirectory())
			{
				FileInputStream istr = new FileInputStream(sourceFile);
				BufferedInputStream bis = new BufferedInputStream(istr);
				FileOutputStream fos = new FileOutputStream(newfileName);

				int sz = (int) sourceFile.length();
				int N = 1024;
				byte buf[] = new byte[N];
				int ln = 0;

				while (sz > 0 &&  // workaround for bug
					(ln = bis.read(buf, 0, Math.min(N, sz))) != -1) 
				{
					fos.write(buf, 0, ln);
					sz -= ln;
				}

				bis.close();
				fos.flush();

			}
			else return sourceFile.renameTo(new File(newfileName));

		} catch (FileNotFoundException fnfe) {
			System.out.println(fnfe);
			return false;

		} catch (IOException ie) {
			System.out.println(ie);
			return false;
		}

		deleteFile(source);
		return true;
	}

	********************************************************************************/

	/**
	 *	Copies a file or directory to another directory (recursive directory copy). 
	 *
	 *	PRECONDITION: source and dest must exist, and dest must be a directory.
	 *
	 *	@param source a file or directory
	 *	@param dest a directory
	 */
	public static boolean copyFile(String source, String dest) 
	{
		return copyFile(source, dest, false);
	}
	

	/**
	 *	Copies a file or directory to another directory (recursive directory copy).
	 *  This function overwrites files if they already exist.
	 *
	 *	PRECONDITION: source and dest must exist, and dest must be a directory.
	 *
	 *	@param source a file or directory
	 *	@param dest a directory
	 *  @param skip_first skip copying the source if it is a directory, but copy its contents over
	 *      i.e. don't make the directory 'source' in the 'dest' dir, but copy the contents of
	 *      of 'source' into 'dest'
	 */
	public static boolean copyFile(String source, String dest, boolean skip_first) 
	{
		return copyFile(source, dest, skip_first, true);
	}
	
	/**
	 *	Copies a file or directory to another directory (recursive directory copy). 
	 *
	 *	PRECONDITION: source and dest must exist, and dest must be a directory.
	 *
	 *	@param source a file or directory
	 *	@param dest a directory
	 *  @param skip_first skip copying the source if it is a directory, but copy its contents over
	 *      i.e. don't make the directory 'source' in the 'dest' dir, but copy the contents of
	 *      of 'source' into 'dest'
	 *  @param over_write overwrite a file if it already exists
	 */
	public static boolean copyFile(String source, String dest, boolean skip_first, boolean over_write) 
	{
		File sourceFile = new File(source);
		File destFile = new File(dest);
		
		if (over_write)
			if (destFile.exists() && ! destFile.isDirectory())
				destFile.delete();
			
		if (!(sourceFile.exists() || destFile.exists() || destFile.isDirectory()))
			return false;

		if (sourceFile.isDirectory())
		{
			String newDir=null;

			if (!skip_first)
			{
				newDir = dest + File.separator + sourceFile.getName();
				File makenewDir = new File(newDir);
				makenewDir.mkdir();
			}
			else
				newDir = dest;

			String[] files = sourceFile.list();
			for (int i=0; i < files.length; i++)
			{
				String filename = sourceFile.getAbsolutePath() + File.separator + files[i];
				debugMessage("File to copy: " + filename + " --> " 
					+ newDir + File.separator + files[i]);
				copyFile(filename,newDir, false, over_write);
			}
		}
		try 
		{
			if (!sourceFile.isDirectory())
			{
				FileInputStream istr = new FileInputStream(sourceFile);
				BufferedInputStream bis = new BufferedInputStream(istr);
				FileOutputStream fos = new FileOutputStream(dest + File.separator +  sourceFile.getName());

				int sz = (int) sourceFile.length();
				int N = 1024;
				byte buf[] = new byte[N];
				int ln = 0;

				while (sz > 0 &&  // workaround for bug
					(ln = bis.read(buf, 0, Math.min(N, sz))) != -1) 
				{
					fos.write(buf, 0, ln);
					sz -= ln;
				}

				bis.close();
				fos.flush();
			}

		} catch (FileNotFoundException fnfe) { 
			debugMessage(fnfe); 	
			return false;

		} catch (IOException ie) {
			debugMessage(ie);
			return false;
		}

		return true;
	}

	/**
	 *	This will recursively delete files (if it is a directory, else it will just
	 *  delete the file).
	 *
	 *  @param source a file or directory
	 *  @return true if the file was deleted, false if not
	 */
	public static boolean deleteFile(String source) 
	{
		File delFile = new File(source);

		if (!delFile.exists())
			return false;

		if (delFile.isDirectory())
		{
			String[] files = delFile.list();
			for (int i=0; i < files.length; i++)
			{
				String filename = delFile.getAbsolutePath() + File.separator + files[i];
				deleteFile(filename);
			}
		}

		boolean deleteSuccess=false;

		try 
		{
			deleteSuccess = delFile.delete();

			if (deleteSuccess)
				debugMessage("Deleted: " + source);
			else
				debugMessage("Failed to delete: " + source);
		}
		catch (Throwable t) { debugMessage(t.toString());   }

		return deleteSuccess;
	}

	/**
	 * This function will generate a random filename of variable length
     * according to the parameters given.
	 *
	 * @param numchars the number of characters for the filename
	 * @return the random string that was generated
     */
    public static String generateRandomFileName(int numchars) throws Exception
	{

        String alphanumArray = new String("abcdefghijklmnopqrstuvwxyz0123456789");
        //Random randomgen = new Random();
		if ( _random == null)
			_random = new Random();

        StringBuffer name = new StringBuffer("");
        char newchar;
        int gen_no;
        
        // exit function if # of chars in file is 0 or less
        if (numchars <= 0)
            throw new Exception("Number of chars must be > 0");
            
        for (int i=0; i < numchars ; i++) {
            gen_no = _random.nextInt() % 36;
            if (gen_no < 0)
                gen_no *= -1;
            else if (gen_no > 35)
                gen_no = 35;
            newchar = alphanumArray.charAt(gen_no);
            name = name.append(newchar);
        }
        
        return name.toString();
	}


	/**
	 *	Returns the current directory of the process.
	 *
	 *  @return the current working directory
	 */
	public static String getCurrentDirectory()
	{
		File currdir = new File("");
		return currdir.getAbsolutePath();
	}

	/**
	 *  Creates a randomly named directory in the directory specified.
	 *
	 *	@param parentDir the directory to create the random directory in parentDir
	 *	@return the name of the random directory if successful, else ""
	 */
	public static String createRandomDirectory(String parentDir)
	{
		return createRandomDirectory(parentDir, "", "");
	}

	/**
	 *  Creates a randomly named directory in the directory specified.
	 *
	 *	@param parentDir the directory to create the random directory in parentDir
	 *  @param prefix the string to prepend to the name
	 *  @param suffix the string to append to the name
	 *	@return the name of the random directory if successful, else ""
	 */
	public static String createRandomDirectory(String parentDir, String prefix, String suffix)
	{
		try 
		{
			File parentFile = new File(parentDir);

			if (!parentFile.exists())
				return "";

			String directoryname = prefix + generateRandomFileName(10) + suffix;
			String dirToCreate = parentDir + File.separator + directoryname;

			// generate a new directory name if the dir already exists
			while (fileExists(dirToCreate))
			{
				directoryname = prefix + generateRandomFileName(10) + suffix;
				dirToCreate = parentDir + File.separator + directoryname;
			}

			if (createDirectory(dirToCreate))
				return directoryname;
			else return "";
		}
		catch (Exception e) 
		{ 
			debugMessage(e);
			return "";
		}
	}

	/**
	 * Create a new directory.
	 *
	 * @param newDir the name of the directory to create (with full path)
	 * @return true if the directory creation was successful, or if the directory already exists.
	 */
	public static boolean createDirectory(String newDir)
	{
		File dirToCreate = new File(newDir);
		
		if (dirToCreate.exists())
			return true;

		return dirToCreate.mkdir();
	}

	/**
	 * Create a new directory.
	 *
	 * @param basedir the name of the base directory to create the new directory in (with full path)
	 * @param newdirname the name of the directory to create
	 * @return true if the directory creation was successful, or if the directory already exists.
	 *		this will return false if basedir does not exist, or is not a directory
	 */
	public static boolean createDirectory(String basedir, String newdirname)
	{
		File basedirFile = new File(basedir);

		if (! ( basedirFile.exists() && basedirFile.isDirectory()) )
			return false;

		String dirToCreate = basedir + File.separator + newdirname;
		return createDirectory(dirToCreate);
	}


	/**
	 * Write a String into a file.<BR>
	 * The file to be written should not exist in the first place
	 * (it will be deleted if it exists).
	 *
	 * @param aFile the file to write the string to 
	 * @param data the string that is to be written to the file
	 * @return true if the operation was successful, false if not
	 */
	public static boolean writeStringToFile(String aFile, String data)
	{
		return writeStringToFile( aFile, data, false );
	}

	/**
	 * Write a String into a file.<BR>
	 *
	 * @param aFile the file to write the string to 
	 * @param data the string that is to be written to the file
	 * @param append true if append write to the file
	 * @return true if the operation was successful, false if not
	 */
	public static boolean writeStringToFile(String aFile, String data, boolean append)
	{
		File fileToWrite = new File(aFile);
		boolean retVal = true;

		if ( !append && fileToWrite.exists()) {
			deleteFile(aFile);
		}

		try
		{
			FileOutputStream fos = new FileOutputStream(aFile, append);
			DataOutputStream dos = new DataOutputStream(fos);

			dos.writeBytes(data);

			dos.flush();
			dos.close();
			fos.close();
		}
		catch (Exception e) 
		{ 
			debugMessage(e + ": writing String to file " + aFile);
			retVal = false;
		}	

		return retVal;

	}

	/**
	 * Write the contents of a String array into a file.<BR>
	 * The file to be written should not exist in the first place
	 * (it will be deleted if it exists).
	 *
	 * @param aFile the file to write the string to 
	 * @param data the string that is to be written to the file
	 * @return true if the operation was successful, false if not
	 */
	public static boolean writeStringArrayToFile(String aFile, String data[])
	{
		return writeStringArrayToFile(aFile, data, false);
	}
	
	/**
	 * Write the contents of a String array into a file.<BR>
	 *
	 * @param aFile the file to write the string to 
	 * @param data the string that is to be written to the file
	 * @param append true if append write to the file
	 * @return true if the operation was successful, false if not
	 */
	public static boolean writeStringArrayToFile(String aFile, String data[], boolean append)
	{
		File fileToWrite = new File(aFile);
		boolean retVal = true;

		if (!append && fileToWrite.exists()) {
			deleteFile(aFile);
		}

		try
		{
			FileOutputStream fos = new FileOutputStream(aFile, append);
			DataOutputStream dos = new DataOutputStream(fos);

			for (int i=0; i < data.length; i++)
				dos.writeBytes(data[i]);

			dos.flush();
			dos.close();
			fos.close();
		}
		catch (Exception e) 
		{ 
			debugMessage(e + ": writing String to file " + aFile);
			retVal = false;
		}	

		return retVal;

	}

	/**
	 * Checks whether a file exists.
	 *
	 * @param aFile the file (with full path) to check for existence of
	 * @return true if the file exists, false if not
	 */
	public static boolean fileExists(String aFile)
	{
		return new File(aFile).exists();
	}


	/**
	 * Prints out a String to the debug service.
	 *
	 */
	private static void debugMessage(Error err)
	{
		debugMessage(err.toString());
	}

	/**
	 * Prints out a String to the debug service.
	 *
	 */
	private static void debugMessage(Exception exc)
	{
		debugMessage(exc.toString());
	}

	/**
	 * Prints out a String to the debug service. This could be a file, or a 
	 * a stream. Currently, it is STDOUT.
	 *
	 */
	private static void debugMessage(String msg)
	{
		System.out.println(msg);
	}


};