/**
 * Created on Jul 6, 2005
 * $id:  $
 */
package org.jvnet.lookout.core.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;

import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.jvnet.lookout.core.model.SearchResultType;
import org.osgi.framework.Bundle;


public class Utilities {
	
	private final static char OPEN_ANGLE_BACKET = '<';
	private final static char CLOSE_ANGLE_BACKET = '>';
	
	private static final String[] htmlExtensions = { "/", "htm", "html"};
	private static final String[] propertiesExtensions = {"properties", "props"};
	private static final String[] pdfExtensions = {"pdf"};
	private static final String[] wordExtensions = {"doc"};
	private static final String[] excelExtensions = {"xls"};
	private static final String[] powerpointExtensions = {"ppt"};
	private static final String[] rtfExtensions = {"rtf"};
	private static final String[] xmlExtensions = {"xml"};
	private static final String[] xmlSchemaExtensions = {"xsd"};
	private static final String[] txtExtensions = {"txt"};
	private static final String[] iniExtensions = {"ini"};
	private static final String[] postscriptExtensions = {"ps"};
	private static final String[] pngExtensions = {"png"};
	private static final String[] gifExtensions = {"gif"};
	private static final String[] jpgExtensions = {"jpeg","jpg"};
	private static final String[] wmvExtensions = {"wmv"};
	private static final String[] mpegExtensions = {"mpeg"};
	private static final String[] movExtensions = {"mov"};
	private static final String[] qtExtensions = {"qt"};
	private static final String[] mpgaExtensions = {"mpga"};
	private static final String[] mp1Extensions = {"mp3"};
	private static final String[] mp2Extensions = {"mp2"};
	private static final String[] mp3Extensions = {"mp3"};
	private static final String[] m3uExtensions = {"m3u"};
	private static final String[] aviExtensions = {"avi"};
	private static final String[] wmaExtensions = {"wma"};
	private static final String[] wavExtensions = {"wav"};
	private static final String[] ramExtensions = {"ram"};
	private static final String[] midiExtensions = {"midi"};
	private static final String[] auExtensions = {"au"};
	private static final String[] aiffExtensions = {"aiff"};
	private static final String[] cdaExtensions = {"cda"};
	private static final String[] midExtensions = {"mid"};
	private static final String[] rmiExtensions = {"rmi"};
	private static final String[] bmpExtensions = {"bmp"};
	
	private static final String[] javaExtensions = {"java"};
	private static final String[] javaClassExtensions = {"class"};
	
	public static boolean isValidValue(String value) {        
		return (null!=value) && value.trim().length() > 0;
	}
	
	public static String stripHTML(String value) {
		
		if(null == value) {
			return "";
		}
		
		String newValue = value.trim();
		
		//Replace all '&gt' with '<' and '&lt' with '>'
		//newValue = newValue.replaceAll(XML_CLOSE_ANGLE_BACKET, CLOSE_ANGLE_BACKET + "");
		//newValue = newValue.replaceAll(XML_OPEN_ANGLE_BACKET, OPEN_ANGLE_BACKET + "");
		
		int indexOfOpenAngleBracket = newValue.indexOf(OPEN_ANGLE_BACKET);
		int indexOfCloseAngleBracket = newValue.indexOf(CLOSE_ANGLE_BACKET);        
		
		while ((indexOfOpenAngleBracket >= 0) && (indexOfCloseAngleBracket >= 0) ){
			
			newValue = newValue.substring(0,indexOfOpenAngleBracket) + newValue.substring(indexOfCloseAngleBracket + 1, newValue.length());
			
			indexOfOpenAngleBracket = newValue.indexOf(OPEN_ANGLE_BACKET);
			indexOfCloseAngleBracket = newValue.indexOf(CLOSE_ANGLE_BACKET);                       
		}
		
		return newValue;
	}
	
	public static SearchResultType getTypeForURL(String url, SearchResultType defaultType) {
		
		int indexOfExtensionStart = url.lastIndexOf('.') + 1;
		int urlLength = url.length();
		
		String urlExtensionPart = (url.substring(indexOfExtensionStart)).trim();
		
		//Most common case: ends with a "/"
		if(containsAnyOfTheseExtensions(urlExtensionPart, htmlExtensions)) {
			return SearchResultType.HTML;
		}
		
		//Try extensions with length 3 ...
		int length = 3;
		
		//Document extension has 3 chars
		if ((indexOfExtensionStart+length) == urlLength) {
			
			if(containsAnyOfTheseExtensions(urlExtensionPart, xmlExtensions)) {
				return SearchResultType.XML;
			} 
			if(containsAnyOfTheseExtensions(urlExtensionPart, pdfExtensions)) {
				return SearchResultType.PDF;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, wordExtensions)) {
				return SearchResultType.DOC;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, excelExtensions)) {
				return SearchResultType.XLS;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, powerpointExtensions)) {
				return SearchResultType.PPT;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, rtfExtensions)) {
				return SearchResultType.RTF;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, xmlSchemaExtensions)) {
				return SearchResultType.XSD;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, txtExtensions)) {
				return SearchResultType.TXT;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, iniExtensions)) {
				return SearchResultType.INI;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, bmpExtensions)) {
				return SearchResultType.BMP;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, gifExtensions)) {
				return SearchResultType.GIF;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, pngExtensions)) {
				return SearchResultType.PNG;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, wmvExtensions)) {
				return SearchResultType.WMV;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, movExtensions)) {
				return SearchResultType.MOV;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, mp1Extensions)) {
				return SearchResultType.MP1;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, mp2Extensions)) {
				return SearchResultType.MP2;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, mp3Extensions)) {
				return SearchResultType.MP3;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, m3uExtensions)) {
				return SearchResultType.M3U;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, aviExtensions)) {
				return SearchResultType.AVI;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, wmaExtensions)) {
				return SearchResultType.WMA;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, wavExtensions)) {
				return SearchResultType.WAV;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, ramExtensions)) {
				return SearchResultType.RAM;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, cdaExtensions)) {
				return SearchResultType.CDA;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, midExtensions)) { 
				return SearchResultType.MID;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, rmiExtensions)) { 
				return SearchResultType.RMI;
			}	
			if(containsAnyOfTheseExtensions(urlExtensionPart, jpgExtensions)) {
				return SearchResultType.JPEG;
			}
			
		} 
		
		//Try 4 now...
		length = 4;
		
		if ((indexOfExtensionStart+length) == urlLength) {
			
			if(containsAnyOfTheseExtensions(urlExtensionPart, javaExtensions)) {
				return SearchResultType.JAVA;
			} 
			
			if(containsAnyOfTheseExtensions(urlExtensionPart, jpgExtensions)) {
				return SearchResultType.JPEG;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, midiExtensions)) {
				return SearchResultType.MIDI;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, aiffExtensions)) {
				return SearchResultType.AIFF;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, mpgaExtensions)) {
				return SearchResultType.MPGA;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, mpegExtensions)) {
				return SearchResultType.MPEG;
			}			
		}
		
		length = 5;
		
		if ((indexOfExtensionStart+length) == urlLength) {
		
			if(containsAnyOfTheseExtensions(urlExtensionPart, javaClassExtensions)) {
				return SearchResultType.JAVA_CLASS;
			}
		}
		//Try 2 now...
		length = 2;
		
		//Document extension has 2 chars
		if((indexOfExtensionStart+length) == urlLength) {
			
			if(containsAnyOfTheseExtensions(urlExtensionPart, postscriptExtensions)) {
				return SearchResultType.PS;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, qtExtensions)) {
				return SearchResultType.QT;
			}
			if(containsAnyOfTheseExtensions(urlExtensionPart, auExtensions)) {
				return SearchResultType.AU;
			}			
		}
		
		if(containsAnyOfTheseExtensions(urlExtensionPart, propertiesExtensions)) {
			return SearchResultType.PROPERTIES;
		}
		//Default to Type.OTHER
		return defaultType;
	}
	
	/**
	 * 
	 * @param plugin
	 * @param exception
	 * @param severity as defined in the IStatus' severity property.
	 * @return
	 */
	public static MultiStatus createPluginStatus(Plugin plugin, String generalMessage, String detailMessage, int severity, Exception exception) {
		
		Bundle bundle = plugin.getBundle();
		String symbolicName = bundle.getSymbolicName();
		String bundleName = bundle.getHeaders().get("Bundle-Name").toString();
		String bundleVendor = bundle.getHeaders().get("Bundle-Vendor").toString();
		String bundleVersion = bundle.getHeaders().get("Bundle-Version").toString();
		
		MultiStatus pluginInfoStatus = new MultiStatus(
				symbolicName,
				severity, 
				generalMessage, 
				exception);
		
		//Vendor name
		pluginInfoStatus.add(
				createStatus(plugin, severity, "Plug-in Vendor: " + bundleVendor));
		
		//Plug-in ID
		pluginInfoStatus.add(
				createStatus(plugin, severity, "Plug-in ID: " + symbolicName));
		
		//Version
		pluginInfoStatus.add(
				createStatus(plugin, severity, "Plug-in Version: " + bundleVersion));

		//Plug-in name (user friendly name)
		pluginInfoStatus.add(
				createStatus(plugin, severity, "Plug-in Name: " + bundleName));
		
		//Add detail message if provided
		if(detailMessage != null) {
			pluginInfoStatus.add(
					createStatus(plugin, severity, detailMessage));				
		} 
		if (null != exception.getLocalizedMessage()) {
			pluginInfoStatus.add(
					createStatus(plugin, severity, exception.getLocalizedMessage()));				
			
		}
		return pluginInfoStatus;
	}
	
	public static MultiStatus createPluginStatus(Plugin plugin, String generalMessage, int severity, Exception exception) {
		return createPluginStatus(plugin, generalMessage, null, severity, exception);		
	}
	
	protected static Status createStatus(Plugin plugin, int severity, String msg) {
		return new Status(
				severity, 
				plugin.getBundle().getSymbolicName(), 
				severity, 
				msg, 
				null);
	}
	
	
	private static boolean containsAnyOfTheseExtensions(String urlExtensionPart, String[] extensions) {
		
		for (int i = 0; (i < extensions.length); i++) {
			
			String extension = extensions[i];
			if(urlExtensionPart.indexOf(extension) > -1) {
				return true;
			}            
		}
		
		return false;
	}
	
    public static boolean isValidUrl(String url) {        
        return url.startsWith("http://") || url.startsWith("file://");
    }
    
    /**
     * Returns the valid URL if the URL can be made valid and the file exists.<p>
     * Return null if the URL can be made valid but the file does not exist. <p>
     * 
     * @param rawUrl the URL to be made valid.
     * @return
     * @throws MalformedURLException if the URL cannot be made valid.
     */
    public static String makeUrlValid(String rawUrl) throws MalformedURLException {
        String urlWithProtocol = null;
        
        //We only address the lack of the file protocol...
        try {
            File file = new File(rawUrl);
            
            if(file.exists()) {
                urlWithProtocol = file.toURL().toString();
            }
        } catch (Exception e) {
        	//CXB_EXTERNALIZE
        	throw new MalformedURLException(
        			"Could not open URL: '" + 
        			rawUrl + 
        			"' with with this exception:\n" + 
        			e.getLocalizedMessage());            
        } 
        
        return urlWithProtocol;
    }
    
    /**
     * Executes a system command and returns the output of that command. If the 
     * command does not properly execute, a null string is returned. If the command 
     * executes correctly but there is no output, am empty String is returned.
     *    
     * @param command
     * @return see description.
     */
    public static String executeSystemCommand(String command) {
		String output = "";
		
		Runtime runtime = Runtime.getRuntime();
		
		try {
			Process process = runtime.exec(command);			
			BufferedInputStream bis = new BufferedInputStream(process.getInputStream());
			StringBuffer buffer = new StringBuffer();
			byte[] dataArray = new byte[500];
			int sizeRead = bis.read(dataArray);			
			while(sizeRead != -1) {
				//Convert to String
				String tempString = new String(dataArray, 0, sizeRead);

				//Save...
				buffer.append(tempString);
				
				//Read more...
				sizeRead = bis.read(dataArray);
			}
			output = buffer.toString();			
		} catch (Exception e) {
			output = null;
		}		
		return output;
	}
    
    public static void saveSearchCorePreferences() {
		org.jvnet.lookout.core.Plugin.getDefault().savePluginPreferences();
    }
        
	// Gregorian Calendar adopted Oct. 15, 1582 (2299161)
	private static int JGREG = 15 + 31*(10+12*1582);

	/**
	 * Returns the Julian day number that begins at noon of
	 * this day, Positive year signifies A.D., negative year B.C.
	 * Remember that the year after 1 B.C. was 1 A.D.
	 *
	 * ref :
	 *  Numerical Recipes in C, 2nd ed., Cambridge University Press 1992
	 * <p> from: http://www.rgagnon.com/javadetails/java-0506.html
	 */
	public static int getJulianDate(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int year = cal.get(Calendar.YEAR);
		
		//This is neeed to adjust the index difference. See Calendar.JANUARY 
		int month = cal.get(Calendar.MONTH) + 1;
		
		int day = cal.get(Calendar.DAY_OF_MONTH);
		
		int julianYear = year;
		if (year < 0) julianYear++;
		int julianMonth = month;
		if (month > 2) {
			julianMonth++;
		}
		else {
			julianYear--;
			julianMonth += 13;
		}
		
		double julian = (java.lang.Math.floor(365.25 * julianYear) 
				+ java.lang.Math.floor(30.6001*julianMonth) + day + 1720995.0);
		
		if (day + 31 * (month + 12 * year) >= JGREG) {
			// change over to Gregorian calendar
			int ja = (int)(0.01 * julianYear);
			julian += 2 - ja + (0.25 * ja);
		}
		return (int)Math.floor(julian);
	}
	
	/**
	 * Converts a Julian day to a calendar date
	 * ref :
	 * Numerical Recipes in C, 2nd ed., Cambridge University Press 1992
	 * <p>
	 * from http://www.rgagnon.com/javadetails/java-0506.html
	 */
	public static Date getDate(int julianDate) {
		
		int jalpha,ja,jb,jc,jd,je,year,month,day;
		
		ja = (int) julianDate;
		if (ja>= JGREG) {    
			jalpha = (int) (((ja - 1867216) - 0.25) / 36524.25);
			ja = ja + 1 + jalpha - jalpha / 4;
		}
		
		jb = ja + 1524;
		jc = (int) (6680.0 + ((jb - 2439870) - 122.1) / 365.25);
		jd = 365 * jc + jc / 4;
		je = (int) ((jb - jd) / 30.6001);
		day = jb - jd - (int) (30.6001 * je);
		month = je - 1;
		if (month > 12) month = month - 12;
		year = jc - 4715;
		if (month > 2) year--;
		if (year <= 0) year--;
		
		Calendar cal = Calendar.getInstance();
		
		cal.set(Calendar.YEAR, year);
		
		//This is neeed to adjust the index difference. See Calendar.JANUARY 
		cal.set(Calendar.MONTH, month-1);
		cal.set(Calendar.DAY_OF_MONTH, day);
		
		return cal.getTime();
	}
	
	public static Date getZeroDate() {
		return getDate(0);
	}

	public static double getNowInJulian() {
		return getJulianDate(new Date());
	}

	public static String createString(List<String> items, String separator) {

		StringBuffer concatnatedList = new StringBuffer();
		
		int size = items.size();
		for (int i = 0; i < size; i++) {
			String token = items.get(i);
			concatnatedList.append(token);
			
			if(i+1 < size) {
				concatnatedList.append(separator);
			}
		}
		return concatnatedList.toString();
	}

	public static List<String> createList(String stringList, String separator) {
		
		StringTokenizer st = new StringTokenizer(stringList, separator);
		
		List<String> tokens = new ArrayList<String>(st.countTokens());
		
		while (st.hasMoreElements()) {
			tokens.add(st.nextToken());
		}
		return tokens;
	}
	
    public static boolean containsInList(String stringList, String stringToLookFor, String separator) {
    	return createList(stringList, separator).contains(stringToLookFor);    	
    }

    public static String removeFromList(String stringList, String stringToRemove, String separator) {
    	List<String> list = createList(stringList, separator);
    	list.remove(stringToRemove);
    	return createString(list, separator);
    }

    public static String addToList(String stringList, String stringToAdd, String separator) {
    	List<String> list = createList(stringList, separator);
    	list.add(stringToAdd);
    	return createString(list, separator);
    }

}
