package pl.zalewski.mikolaj.symbdev;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.plugin.*;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import com.ice.jni.registry.NoSuchKeyException;
import com.ice.jni.registry.NoSuchValueException;
import com.ice.jni.registry.Registry;
import com.ice.jni.registry.RegistryException;
import com.ice.jni.registry.RegistryKey;

import java.io.IOException;
import java.net.URL;
import java.util.*;

/**
 * The main plugin class to be used in the desktop.
 */
public class SymbdevPlugin extends AbstractUIPlugin {
	//The shared instance.
	private static SymbdevPlugin fPlugin;
	//Resource bundle.
	private ResourceBundle fResourceBundle;
	
	/**
	 * The title of all the message box dislpayed by the plug-in
	 */
	public static final String MESSAGE_TITLE = "Eclipse";
    private static final String PLUGIN_ID = "pl.zalewski.mikolaj.symbdev";
    public static final String CPROJECT_ID = SymbdevPlugin.getUniqueIdentifier()+".symbianProject";
    public static final String BUILDER_NAME="pl.zalewski.mikolaj.symbdev.abldBuilder";
    public static final String CMD_PATH="os/win32/x86/parsablecmd.exe";
    
    public static final String SDK_DETECT_TRACE=PLUGIN_ID+"/trace/sdkdetect";
    public static final String SKIN_TRACE=PLUGIN_ID+"/trace/skin";
    public static final String MMP_TRACE=PLUGIN_ID+"/trace/mmp";

    // Trace facilities
    /**
     * Trace about SDK autodetection
     */
    public static final int TRACE_SDKDETECT = 1;
    /**
     * Trace about eumlator skins
     */
    public static final int TRACE_SKIN = 2;
    /**
     * Trace the MMP parsing/editing
     */
    public static final int TRACE_MMP = 3;
    
    // IStatus Error codes
    /**
     * No Win32 compilation tool is configures
     */
    public static final int ERROR_NO_TOOL_CONFIG = 1;
    /**
     * Error while launching build commands
     */
    public static final int ERROR_BUILD_EXEC = 2;
    /**
     * Error while parsing project template file
     */
    public static final int ERROR_TEMPLATE_PARSE = 3;
    /**
     * The launcher couldn't find the active project
     */
    public static final int ERROR_LAUNCH_NO_PROJECT = 4;
    /**
     * The current project if not a Symbian project during launch
     */
    public static final int ERROR_LAUNCH_BAD_PROJECT = 5;
    /**
     * Error launching the emulator
     */
    public static final int ERROR_LAUNCH_EXEC = 6;
    /**
     * Error in skin file
     */
    public static final int ERROR_EPOC_SKIN = 7;
    /**
     * Error rewriting epoc.ini to epoc.eclipse.ini
     */
    public static final int ERROR_EPOC_INI_REWRITE = 8;
    /**
     * A non-fatal error loading skins
     */
    public static final int WARNING_SKIN = 9;
    /**
     * Bad user input (e.g. the input is not a number but should be)
     */
    public static final int ERROR_BAD_INPUT = 10;    
    /**
     * This project has no symbian nature
     */
    public static final int ERROR_NO_SYMBIAN_NATURE = 11;
    /**
     * A non-fatal error loading templates
     */
    public static final int WARNING_TEMPLATE = 12;
    /**
     * Error accessing Win32 registry
     */
    public static final int ERROR_WIN32_REG_ACCESS = 13;
    /**
     * I/O error
     */
    public static final int ERROR_IO_ACCESS = 14;
    /**
     * I/O error. The plug-in will try to continue
     */
    public static final int WARNING_IO_ACCESS = 15;
    /**
     * Error accessing the .symbbuild file
     */
    public static final int ERROR_ACCESS_SYMBBUILD = 16;
    
    /**
     * The MMP model exceptions uses the codes 0x10000-0x20000. See
     * MMPModelException for details. 
     */
    public static final int ERROR_MODEL_BASE = 0x10000;
	
	/**
	 * The constructor.
	 */
	public SymbdevPlugin() {
		super();
		fPlugin = this;
		try {
			fResourceBundle = ResourceBundle.getBundle("pl.zalewski.mikolaj.symbdev.SymbdevPluginResources");
		} catch (MissingResourceException x) {
			fResourceBundle = null;
		}
	}

	/**
	 * This method is called upon plug-in activation
	 */
	public void start(BundleContext context) throws Exception {
		super.start(context);
		setupDebug();
	}

	/**
	 * This method is called when the plug-in is stopped
	 */
	public void stop(BundleContext context) throws Exception {
		super.stop(context);
	}
	
	/**
	 * Returns the path to a file inside the plugin
	 * @param name The path to the file relative to the plug-in root
	 * @return The system path to the plug-in
	 */
	public Path getFile(String name)
		throws IOException
	{
	    URL url=Platform.asLocalURL(SymbdevPlugin.getDefault().getBundle().getEntry(name));
	    if (!url.getProtocol().equals("file"))
	        return null;
	    String path=url.getPath();
	    
	    // this function returns the path with a '/' on the beginning
	    // e.g. "/C:/Eclipse/..."
	    if (path.indexOf(':')!=-1 && path.charAt(0)=='/')
	        path=path.substring(1);
	    return new Path(path);
	}

	/**
	 * Returns the path to a file inside the plugin
	 * @param name The path to the file relative to the plug-in root
	 * @return The system path to the plug-in
	 */
	public static Path getFile(Bundle b, String name)
		throws IOException
	{
	    URL url=Platform.asLocalURL(b.getEntry(name));
	    if (!url.getProtocol().equals("file"))
	        return null;
	    String path=url.getPath();
	    
	    // this function returns the path with a '/' on the beginning
	    // e.g. "/C:/Eclipse/..."
	    if (path.indexOf(':')!=-1 && path.charAt(0)=='/')
	        path=path.substring(1);
	    return new Path(path);
	}

	/**
	 * Returns the shared instance.
	 */
	public static SymbdevPlugin getDefault() {
		return fPlugin;
	}
	
	/**
	 * Returns the standard display to be used. The method first checks, if
	 * the thread calling this method has an associated display. If so, this
	 * display is returned. Otherwise the method returns the default display.
	 */
	public static Display getStandardDisplay() {
		Display display= Display.getCurrent();
		if (display == null) {
			display= Display.getDefault();
		}
		return display;		
	}	

	/**
	 * Shows an error dialog informing the user of the exception.
	 * The stack trae is logged to the log file
	 * @param e The exception
	 * @param desc A description of the exception or <code>null</code> if
	 * the exception message should be used
	 * @param shell The parent of the dialog or <code>null</code> if none 
	 */
	public static void showError(CoreException e, String desc, Shell s)
	{
	    SymbdevPlugin self=getDefault();
		ErrorDialog.openError(s, SymbdevPlugin.MESSAGE_TITLE,
		        desc, e.getStatus());
		self.getLog().log(new Status(IStatus.ERROR, getUniqueIdentifier(),
		        0, "Exception logged by the Symbian C++ development plug-in", e));
	}
	
	/**
	 * Creates an error Status object for this plug-in
	 * @param code The numeric code
	 * @param message The human-readable error messag 
	 * @param e The chained exception
	 * @return The status object
	 */
	public static IStatus createErrorStatus(int code, String message, Throwable e)
	{
	    return new Status(IStatus.ERROR, getUniqueIdentifier(), code,
	            message, e);
	}

	/**
	 * Creates an error Status object for this plug-in
	 * @param code The numeric code
	 * @param message The human-readable error messag 
	 * @return The status object
	 */
	public static IStatus createErrorStatus(int code, String message)
	{
	    return new Status(IStatus.ERROR, getUniqueIdentifier(), code,
	            message, null);
	}
	
	/**
	 * Writes a warning status in the log. The plug-in identifier of the status
	 * object is the Symbdev plug-in
	 * @param code The numeric warning code
	 * @param message The human-readable warning message
	 * @param t A throwable
	 */
	public static void logWarning(int code, String message, Throwable t)
	{
	    Status s=new Status(IStatus.WARNING, getUniqueIdentifier(), code,
	            message, t);
	    SymbdevPlugin.getDefault().getLog().log(s);
	}

	/**
	 * Writes a warning status in the log. The plug-in identifier of the status
	 * object is the Symbdev plug-in
	 * @param code The numeric warning code
	 * @param message The human-readable warning message
	 */
	public static void logWarning(int code, String message)
	{
	    logWarning(code, message, null);
	}
	
	public static void log(CoreException e)
	{
	    getDefault().getLog().log(e.getStatus());
	}
	
	/**
	 * @return The plugin name (pl.zalewski.mikolaj.symbdev)
	 */
	public static String getUniqueIdentifier() {
		if (getDefault() == null) {
			// If the default instance is not yet initialized,
			// return a static identifier. This identifier must
			// match the plugin id defined in plugin.xml
			return PLUGIN_ID;
		}
		return getDefault().getBundle().getSymbolicName();
	}
	
	/**
	 * Returns the string from the plugin's resource bundle,
	 * or 'key' if not found.
	 */
	public static String getResourceString(String key) {
		ResourceBundle bundle = SymbdevPlugin.getDefault().getResourceBundle();
		try {
			return (bundle != null) ? bundle.getString(key) : key;
		} catch (MissingResourceException e) {
			return key;
		}
	}
	
	static public String getHKLMRegValueString(String keyName, String valueName)
	{
	  	String path=null;
	  	try {
	  		RegistryKey key=Registry.HKEY_LOCAL_MACHINE.openSubKey(keyName);
	  		path=key.getStringValue(valueName);
	  		key.closeKey();
	  	}
	  	catch (RegistryException e)
		{
	  		if (!(e instanceof NoSuchKeyException) &&
	  			!(e instanceof NoSuchValueException))
	  			getDefault().getLog().log(createErrorStatus(
	  			        ERROR_WIN32_REG_ACCESS,
	  			        "Error accessing Win32 registry", e));
	  	    return null;
		}
	  	return path;
	}
	
	public static String[] envPropertiesToArray(Properties props)
	{
		String[] env = null;
		ArrayList envList = new ArrayList();
		Enumeration names = props.propertyNames();
		if (names != null) {
			while (names.hasMoreElements()) {
				String key = (String) names.nextElement();
				envList.add(key + "=" + props.getProperty(key)); //$NON-NLS-1$
			}
			env = (String[]) envList.toArray(new String[envList.size()]);
		}
		return env;
	}

	/**
	 * Returns the plugin's resource bundle,
	 */
	public ResourceBundle getResourceBundle() {
		return fResourceBundle;
	}
	
	public void setupDebug()
	{
		if (isDebugging()) {
			String option=Platform.getDebugOption(SDK_DETECT_TRACE);
			if (option!=null) fSdkDetectDebug=option.equalsIgnoreCase("true") ; //$NON-NLS-1$

			option=Platform.getDebugOption(SKIN_TRACE);
			if (option!=null) fSkinDebug=option.equalsIgnoreCase("true") ; //$NON-NLS-1$		

			option=Platform.getDebugOption(MMP_TRACE);
			if (option!=null) fMMPDebug=option.equalsIgnoreCase("true") ; //$NON-NLS-1$
		}	    
	}
	
	public boolean isTracing(int id)
	{
	    switch (id)
	    {
	    	case TRACE_SDKDETECT: return fSdkDetectDebug;
	    	case TRACE_SKIN: return fSkinDebug;
	    	case TRACE_MMP: return fMMPDebug;
	    	default: return false;
	    }
	}
	
	public void trace(int id, String message)
	{
	    if (isTracing(id))
	        System.out.println(message);
	}
	
	static public void traceStr(int id, String message)
	{
	    getDefault().trace(id, message);
	}
	
	boolean fSdkDetectDebug=false;
	boolean fSkinDebug=false;
	boolean fMMPDebug=false;
}
