package com.limegroup.gnutella.util;

import java.util.Properties;
import java.io.*;
import java.net.*;
import java.util.Locale;

/**
 * This class has been modified extensively for Acquisition.
 * David Watanabe
 * 
 * AcqLite0.1: modified path, useragent, etc.
 * 
 */

public final class CommonUtils 
{
    /**
     * The vendor code for QHD and GWebCache.  WARNING: to avoid character
     * encoding problems, this is hard-coded in QueryReply as well.  So if you
     * change this, you must change QueryReply.
     */
    public static final String QHD_VENDOR_NAME = "ACQL";

    /**
     * Constant for the java system properties.
     */
    private static final Properties PROPS = System.getProperties();

    /**
     * Cached constant for the HTTP Server: header value.
     */
    private static final String VERSION = "0.23";
    private static final String HTTP_SERVER = "LimeWire(AcqLite)/" + VERSION;

    public static String getVendor() {
        return HTTP_SERVER;
    }    

    public static String getHttpServer() {
        return HTTP_SERVER;
    }

    public static String getJavaVersion() {
        return PROPS.getProperty("java.version");
    }

    public static String getOS() {
        return PROPS.getProperty("os.name");
    }

    public static File getCurrentDirectory() {
        return new File(PROPS.getProperty("user.dir"));
    }

    public static boolean isUltrapeerOS() {
        return true;
    }
    
    public static int getUPMajorVersionNumber() {
        return 0;
    }

    public static int getUPMinorVersionNumber() {
        return 1;
    }

    public static int getGUESSMajorVersionNumber() {    
        return 0;
    }
    
    public static int getGUESSMinorVersionNumber() {
        return 1;
    }

    public static int getMajorVersionNumber() {    
        return 0;
    }
    
    public static int getMinorVersionNumber() {
        return 7;
    }

    public static boolean isJava14OrLater() {
        String version=CommonUtils.getJavaVersion();
        return !version.startsWith("1.3") 
            && !version.startsWith("1.2") 
            && !version.startsWith("1.1")  
            && !version.startsWith("1.0"); 
    }	

    /** 
     * Attempts to copy the first 'amount' bytes of file 'src' to 'dst',
     * returning the number of bytes actually copied.  If 'dst' already exists,
     * the copy may or may not succeed.
     * 
     * @param src the source file to copy
     * @param amount the amount of src to copy, in bytes
     * @param dst the place to copy the file
     * @return the number of bytes actually copied.  Returns 'amount' if the
     *  entire requested range was copied.
     */
    public static int copy(File src, int amount, File dst) {
        final int BUFFER_SIZE=1024;
        int amountToRead=amount;
        InputStream in=null;
        OutputStream out=null;
        try {
            //I'm not sure whether buffering is needed here.  It can't hurt.
            in=new BufferedInputStream(new FileInputStream(src));
            out=new BufferedOutputStream(new FileOutputStream(dst));
            byte[] buf=new byte[BUFFER_SIZE];
            while (amountToRead>0) {
                int read=in.read(buf, 0, Math.min(BUFFER_SIZE, amountToRead));
                if (read==-1)
                    break;
                amountToRead-=read;
                out.write(buf, 0, read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in!=null)
                try { in.close(); } catch (IOException e) { }
            if (out!=null) {
                try { out.flush(); } catch (IOException e) { }
                try { out.close(); } catch (IOException e) { }
            }
        }
        return amount-amountToRead;
    }

    /** 
     * Copies the file 'src' to 'dst', returning true iff the copy succeeded.
     * If 'dst' already exists, the copy may or may not succeed.  May also
     * fail for VERY large source files.
     */
    public static boolean copy(File src, File dst) {
        //Downcasting length can result in a sign change, causing
        //copy(File,int,File) to terminate immediately.
        long length=src.length();
        return copy(src, (int)length, dst)==length;
    }
    
    public static File getUserHomeDir() {
        return new File(PROPS.getProperty("user.home"));
    }
    
    static File SETTINGS_DIRECTORY = new File(System.getProperty("user.home") + "/Library/Application Support/AcqLite/");

    public synchronized static File getUserSettingsDir() {
        return SETTINGS_DIRECTORY;
    }
    
    public static String getLimeWireVersion() {
        return VERSION;
    }
    
    public static boolean isMacClassic() {
        return false;
    }

    public static boolean isMacOSX() {
        return true;
    }

    /**
     * Old metadata definitions must be moved from ./lib/xml/data/*.*
     * This is done like the windows files copying, but for all files
     * in the data directory.
     */
    /*
    private synchronized static void moveXMLFiles(File settingsDir) {
        if(_xmlFilesMoved) return;
        // We must extend the currentDir & settingsDir to look 
        // in the right places (lib/xml/data & xml/data).
        File currentDir = new File( 
            CommonUtils.getCurrentDirectory().getPath() + "/lib/xml/data"
        );
        settingsDir = new File(settingsDir.getPath() + "/xml/data");
        settingsDir.mkdirs();
        String[] filesToMove = currentDir.list();
        if ( filesToMove != null ) {
            for(int i=0; i<filesToMove.length; i++) {
                File curUserFile = new File(settingsDir, filesToMove[i]);
                File curDirFile  = new File(currentDir,  filesToMove[i]);
                
                // if the file already exists in the user's home directory,
                // don't copy it
                if(curUserFile.isFile()) {
                    continue;
                }
                copy(curDirFile, curUserFile);
            }
        }
        _xmlFilesMoved = true;
    }
    */
		
	/**
	 * Convenience method to generically compare any two comparable
	 * things.
     *
	 * Handles comparison uniquely for 'native' types.
	 * This is for a few reasons:
	 * 1) We want to compare strings by lowercase comparison
	 * 2) Java 1.1.8 did not have native types implement Comparable
	 * Note that we check for both java.lang.Comparable and
	 * com.sun.java.util.collections.Comparable,
	 * and we do this _before_ checking for native types.
	 * So, this is slightly optimized for more recent JVMs
	 * Note that non-integer comparisons must specifically
	 * check if the difference is less or greater than 0
	 * so that rounding won't be wrong.
	 * Of the native types, we check 'Integer' first since
	 * that's the most common, Boolean,
	 * then Double or Float, and finally, the rest will be caught in
	 * 'Number', which just uses an int comparison.
	 */
    /*
    public static int compare(Object o1, Object o2) {
        int retval;
        
        if ( o1 == null && o2 == null ) {
            retval = 0;
        } else if ( o1 == null ) {
            retval = -1;
        } else if ( o2 == null ) {
            retval = 1;
        } else if ( o1.getClass() == String.class ) {
            retval = StringUtils.compareIgnoreCase( (String)o1, (String)o2 );
        } else if( o1 instanceof com.sun.java.util.collections.Comparable ) {
            retval =
                ((com.sun.java.util.collections.Comparable)o1).compareTo(o2);
        } else if( o1 instanceof Integer ) {
            retval = ((Integer)o1).intValue() - ((Integer)o2).intValue();
        } else if( o1 instanceof Boolean ) {
            retval = o1.equals(o2) ? 0 : o1.equals(Boolean.TRUE) ? 1 : -1;
        } else if( o1 instanceof Double || o1 instanceof Float ) {
            double dbl = 
                ((Number)o1).doubleValue() - ((Number)o2).doubleValue();
            if ( dbl > 0 ) retval = 1;
            else if ( dbl < 0 ) retval = -1;
            else retval = 0;
        } else if( o1 instanceof Number ) {
            retval = ((Number)o1).intValue() - ((Number)o2).intValue();
        } else {
            retval = 0;
        }
        return retval;
    }
    */
    
    public static boolean isWindows() {
        return false;
    }
    
    public static boolean supportsTray() {
        return false;
    }

    public static boolean isJava13OrLater() {
        return true;
    }	

    public static boolean isJava118() {
        return false;
    }	

    public static String getUserName() {
        return PROPS.getProperty("user.name");
    }

    /**
     * Several arrays of illegal characters on various operating systems.
     * Used by convertFileName
     */
    private static final char[] ILLEGAL_CHARS_ANY_OS = {
		'/', '\n', '\r', '\t', '\0', '\f' 
	};
    private static final char[] ILLEGAL_CHARS_UNIX = {'`'};
    private static final char[] ILLEGAL_CHARS_WINDOWS = { 
		'?', '*', '\\', '<', '>', '|', '\"', ':'
	};

    /** 
     * Replaces OS specific illegal characters from any filename with '_', 
	 * including ( / \n \r \t ) on all operating systems, ( ? * \  < > | " ) 
	 * on Windows, ( ` ) on unix.
     *
     * @param name the filename to check for illegal characters
     * @return String containing the cleaned filename
     */
    public static String convertFileName(String name) {
		
		// if the name is too long, reduce it.  We don't go all the way
		// up to 256 because we don't know how long the directory name is
		// We want to keep the extension, though.
		if(name.length() > 180) {
		    int extStart = name.lastIndexOf('.');
		    if ( extStart == -1) { // no extension, wierd, but possible
		        name = name.substring(0, 180);
		    } else {
		        // if extension is greater than 11, we concat it.
		        // ( 11 = '.' + 10 extension characters )
		        int extLength = name.length() - extStart;		        
		        int extEnd = extLength > 11 ? extStart + 11 : name.length();
			    name = name.substring(0, 180 - extLength) +
			           name.substring(extStart, extEnd);
            }          
		}
        for (int i = 0; i < ILLEGAL_CHARS_ANY_OS.length; i++) 
            name = name.replace(ILLEGAL_CHARS_ANY_OS[i], '_');
		
        if ( isWindows() ) {
            for (int i = 0; i < ILLEGAL_CHARS_WINDOWS.length; i++) 
                name = name.replace(ILLEGAL_CHARS_WINDOWS[i], '_');
        /* davew
        } else if ( _isLinux || _isSolaris ) {
            for (int i = 0; i < ILLEGAL_CHARS_UNIX.length; i++) 
                name = name.replace(ILLEGAL_CHARS_UNIX[i], '_');
        */
        }
        return name;
    }
}