/*

 * Created on Aug 5, 2003

 *

 * To change the template for this generated file go to

 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments

 */

package com.isogen.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;

/**
 * @author adrian
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */

public class FileUtil {

	public static void copyDirectory(File sourceDir, File destDir) throws IOException {
		copyDirectory(sourceDir, destDir, false);
	}
	
	public static void copyDirectory(File sourceDir, File destDir, boolean ignore) throws IOException {
		copyDirectory(sourceDir, destDir, ignore, null);
	}
	
	private static void copyDirectory(File sourceDir, File destDir, boolean ignore, IgnoreFilter ignoreFilter) throws IOException {
		if(ignoreFilter == null) {
			ignoreFilter = new IgnoreFilter();	
		}
		
		if(!destDir.exists()) {
			destDir.mkdirs();
		}
		File[] contents = null;
		
		if(ignore) {
			contents = sourceDir.listFiles(ignoreFilter);
		} else {
			contents = sourceDir.listFiles();
		}
		
		if (contents == null) {
			return;
		}
		
		for(int i = 0; i < contents.length ; i++) {
			if(contents[i].isDirectory()) {
				File destChildDir = new File(destDir, contents[i].getName());
				if(!destChildDir.exists()) {
					destChildDir.mkdir();
				}
				copyDirectory(contents[i], destChildDir, ignore, ignoreFilter);
			} else {
				copyUpdatedFile(contents[i], destDir);
			}
		}
	}
	
	public static void copyUpdatedFile(File sourceFile, File destDir) throws IOException {
		File destChildFile = new File(destDir, sourceFile.getName());
		if(!destChildFile.exists() || destChildFile.lastModified() < sourceFile.lastModified()) {
			BufferedInputStream in = new BufferedInputStream(new FileInputStream(sourceFile));
			BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destChildFile));
			
			byte[] bytes = new byte[1024];
			while(in.available() > 0) {
				int bytesRead = in.read(bytes);
				out.write(bytes, 0, bytesRead);
			}
			
			out.flush();
			out.close();
			in.close();
		}
	}


	/**
	 * Copies a file from one location to another.
	 * @param source The source file.
	 * @param dest The destination file.
	 * @throws IOException Thrown if there is any error reading or writing the files.
	 */
	public static void copyFile(File source, File dest) throws IOException {
		copyFile(source, dest, true);
	}
	
	/**
	 * Copy one file to another file.
	 * 
	 * @param source The file to be copied.
	 * @param dest The file to copy to
	 * @param quiet If true, do not print the copying message.
	 * @throws IOException
	 */
	public static void copyFile(File source, File dest, boolean quiet) throws IOException {
		if (!quiet) {
			System.err.println("Copying " + source.getPath() + 
			                   " to " + dest.getPath() + "...");
		}
		FileInputStream in = new FileInputStream(source);
		FileOutputStream out = new FileOutputStream(dest);
		int c;

		while ((c = in.read()) != -1)
			out.write(c);
		in.close();
		out.close();
		
	}

	/**
	 * Copy an input stream to a file.
	 * 
	 * @param source The InputStream to be copied.
	 * @param dest The file to copy to
	 * @throws IOException
	 */
	public static void copyFile(InputStream source, File dest) throws IOException {
		copyFile(source, dest, true);
	}

	/**
	 * Copy an input stream to a file.
	 * 
	 * @param source The InputStream to be copied.
	 * @param dest The file to copy to
	 * @param quiet If true, do not print the copying message.
	 * @throws IOException
	 */
	public static void copyFile(InputStream source, File dest, boolean quiet) throws IOException {
		if (!quiet) {
			System.err.println("Copying input stream "  + 
							   " to " + dest.getPath() + "...");
		}
		InputStream in = source;
		FileOutputStream out = new FileOutputStream(dest);
		int c;

		while ((c = in.read()) != -1)
			out.write(c);
		in.close();
		out.close();
		
	}

	public static void deleteDirectory(File dir) {
		File[] contents = dir.listFiles();
		for(int i = 0; i < contents.length ; i++) {
			if(contents[i].isDirectory()) {
				deleteDirectory(contents[i]);
			} else {
				contents[i].delete();
			}
		}
		dir.delete();
	}
	
	/**
	 * Given a filePath, returns the directory part.
	 * @param filePath The path to get the directory part of.
	 * @return The directory.
	 */
	public static String getFileDir(String filePath) {
		String resultDir = "";
		if (!filePath.substring(1,2).equals(":") & 
		    !filePath.substring(1,2).equals(".")) {
			resultDir = File.separator; // Must not have a "c:" bit
		}
		StringTokenizer pathSt = new StringTokenizer(filePath, "/\\");
		File f= new File(filePath);
		if (f.isDirectory()) {
			return filePath;
		}
		int n = pathSt.countTokens();
		if (n > 1) {
			resultDir = resultDir + pathSt.nextToken();
			for (int i = 1; i < n-1; i++) {
				resultDir = resultDir + File.separator + pathSt.nextToken();
			}
		}
		return resultDir;
	}

	/**
	 * Given a filePath, returns the filename part.
	 * @param filePath The path to get the filename part of.
	 * @return The filename
	 */
	public static String getFileName(String filePath) {
		if (filePath.equals("")) {
			return ""; // Not sure what else to do here. Don't really want to throw an exception.
		}
		String resultFn = "";
		File f= new File(filePath);
		if (f.isDirectory()) {
			return resultFn;
		}

		StringTokenizer pathSt = new StringTokenizer(filePath, "/\\");
		int n = pathSt.countTokens();
		if (n > 1) {
			for (int i = 1; i < n; i++) {
				pathSt.nextToken();
			}
		}
		String fileName = pathSt.nextToken(); 
		return fileName;
	}

	/**
	 * Given a filePath, returns the extension part.
	 * @param filePath The path to get the extension part of.
	 * @return The extension, without the leading "."
	 */
	public static String getFileExtension(String filePath) {
		String fileName = getFileName(filePath);
		if (fileName.indexOf(".") == -1) {
			return "";
		}
		StringTokenizer extSt = new StringTokenizer(fileName, ".");
		int n = extSt.countTokens();
		if (n > 1) {
			for (int i = 0; i < n-1; i++) {
				extSt.nextToken();
			}
		}
		if (extSt.hasMoreTokens()) {
			return extSt.nextToken();
		}
		return "";
	}

	/**
	 * Given a filePath, returns the filename minus the extension, e.g., if
	 * the input filename "/a/b/c/foo.bar" returns "foo".
	 * @param filePath The path to get the base part of.
	 * @return The filename, without the extension.
	 */
	public static String getFileBaseName(String filePath) {
		String fileName = getFileName(filePath);
		if (fileName.indexOf(".") <= 0) {
			return fileName; // Return whole name if it starts with ".", e.e., ".cvsroot"
		}
		StringTokenizer baseSt = new StringTokenizer(fileName, ".");
		int n = baseSt.countTokens();
		// If we're here, there must be at least two tokens.
		for (int i = 0; i < n-2; i++) {
			baseSt.nextToken();
		}
		if (baseSt.hasMoreTokens()) {
			return baseSt.nextToken();
		}
		return "";
	}

	/**
	 * Returns the relative from targetPath to filePath.
	 * @param filePath The path the result is relative to. Must be an absolute path and must
	 * include the filename part.
	 * @param targetPath The path the result is derived from. Must be an absolute path, 
	 * otherwise it is returned immediately.
	 * @return The relative path, if any. 
	 */
	public static String getRelativePath(String filePath, String targetPath) {
		String relative_path = "";
		if (targetPath.substring(0, 2).equals("..")) {
			return targetPath;
		}
		if (targetPath.indexOf("/") < 0 && 
		    targetPath.indexOf("\\") < 0) {
			return targetPath;
		}
		if (filePath.endsWith("\\") || filePath.endsWith("/")) {
			filePath += "fake.file"; // Have to have a trailing token for the algorithm to work.
		}

		// if win platform, to_lower case, assume always win
		//local fp = tolower(filePath)
		// local tp = tolower(targetPath)
		//local fArr[], tArr[]
		//local nf, nt, n, i, j, relative_path
		//nf = split(fp, fArr, "/\\") - 1 ; #not counting file name
		//nt = split(tp, tArr, "/\\") - 1
		StringTokenizer fpst = new StringTokenizer(filePath, "/\\");
		StringTokenizer tpst = new StringTokenizer(targetPath, "/\\");
		int nf = fpst.countTokens() - 1; 
		int nt = tpst.countTokens() - 1;
		int n = nf;
 		
		if ( nf > nt ) {
			n = nt;
		}

		int i = 0;
		// Count the number of common ancestors.
		String ttoken = ""; // We need this later 
		while (i <= n) {
			String ftoken = fpst.nextToken();
			ttoken = tpst.nextToken();
			if (ftoken.equals(ttoken)) {
				i++;
				continue;
			}
			else {
				if (i == 0) {
					// nothing in common, return full path
					return targetPath;
				}
				else {
					break;
				}
			}
		}
		// At this point, i == the number of
		// common initial path components.
		
		// Now we need to go up from the filePath (where we will be relative
		// to) for each component in filePath after the common components
		// At this point  
		int j;
		int unCommonCount = nf - i; 		
		if (unCommonCount > 0) {
			for (j=1; j <= unCommonCount; j++) {
				relative_path = relative_path + "../";
			}
		}
		relative_path = relative_path + ttoken;
		while (tpst.hasMoreTokens()) {
			relative_path = relative_path + "/" +  tpst.nextToken();
		}

		return relative_path;
	}
	
	public static void printFileBytes(String fn, int startByte, int length) throws IOException {
		File f = new File(fn);
		BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
		if (startByte > 0) {
			in.skip(startByte - 1);
		}
		StringBuffer buf = new StringBuffer();
		for (int i = startByte; i < startByte + length; i++) {
			byte b = (byte)(in.read());
			System.out.print(byteToHex(b) + " ");
			if (b >= 0 && b < 0xFF) {
				buf.append((char)b);
				buf.append("  ");
			} else {
				buf.append("   ");
			}
		}
		System.out.print("\n");
		System.out.println(buf.toString());
		in.close();
	}
	
	public static void printFileBytes(String fn) throws IOException {
		printFileBytes(fn, 0, 10);
	}
	
	static public String byteToHex(byte b) {
		 // Returns hex String representation of byte b
	 char hexDigit[] = {
		'0', '1', '2', '3', '4', '5', '6', '7',
		'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
		 };
		 char[] array = { hexDigit[(b >> 4) & 0x0f], hexDigit[b & 0x0f] };
		 return new String(array);
	}

    static public String charToHex(char c) {
	   // Returns hex String representation of char c
	   byte hi = (byte) (c >>> 8);
	   byte lo = (byte) (c & 0xff);
	   return byteToHex(hi) + byteToHex(lo);
    }	
    
	/**
	 * Given a base path (e.g., input source file path) and a path relative to it,
	 * returns an operating-system-specific absolute path formed by combining the base part
	 * with the relative part.
	 * @param basePath
	 * @param relativePath
	 * @return Absolute path string. Returns "" if there is a problem constructing the path.
	 */
	public static String getAbsolutePath(String basePath, String relativePath) {
		String absPath = "";
 		File baseDir = null;
		
		if (basePath.indexOf(File.separator) > 0) {
			baseDir = new File(basePath);
		} else {
			if (basePath.indexOf("/") > 0) {
				baseDir = new File(basePath.replace('/', File.separatorChar));
			} else {
				baseDir = new File(basePath);
			}
		}
		if (baseDir.isFile()) {
			baseDir = baseDir.getParentFile();
		}
		if (relativePath.indexOf(File.separator) > 0) {
			absPath = new File(baseDir, relativePath).getAbsolutePath();
		} else {
			String tempPath = "";
			if (basePath.indexOf("/") > 0) {
				tempPath = relativePath.replace('/', File.separatorChar);
			} else {
				tempPath = relativePath;
			}
			absPath = new File(baseDir, tempPath).getAbsolutePath();
		}
		return absPath;
	}
	
    
} 

