/*
 * 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.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Implementation of LibInstaller
 * 
 * @author shinobu
 * 
 */
public class LibInstallerImpl implements LibInstaller {

	private List<File> installed;

	private File installDir;

	public LibInstallerImpl() {
	}

	public void deleteAll(File file) {
		if (file.isDirectory()) {
			File[] fs = file.listFiles();
			for (File file2 : fs) {
				deleteAll(file2);
			}
			file.delete();
		} else {
			file.delete();
		}
	}

	@Override
	public boolean installLibs(File dir, Class<?> c) throws IOException {

		// Delete previously installed files
		deleteAll(dir);

		boolean flag = true;

		installed = new ArrayList<File>();

		List<File> dlls = new ArrayList<File>();

		installDir = new File(dir, UUID.randomUUID().toString());
		installDir.mkdirs();
		System.out.println(installDir.getAbsolutePath());

		File rootDir = Misc.getRootDirAccess(c, installDir);

		if (rootDir == null) {
			throw new FileNotFoundException();
		}
		System.out.println("Root Dir is : " + rootDir);
		File osXml = new File(rootDir, "os.xml");

		File nativeDir = new File(rootDir, "native");
		if (osXml.exists()) {
			System.out.println("os.xml is found at : " + osXml);
			String osName = System.getProperty("os.name");
			String osArch = System.getProperty("os.arch");

			Document doc = null;
			try {
				doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
						.parse(osXml);
			} catch (Exception e) {
				e.printStackTrace();
			}
			NodeList nl = doc.getElementsByTagName("library");
			for (int i = 0; i < nl.getLength(); i++) {
				Node n = nl.item(i);
				if (n instanceof Element) {
					Element e = (Element) n;
					if (osName.trim().startsWith(e.getAttribute("name").trim())
							&& osArch.trim().startsWith(
									e.getAttribute("arch").trim())) {
						nativeDir = new File(rootDir, e.getAttribute("path")
								.trim());
					}
				}
			}
		}

		File[] dllFiles = nativeDir.listFiles();
		if (dllFiles == null) {
			return false;
		}
		for (File file : dllFiles) {
			System.out.println("Copying DLL: " + file.getAbsolutePath());

			File outFile = new File(installDir, file.getName());
			outFile.deleteOnExit();

			if (!outFile.exists()) {
				try {
					final OutputStream os = new FileOutputStream(outFile);
					try {
						final InputStream is = new FileInputStream(file);
						try {
							final byte[] buf = new byte[4096];
							for (;;) {
								final int rdsz = is.read(buf);
								if (rdsz < 0) {
									break;
								}
								os.write(buf, 0, rdsz);
							}
						} finally {
							is.close();
						}
					} finally {
						os.flush();
						os.close();
					}
				} catch (Exception e) {
					e.printStackTrace();
					outFile = null;
				}
			}
			System.out.println("Writing File Succeed: "
					+ outFile.getAbsolutePath());
			dlls.add(outFile);
			installed.add(outFile);

		}

		List<String> loadseq = new ArrayList<String>();
		List<File> loaded = new ArrayList<File>();
		while (dlls.size() > 0) {
			for (File file : dlls) {
				try {
					System.out.println("Loading Library: " + file.getName());
					System.load(file.getAbsolutePath());
					System.out.println("Load Library: " + file.getName());
					loaded.add(file);
					loadseq.add(file.getName());
				} catch (java.lang.UnsatisfiedLinkError e) {
					System.out.println(e);
				}
			}
			if (loaded.size() <= 0) {
				System.out
						.println("ERROR: some dll instration have been failed.");
				System.out.println(dlls);
				flag = false;
				break;
			}
			dlls.removeAll(loaded);
			loaded.clear();
		}

		// System.in.read();
		System.out.println("--------dll load sequence---------");
		for (String n : loadseq) {
			System.out.println(n);
		}
		return flag;
	}

	public File getInstallDir() {
		return installDir;
	}

}
