package org.protege.xmlcatalog.swing;

import java.util.Enumeration;
import java.util.List;
import java.util.NoSuchElementException;

import javax.swing.tree.TreeNode;

import org.apache.log4j.Logger;
import org.protege.xmlcatalog.CatalogUtilities;
import org.protege.xmlcatalog.XMLCatalog;
import org.protege.xmlcatalog.entry.Entry;
import org.protege.xmlcatalog.entry.GroupEntry;
import org.protege.xmlcatalog.entry.NextCatalogEntry;


public class EntryTreeNode implements TreeNode {
	public static final Logger LOGGER = Logger.getLogger(EntryTreeNode.class);
	
	private TreeNode parent;
	private Entry entry;
	private List<EntryEditor> editors;
	private EntryEditor editor;
	private boolean autoGenerated;
	
	private XMLCatalog childCatalog;  // only if the entry is a NextCatalogEntry.
	
	public EntryTreeNode(TreeNode parent, Entry entry, List<EntryEditor> editors) {
		if (!(parent instanceof CatalogTreeNode) && !(parent instanceof EntryTreeNode)) {
			throw new IllegalArgumentException("Invalid Parent!");
		}
		this.entry = entry;
		this.editors = editors;
		this.parent = parent;
		editor = null;
		autoGenerated = false;
		
		boolean isParentAutoGenerated;
		if (parent instanceof CatalogTreeNode) {
			isParentAutoGenerated = false;
		}
		else {
			isParentAutoGenerated = ((EntryTreeNode) parent).isAutoGenerated();
		}

		if (isParentAutoGenerated) {
			autoGenerated = true;
		}
		else {
			for (EntryEditor editor : editors) {
				if (editor.isSuitable(entry)) {
					this.editor = editor;
				}
			}
		}
	}
	
	public Entry getEntry() {
		return entry;
	}
	
	public List<EntryEditor> getEditors() {
		return editors;
	}
	
	public EntryEditor getEditor() {
		return editor;
	}
	
	public boolean isAutoGenerated() {
		return autoGenerated;
	}

	/* *********************************************
	 * TreeNode Interfaces
	 */
	
	public TreeNode getChildAt(int childIndex) {
		if (entry instanceof GroupEntry) {
			Entry childEntry = ((GroupEntry) entry).getEntries().get(childIndex);
			return new EntryTreeNode(this, childEntry, editors);
		}
		else if (entry instanceof NextCatalogEntry && childIndex == 0) {
			try {
				if (childCatalog == null) {
					childCatalog = CatalogUtilities.parseDocument(((NextCatalogEntry) entry).getCatalog().toURL());
				}
			}
			catch (Exception e) {
				LOGGER.error("Exception caught trying to parse nested catalog in xml catalog", e);
			}
			return new CatalogTreeNode(childCatalog, editors);
		}
		return null;
	}

	
	public int getChildCount() {
		if (entry instanceof GroupEntry) {
			return ((GroupEntry) entry).getEntries().size();
		}
		else if (entry instanceof NextCatalogEntry) {
			return 1;
		}
		else {
			return 0;
		}
	}

	
	public TreeNode getParent() {
		return parent;
	}

	
	public int getIndex(TreeNode node) {
		if (node instanceof EntryTreeNode && ((EntryTreeNode) node).getEntry() instanceof GroupEntry) {
			return  ((GroupEntry) ((EntryTreeNode) node).getEntry()).getEntries().indexOf(entry);
		}
		return -1;
	}

	
	public boolean getAllowsChildren() {
		return entry instanceof GroupEntry || entry instanceof NextCatalogEntry;
	}

	
	public boolean isLeaf() {
		if (entry instanceof GroupEntry) {
			return ((GroupEntry) entry).getEntries().size() == 0;
		}
		return entry instanceof NextCatalogEntry;
	}

	
	public Enumeration<TreeNode> children() {
		return new Enumeration<TreeNode>() {
			int currentIndex = 0;
			
			public boolean hasMoreElements() {
				return currentIndex < getChildCount();
			}
			
			public TreeNode nextElement() throws NoSuchElementException {
				if (!hasMoreElements()) {
					throw new NoSuchElementException("No such element");
				}
				try {
					return getChildAt(currentIndex);
				}
				finally {
					currentIndex++;
				}
			}
		};
	}

}
