/*
 * NativeCapable is Dynamic Install and Loading Framework for Java Application and Applet.
 * Copyright (C) 2008  Shinobu Izumi
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package jp.ac.kyutech.ai.ylab.shiva.utils.nativecapable;

import java.applet.Applet;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.HashSet;

public class NativeCapableModuleBuilder {

	private NativeCapable nativeCapable;

	private ClassLoader oldClassLoader;

	private Collection<String> targetPackages = new HashSet<String>();

	private boolean delegeteTargets = false;

	private String moduleClassName;

	private NaiveCapableClassLoader libEnabledLoader;

	private LibInstaller installer;

	private boolean debug = false;

	/**
	 * NativeCapableModuleの生成を行うクラスです。
	 * 
	 * @param nca
	 * @param mcn
	 */
	public NativeCapableModuleBuilder(NativeCapable nca, String mcn) {
		this.nativeCapable = nca;
		moduleClassName = mcn;
	}

	/**
	 * NativeCapableModuleを生成します。<br>
	 * 必要に応じてネイティブライブラリをインストールし、ロードします。
	 * 
	 * @return 生成されたモジュール
	 */
	public NativeCapableModule newModule() {

		SecurityManager sm = System.getSecurityManager();
		System.setSecurityManager(null);

		NativeCapableModule m = null;

		oldClassLoader = Thread.currentThread().getContextClassLoader();
		nativeCapable.setOldClassLader(oldClassLoader);

		Class<?> cls = null;

		URL codeBase = null;
		if (nativeCapable instanceof Applet) {
			Applet ap = (Applet) nativeCapable;
			System.out.println("codebase = " + ap.getCodeBase());

			codeBase = ap.getCodeBase();

		}
		try {
			if (!debug) {
				File installDir = new File(
						System.getProperty("java.io.tmpdir"), nativeCapable
								.getClass().getName());

				if (codeBase != null) {
					Collection<URL> c = new HashSet<URL>();

					String path = System.getProperty("java.class.path");
					System.out.println("classpath = " + path);
					String[] paths = path.split(";");
					for (String string : paths) {
						if (string.endsWith(".jar")) {
							string = string.replace('\\', '/');
							int idx = string.lastIndexOf('/');
							if (idx > 0) {
								c.add(new URL(codeBase, string.substring(idx)));
							}
						}
					}

					ClassLoader cl = getClass().getClassLoader();
					if (cl instanceof URLClassLoader) {
						URLClassLoader acl = (URLClassLoader) cl;
						URL[] urls = acl.getURLs();
						for (URL url : urls) {
							String string = url.toString();
							if (string.endsWith(".jar")) {
								c.add(url);
							}
						}
					}

					URL[] urls = new URL[c.size()];
					c.toArray(urls);

					System.out.println("installed jars : " + c);
					libEnabledLoader = new NaiveCapableClassLoader(urls,
							oldClassLoader, installDir);

				} else {
					libEnabledLoader = new NaiveCapableClassLoader(new URL[0],
							oldClassLoader, installDir);
				}

				libEnabledLoader.setDelegateTargets(delegeteTargets);
				libEnabledLoader.setTargets(targetPackages);

				Thread.currentThread().setContextClassLoader(libEnabledLoader);

				cls = libEnabledLoader.loadClass(moduleClassName);

				// install libraries
				installer = (LibInstaller) libEnabledLoader
						.findClass(
								"jp.ac.kyutech.ai.ylab.shiva.utils.nativecapable.LibInstallerImpl")
						.newInstance();
				try {
					installer.installLibs(installDir, cls);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			} else {
				// for local debugging
				cls = oldClassLoader.loadClass(moduleClassName);
			}

			m = (NativeCapableModule) cls.newInstance();
			nativeCapable.setModule(m);
		} catch (Exception e) {
			e.printStackTrace();
		}

		System.setSecurityManager(sm);
		return m;
	}

	/**
	 * モジュールクラスの名前を取得します
	 * 
	 * @return モジュールクラス名(FQCN)
	 */
	public String getModuleClassName() {
		return moduleClassName;
	}

	/**
	 * モジュールクラスの名前を設定します
	 */
	public void setModuleClassName(String moduleClassName) {
		this.moduleClassName = moduleClassName;
	}

	public Collection<String> getTargetPackages() {
		return targetPackages;
	}

	public void setTargetPackages(Collection<String> targetPackages) {
		this.targetPackages = targetPackages;
	}

	public boolean isDelegateTargets() {
		return delegeteTargets;
	}

	public void setDelegateTargets(boolean delegateTargets) {
		this.delegeteTargets = delegateTargets;
	}

	public boolean isDebug() {
		return debug;
	}

	public void setDebug(boolean debug) {
		this.debug = debug;
	}
}