//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.common.plugin;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Platform;
import org.eclipse.epf.common.serviceability.Logger;
import org.eclipse.epf.common.serviceability.MsgDialog;
import org.eclipse.epf.common.utils.FileUtil;
import org.eclipse.epf.common.utils.I18nUtil;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

/**
 * The abstract base class for all plug-ins.
 * 
 * @author Kelvin Low
 * @author Jinhua Xi
 * @since 1.0
 */
public abstract class AbstractPlugin extends AbstractUIPlugin {

	// The relative path to the icons.
	private static final String ICON_PATH = "icons/"; //$NON-NLS-1$;

	// The resource bundle for this plug-in.
	private ResourceBundle resourceBundle;

	// This plug-in ID.
	private String pluginId;

	// The plug-in install URL.
	private URL installURL;

	// The plug-in install path.
	private String installPath;

	// The plug-in icon URL.
	private URL iconURL;

	// The logger hash map.
	private static Map loggers = new HashMap();

	// The message dialog hash map.
	private static Map msgDialogs = new HashMap();

	// The shared image hash map.
	private static Map sharedImages = new HashMap();

	/**
	 * Default constructor.
	 */
	public AbstractPlugin() {
		super();
	}

	/**
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(BundleContext)
	 */
	public void start(BundleContext context) throws Exception {
		super.start(context);

		init(context);

		if (isDebugging()) {
			getLogger().logInfo("Started " + pluginId); //$NON-NLS-1$			
		}
	}

	/**
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(BundleContext)
	 */
	public void stop(BundleContext context) throws Exception {
		// Free the shared images.
		for (Iterator images = sharedImages.values().iterator(); images
				.hasNext();) {
			Image image = (Image) images.next();
			if (image != null && !image.isDisposed()) {
				image.dispose();
			}
		}

		super.stop(context);

		if (isDebugging()) {
			getLogger().logInfo("Stopped " + pluginId); //$NON-NLS-1$
		}
	}

	/**
	 * Initializes this plug-in.
	 * 
	 * @param context
	 *            The bundle context.
	 */
	protected void init(BundleContext context) throws Exception {
		// Get the bundle for this plug-in.
		Bundle bundle = getBundle();

		// Get the resouce bundle for this plug-in.
		resourceBundle = Platform.getResourceBundle(bundle);

		// Get the ID for this plug-in.
		pluginId = bundle.getSymbolicName();

		if (isDebugging()) {
			getLogger().logInfo("Initializing " + pluginId); //$NON-NLS-1$		
		}

		// Get the install path of this plug-in.
		installURL = bundle.getEntry("/"); //$NON-NLS-1$

		try {
			installPath = FileLocator.resolve(installURL).getPath();
		} catch (IOException e) {
			installPath = Platform.getInstallLocation().getURL().getPath();
		}

		try {
			iconURL = new URL(installURL, ICON_PATH);
		} catch (IOException e) {
		}

		if (isDebugging()) {
			getLogger().logInfo(
					"Initialized " + pluginId + ", installPath=" + installPath); //$NON-NLS-1$ //$NON-NLS-2$
		}
	}

	/**
	 * Returns the ID of this plug-in.
	 * 
	 * @return The ID of this plug-in.
	 */
	public String getId() {
		return pluginId;
	}

	/**
	 * Returns the install URL of this plug-in.
	 * 
	 * @param The
	 *            install URL of this plug-in.
	 */
	public URL getInstallURL() {
		return installURL;
	}

	/**
	 * Returns the install path of this plug-in.
	 * 
	 * @param The
	 *            install path of this plug-in.
	 */
	public String getInstallPath() {
		return installPath;
	}

	/**
	 * Loads and returns the localized properties of a Java properties file.
	 * <p>
	 * 
	 * @param path
	 *            The properties file path relative to the plug-in root.
	 * @return A <code>Properties</code> object.
	 */
	public Properties getProperties(String path) throws IOException {
		Properties props = new Properties();
		if (path == null) {
			return props;
		}
		
		String filePath = getLocalizedFile(path, true);
		
//		String filePath = null;
//		String fileName = FileUtil.getFileName(path);
//		int index = path.lastIndexOf(fileName);
//		String pathName = path.substring(0, index);
//
//		Locale locale = Locale.getDefault();
//		Bundle bundle = getBundle();
//		Bundle[] bundles = Platform.getFragments(bundle);
//		if (bundles != null) {
//			for (int i = 0; i < bundles.length; i++) {
//				URL entry = bundles[i].getEntry(pathName);
//				if (entry != null) {
//					URL url = Platform.resolve(entry);
//					filePath = I18nUtil.getLocalizedFile(url.getPath() + fileName, locale); //$NON-NLS-1$
//					if (filePath != null) {
//						break;
//					}
//				}
//			}
//		}
//
//		if (filePath == null) {
//			URL entry = bundle.getEntry(path);
//			if (entry != null) {
//				URL url = Platform.resolve(entry);
//				filePath = I18nUtil.getLocalizedFile(url.getPath(), locale);
//				if (filePath == null) {
//					filePath = url.getPath();
//				}
//			}
//		}

		if (filePath != null) {
			props.load(new FileInputStream(filePath));
		}

		return props;
	}

	/**
	 * get the locale specific absolute file path name of the given file in the plugin.
	 * @param path The properties file path relative to the plug-in root.
	 * @return String the locale specific absolute file path name of the given file.
	 * @throws IOException
	 */
	public String getLocalizedFile(String path, boolean useDefault) throws IOException
	{
		String filePath = null;
		String fileName = FileUtil.getFileName(path);
		int index = path.lastIndexOf(fileName);
		String pathName = path.substring(0, index);

		Locale locale = Locale.getDefault();
		
		Bundle bundle = getBundle();
		Bundle[] bundles = Platform.getFragments(bundle);
		if (bundles != null) {
			for (int i = 0; i < bundles.length; i++) {
				URL entry = bundles[i].getEntry(pathName);
				if (entry != null) {
					URL url = FileLocator.resolve(entry);
					filePath = I18nUtil.getLocalizedFile(url.getPath() + fileName, locale); //$NON-NLS-1$
					if (filePath != null) {
						break;
					}
				}
			}
		}

		if (filePath == null) {
			URL entry = bundle.getEntry(path);
			if (entry != null) {
				URL url = FileLocator.resolve(entry);
				filePath = I18nUtil.getLocalizedFile(url.getPath(), locale);
				if (filePath == null && useDefault) {
					filePath = url.getPath();
				}
			}
		}

		return filePath;
	}
	
//	/**
//	 * get the localized files for the files in the specified folder. The folder is a relative path to the plugin.
//	 * returns a map of the files (in ralitive path to the plugin) to their localized files in absolute path.
//	 * @param folder String path relative to the current plugin
//	 * @param recursive boolean if true recurisely get all files in sub folders
//	 * @return Map a map of the files (in ralitive path to the plugin) to their localized files in absolute path.
//	 * @throws IOException
//	 */
//	public Map getLocalizedFiles(String folder, boolean recursive) throws IOException
//	{
//		Map fileMap = new HashMap();
//		
//		String pluginPath = getInstallPath();
//		
//		List files = new ArrayList();
//		File f = new File(pluginPath, folder);
//		FileUtil.getAllFiles(f, files, true);
//
//		// for each file found in the specified folder, get the localized file
//		// put an antry of the original file and the localized file
//		URI pluginUri = new File(pluginPath).toURI();
//		
//		for ( Iterator it = files.iterator(); it.hasNext(); )
//		{
//			File src = (File)it.next();
//			String relPath = pluginUri.relativize(src.toURI()).getPath();
//			
//			// only get the locale specific file, don't include the default one
//			String localizedFile = getLocalizedFile(relPath, false);
//			if ( localizedFile != null )
//			{
//				fileMap.put(relPath, localizedFile);
//			}
//		}
//		
//		return fileMap;
//	}
	
	/**
	 * for the given path in the plugin, find the localized files form the nl fragemenets 
	 * and copy the localized files to the destination folder
	 * @param path String a relative path to the plugin root. 
	 * The files in this folder will be iterated and their localized files will be copied over
	 * @param toDir FIle the destination folder 
	 * @param recursive boolean recurively looking for files int the specified folder
	 * @param useLocaleFileName boolean if true the locale specific file names will be used in the copied destination, 
	 * otherwise, the locale specific file name will be renamed to the default one in the destination folder
	 * @throws IOException
	 */
	public void copyLocalizedFiles(String path, File toDir, boolean recursive, boolean useLocaleFileName) throws IOException
	{
		String pluginPath = getInstallPath();
		URI pluginUri = new File(pluginPath).toURI();
	    URI pathUri = new File(pluginPath, path).toURI();

		List files = new ArrayList();
		File f = new File(pluginPath, path);
		FileUtil.getAllFiles(f, files, recursive);

		// for each file found in the specified folder, get the localized file
		for ( Iterator it = files.iterator(); it.hasNext(); )
		{
			URI srcUri = ((File)it.next()).toURI();
			
			// get the relative path of the file to the plugin root, then find the localized file
			String relPath = pluginUri.relativize(srcUri).getPath();
			
			// only get the locale specific file, don't include the default one
			String localizedFile = getLocalizedFile(relPath, false);
			if ( localizedFile == null )
			{
				continue;
			}
						
    		// need to change the target file path to relative to the path instead of the plugin root
    		relPath = pathUri.relativize(srcUri).getPath();
    		File srcFile = new File(localizedFile);
    		File targetFile = new File(toDir, relPath);
    		File targetParent = targetFile.getParentFile();
    		
			// copy the file to the desitination
			// if useLocaleFileName is true, the destination file name should also use the locale specific file name
    		if ( useLocaleFileName )
    		{
    			String fileName = srcFile.getName();
    			targetFile = new File(targetParent, fileName);
    		}
    		
    		if (isDebugging()) {
	    		System.out.println("Copying localized file: "); //$NON-NLS-1$
	    		System.out.println("Source: " + srcFile); //$NON-NLS-1$
	    		System.out.println("Target: " + targetFile); //$NON-NLS-1$
	    		System.out.println(""); //$NON-NLS-1$
    		}
    		
    		try {
    			if ( !targetParent.exists() )
    			{
    				targetParent.mkdirs();
    			}
    			
    			if ( !targetFile.exists() )
    			{
    				targetFile.createNewFile();
    			}
    			
				FileUtil.copyFile(srcFile, targetFile);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	
	/**
	 * Returns the localized resource.
	 * 
	 * @param key
	 *            The resource key.
	 * @return The localized resource.
	 */
	public String getString(String key) {
		if (resourceBundle != null) {
			try {
				return resourceBundle.getString(key);
			} catch (MissingResourceException e) {
			}
		}
		return '[' + key + ']';
	}

	/**
	 * Returns the formatted localized message given the resource key and the
	 * message argument.
	 * 
	 * @param key
	 *            The resource key.
	 * @param argument
	 *            The message argument.
	 * @return The formatted localized message.
	 */
	public String formatMessage(String key, Object argument) {
		if (resourceBundle != null) {
			try {
				String msg = resourceBundle.getString(key);
				Object[] arguments = { argument };
				return MessageFormat.format(msg, arguments);
			} catch (MissingResourceException e) {
			}
		}
		return '[' + key + ']';
	}

	/**
	 * Returns the image URL given the relative path.
	 * 
	 * @param relativePath
	 *            The image's path relative to the plug-in's root.
	 * @return The image URL.
	 */
	public URL getImageURL(String relativePath) {
		try {
			URL url = new URL(iconURL, relativePath);
			return FileLocator.resolve(url);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * Returns the image descriptor given the relative path.
	 * 
	 * @param relativePath
	 *            The image's path relative to the plug-in's root.
	 * @return The image descriptor.
	 */
	public ImageDescriptor getImageDescriptor(String relativePath) {
		try {
			URL url = new URL(iconURL, relativePath);
			return ImageDescriptor.createFromURL(url);
		} catch (MalformedURLException e) {
			return ImageDescriptor.getMissingImageDescriptor();
		}
	}

	/**
	 * Returns the image given the relative path.
	 * <p>
	 * Note: The returned image need to be freed by the caller.
	 * 
	 * @param relativePath
	 *            The image's path relative to the plug-in's root.
	 * @return The image.
	 */
	public Image getImage(String relativePath) {
		Image image = null;

		ImageDescriptor imageDescriptor = getImageDescriptor(relativePath);
		if (imageDescriptor != null) {
			image = imageDescriptor.createImage(false);
		}

		return image;
	}

	/**
	 * Returns the shared image given the relative path.
	 * <p>
	 * Note: The returned image will be automatically freed when the plug-in
	 * shuts down.
	 * 
	 * @param relativePath
	 *            The image's path relative to the plug-in's root.
	 * @return The image.
	 */
	public Image getSharedImage(String relativePath) {
		Image image = (Image) sharedImages.get(relativePath);
		if (image != null) {
			return image;
		}

		ImageDescriptor imageDescriptor = getImageDescriptor(relativePath);
		if (imageDescriptor != null) {
			image = imageDescriptor.createImage(false);
			if (image != null) {
				sharedImages.put(relativePath, image);
			}
		}

		return image;
	}

	/**
	 * Returns the logger given the plug-in ID.
	 * 
	 * @return The new or cached logger.
	 */
	public Logger getLogger() {
		Logger logger = (Logger) loggers.get(pluginId);
		if (logger == null) {
			logger = new Logger(this);
			loggers.put(pluginId, logger);
		}
		return logger;
	}

	/**
	 * Returns the message dialog given the plug-in ID.
	 * 
	 * @return The new or cached message dialog.
	 */
	public MsgDialog getMsgDialog() {
		MsgDialog msgDialog = (MsgDialog) msgDialogs.get(pluginId);
		if (msgDialog == null) {
			msgDialog = new MsgDialog(this);
			msgDialogs.put(pluginId, msgDialog);
		}
		return msgDialog;
	}

}
