package jp.ac.kyutech.ai.ylab.shiva.nativecapable.plugin;

import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;

import jp.ac.kyutech.ai.ylab.shiva.nativecapable.plugin.properties.NativeCapableJavaProjectPropertyPage;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.JavaRuntime;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class NCClasspathContainerInitializer extends
		ClasspathContainerInitializer {

	public String getDescription(IPath containerPath, IJavaProject project) {
		return "NCLIB Classpath Container Initializer";//$NON-NLS-1$
	}

	/**
	 * Description of the Method
	 * 
	 * @param containerPath
	 *            Description of the Parameter
	 * @param project
	 *            Description of the Parameter
	 * @exception CoreException
	 *                Description of the Exception
	 */
	public void initialize(IPath containerPath, IJavaProject project)
			throws CoreException {
		int size = containerPath.segmentCount();

		System.out.println("ContainerPath = " + containerPath);

		if (size > 0) {
			if (containerPath.segment(0).equals("NCLIB")) {
				System.out.println("ContainerPath Segment = "
						+ containerPath.segment(0));

				IPath libDirPath = new Path(
						project
								.getProject()
								.getPersistentProperty(
										new QualifiedName(
												"",
												NativeCapableJavaProjectPropertyPage.LIB_DIR_PROPERTY)));
				final IFolder libDir = project.getProject().getFolder(
						libDirPath);

				String srcDirPath = project
						.getProject()
						.getPersistentProperty(
								new QualifiedName(
										"",
										NativeCapableJavaProjectPropertyPage.SRC_DIR_PROPERTY));
				IFolder srcDir = project.getProject().getFolder(
						new Path(srcDirPath));

				// ------- read os.xml -------
				String nativePath = "native";
				String osName = System.getProperty("os.name");
				String osArch = System.getProperty("os.arch");

				IFile osXml = srcDir.getFile("os.xml");
				if (osXml.exists()) {
					System.out.println("found os.xml in : " + srcDir);
					Document doc = null;
					try {
						doc = DocumentBuilderFactory.newInstance()
								.newDocumentBuilder().parse(
										osXml.getRawLocationURI().toString());
					} 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())) {
								nativePath = e.getAttribute("path").trim();
							}
						}
					}
				} else {
					System.out.println("couldn't found os.xml in : " + srcDir);
				}
				// ------- read os.xml -------

				IPath conNativePath = project.getPath().append(
						srcDirPath + "/" + nativePath);

				IClasspathContainer libContainer = new NCClasspathContainer(
						libDir);
				JavaCore.setClasspathContainer(containerPath,
						new IJavaProject[] { project }, // value for
						// 'myProject'
						new IClasspathContainer[] { libContainer }, null);
				// // AbstractClasspathContainer container = this
				// // .createClasspathContainer(containerPath);
				// // XXX:
				// ClassPathContainer container = null;
				// JavaCore.setClasspathContainer(containerPath,
				// new IJavaProject[] { project },
				// new IClasspathContainer[] { container }, null);

				IResourceChangeListener listener = null;
				// For detecting libraries entry changing
				if (listener == null) {
					listener = new NCResourceChangeListener(project,
							libContainer, conNativePath, containerPath);
					// FIXME: dynamic library addition
					ResourcesPlugin.getWorkspace().addResourceChangeListener(
							listener, IResourceChangeEvent.POST_CHANGE);
				}
			}
		}
	}

	private class NCClasspathContainer implements IClasspathContainer {

		private IFolder libDir;

		private IClasspathEntry[] entries;

		public NCClasspathContainer(IFolder libDir) {
			this.libDir = libDir;
		}

		public IClasspathEntry[] getClasspathEntries() {
			System.out.println("getClasspathEntries=======================");
			System.out.println(new Throwable().getStackTrace()[1]);
			System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");

			List<IClasspathEntry> ices = new ArrayList<IClasspathEntry>();
			try {

				IResource[] rs = libDir.members();
				for (IResource r : rs) {
					if (r instanceof IFile) {
						IFile f = (IFile) r;
						IClasspathEntry entry = JavaCore.newLibraryEntry(f
								.getFullPath(), null, null, false);
						ices.add(entry);
					}
				}
			} catch (CoreException e) {
				e.printStackTrace();
			}
			entries = ices.toArray(new IClasspathEntry[ices.size()]);
			return entries;
		}

		public String getDescription() {
			return "Application Library Container";
		}

		public int getKind() {
			return IClasspathContainer.K_APPLICATION;
		}

		public IPath getPath() {
			return new Path("NCLIB/basic");
		}

	};

	private class NCResourceChangeListener implements IResourceChangeListener {

		private IJavaProject javaProject;

		private IClasspathContainer libContainer;

		private IPath conNativePath;

		private IPath containerPath;

		public NCResourceChangeListener(IJavaProject ijp,
				IClasspathContainer icc, IPath cnp, IPath cp) {
			javaProject = ijp;
			libContainer = icc;
			conNativePath = cnp;
			containerPath = cp;
		}

		@Override
		public void resourceChanged(IResourceChangeEvent event) {

			// NCClasspathContainer nccc = (NCClasspathContainer) libContainer;

			try {
				final IClasspathEntry[] classpathEntries = javaProject
						.getRawClasspath();
				System.out.println("num classpath entries = "
						+ classpathEntries.length);

				boolean f1 = true;
				boolean f2 = true;

				if (f1) {
					for (int i = 0; i < classpathEntries.length; i++) {
						IClasspathEntry entry = classpathEntries[i];

						System.out.println(i);
						System.out.println("Project = "
								+ javaProject.getProject());
						System.out.println("Path = " + entry.getPath());

						if (entry.getPath().toString().equals("NCLIB/default")) {

							JavaCore.setClasspathContainer(containerPath,
									new IJavaProject[] { javaProject },
									new IClasspathContainer[] { libContainer },
									null);

							IClasspathEntry libEntry = JavaCore
									.newContainerEntry(
											containerPath,
											null,
											new IClasspathAttribute[] { JavaCore
													.newClasspathAttribute(
															JavaRuntime.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY,
															conNativePath
																	.toString()
																	.substring(
																			1)) },
											true);
							classpathEntries[i] = libEntry;
						}
					}
				}

				if (f2) {
					Thread th = new Thread() {
						@Override
						public void run() {
							boolean flag = false;
							while (!flag) {
								try {
									javaProject.setRawClasspath(
											classpathEntries, null);
									flag = true;
								} catch (JavaModelException e) {
									e.printStackTrace();
									try {
										Thread.sleep(300);
									} catch (InterruptedException e1) {
										e1.printStackTrace();
									}
								}
								System.out.println("retry...");
							}
							System.out.println("Classpath's been set.");
						}
					};
					th.start();
				}
			} catch (JavaModelException e) {
				System.out.println(e + " [this exception might be ignorable]");
			}

			IResourceDelta delta = event.getDelta();
			if (delta != null) {
			}
			IResourceDelta affected[] = delta.getAffectedChildren();
			for (int i = 0; i < affected.length; i++) {
				// System.out.println(affected[i].getResource());
			}
		}
	}

	public boolean canUpdateClasspathContainer(IPath containerPath,
			IJavaProject project) {
		System.out.println("canUpdateClasspathContainer");
		return true;
	}

	public void requestClasspathContainerUpdate(final IPath containerPath,
			final IJavaProject project, final IClasspathContainer sg)
			throws CoreException {
		System.out.println("requestClasspathContainerUpdate");
		// String key = AbstractClasspathContainer
		// .getDecorationManagerKey(containerPath.toString());
		//
		// IClasspathEntry[] entries = sg.getClasspathEntries();
		// ClasspathDecorationsManager decorations = AbstractClasspathContainer
		// .getDecorationsManager();
		// decorations.clearAllDecorations(key);
		//
		// for (int i = 0; i < entries.length; i++) {
		// final IClasspathEntry entry = entries[i];
		//
		// final IPath srcpath = entry.getSourceAttachmentPath();
		// final IPath srcrootpath = entry.getSourceAttachmentRootPath();
		// final IClasspathAttribute[] attrs = entry.getExtraAttributes();
		// final String eid = entry.getPath().toString();
		// final ClasspathDecorations dec = new ClasspathDecorations();
		//
		// dec.setSourceAttachmentPath(srcpath);
		// dec.setSourceAttachmentRootPath(srcrootpath);
		// dec.setExtraAttributes(attrs);
		//
		// decorations.setDecorations(key, eid, dec);
		// }
		// decorations.save();
		// final IClasspathContainer container = JavaCore.getClasspathContainer(
		// containerPath, project);
		// ((AbstractClasspathContainer) container).refresh();
	}

}
