package your.choice;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Iterator;

import javax.jcr.AccessDeniedException;
import javax.jcr.Binary;
import javax.jcr.InvalidItemStateException;
import javax.jcr.InvalidLifecycleTransitionException;
import javax.jcr.Item;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.ItemVisitor;
import javax.jcr.MergeException;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.ReferentialIntegrityException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.Lock;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.ActivityViolationException;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;

import org.apache.jackrabbit.commons.flat.BTreeManager;
import org.apache.jackrabbit.commons.flat.ItemSequence;
import org.apache.jackrabbit.commons.flat.NodeSequence;
import org.apache.jackrabbit.commons.flat.TreeManager;
import org.apache.jackrabbit.commons.flat.TreeTraverser.ErrorHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NodeWrapper implements Node, ErrorHandler {

	Node node;
	NodeSequence nodeSequence = null;
    private Logger log = LoggerFactory.getLogger(Jackrabbit.class);

	NodeWrapper(Node node) {
		this.node = node;
		TreeManager treeMgr = null;
		try {
			 treeMgr = new BTreeManager(node, 20, 100, new Comparator<String>() {
				public int compare(String left, String right) {
					return left.compareTo(right);
				}
			}, false);
		} catch (RepositoryException e) {
			call(null, e);
		}
		if (treeMgr != null) {
			nodeSequence = ItemSequence.createNodeSequence(treeMgr, this);
		}
	}


	@Override
	public void call(Item arg0, RepositoryException e) {
		log.error("Failed with exception.", e);
	}

	@Override
	public void addMixin(String mixinName) throws NoSuchNodeTypeException,
			VersionException, ConstraintViolationException, LockException,
			RepositoryException {
		node.addMixin(mixinName);
	}

	@Override
	public Node addNode(String relPath) throws ItemExistsException,
			PathNotFoundException, VersionException,
			ConstraintViolationException, LockException, RepositoryException {
		return addNode(relPath, null);
	}

	@Override
	public Node addNode(String relPath, String primaryNodeTypeName)
			throws ItemExistsException, PathNotFoundException,
			NoSuchNodeTypeException, LockException, VersionException,
			ConstraintViolationException, RepositoryException {
		return (nodeSequence != null) ? nodeSequence.addNode(relPath, primaryNodeTypeName) : node.addNode(relPath, primaryNodeTypeName);
	}

	@Override
	public boolean canAddMixin(String mixinName)
			throws NoSuchNodeTypeException, RepositoryException {
		return node.canAddMixin(mixinName);
	}

	@Override
	@Deprecated
	public void cancelMerge(Version version) throws VersionException,
			InvalidItemStateException, UnsupportedRepositoryOperationException,
			RepositoryException {
		node.cancelMerge(version);
	}

	@Override
	@Deprecated
	public Version checkin() throws VersionException,
			UnsupportedRepositoryOperationException, InvalidItemStateException,
			LockException, RepositoryException {
		return node.checkin();
	}

	@Override
	@Deprecated
	public void checkout() throws UnsupportedRepositoryOperationException,
			LockException, ActivityViolationException, RepositoryException {
		node.checkout();
	}

	@Override
	@Deprecated
	public void doneMerge(Version version) throws VersionException,
			InvalidItemStateException, UnsupportedRepositoryOperationException,
			RepositoryException {
		node.doneMerge(version);
	}

	@Override
	public void followLifecycleTransition(String transition)
			throws UnsupportedRepositoryOperationException,
			InvalidLifecycleTransitionException, RepositoryException {
		node.followLifecycleTransition(transition);
	}

	@Override
	public String[] getAllowedLifecycleTransistions()
			throws UnsupportedRepositoryOperationException, RepositoryException {
		return node.getAllowedLifecycleTransistions();
	}

	@Override
	@Deprecated
	public Version getBaseVersion()
			throws UnsupportedRepositoryOperationException, RepositoryException {
		return node.getBaseVersion();
	}

	@Override
	public String getCorrespondingNodePath(String workspaceName)
			throws ItemNotFoundException, NoSuchWorkspaceException,
			AccessDeniedException, RepositoryException {
		return node.getCorrespondingNodePath(workspaceName);
	}

	@Override
	public NodeDefinition getDefinition() throws RepositoryException {
		return node.getDefinition();
	}

	@Override
	public String getIdentifier() throws RepositoryException {
		return node.getIdentifier();
	}

	@Override
	public int getIndex() throws RepositoryException {
		return node.getIndex();
	}

	@Override
	@Deprecated
	public Lock getLock() throws UnsupportedRepositoryOperationException,
			LockException, AccessDeniedException, RepositoryException {
		return node.getLock();
	}

	@Override
	public NodeType[] getMixinNodeTypes() throws RepositoryException {
		return node.getMixinNodeTypes();
	}

	@Override
	public Node getNode(String relPath) throws PathNotFoundException,
			RepositoryException {
		return (nodeSequence != null) ? nodeSequence.getItem(relPath) : node.getNode(relPath);
	}

	@Override
	public NodeIterator getNodes() throws RepositoryException {
		return (nodeSequence != null) ? new AdaptingNodeIterator(nodeSequence.iterator()) : node.getNodes();
	}

	@Override
	public NodeIterator getNodes(String namePattern) throws RepositoryException {
		return node.getNodes(namePattern);
	}

	@Override
	public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
		return node.getNodes(nameGlobs);
	}

	@Override
	public Item getPrimaryItem() throws ItemNotFoundException,
			RepositoryException {
		return node.getPrimaryItem();
	}

	@Override
	public NodeType getPrimaryNodeType() throws RepositoryException {
		return node.getPrimaryNodeType();
	}

	@Override
	public PropertyIterator getProperties() throws RepositoryException {
		return node.getProperties();
	}

	@Override
	public PropertyIterator getProperties(String namePattern)
			throws RepositoryException {
		return node.getProperties(namePattern);
	}

	@Override
	public PropertyIterator getProperties(String[] nameGlobs)
			throws RepositoryException {
		return node.getProperties(nameGlobs);
	}

	@Override
	public Property getProperty(String relPath) throws PathNotFoundException,
			RepositoryException {
		return node.getProperty(relPath);
	}

	@Override
	public PropertyIterator getReferences() throws RepositoryException {
		return node.getReferences();
	}

	@Override
	public PropertyIterator getReferences(String name)
			throws RepositoryException {
		return node.getReferences(name);
	}

	@Override
	public NodeIterator getSharedSet() throws RepositoryException {
		return node.getSharedSet();
	}

	@Override
	@Deprecated
	public String getUUID() throws UnsupportedRepositoryOperationException,
			RepositoryException {
		return node.getUUID();
	}

	@Override
	@Deprecated
	public VersionHistory getVersionHistory()
			throws UnsupportedRepositoryOperationException, RepositoryException {
		return node.getVersionHistory();
	}

	@Override
	public PropertyIterator getWeakReferences() throws RepositoryException {
		return node.getWeakReferences();
	}

	@Override
	public PropertyIterator getWeakReferences(String name)
			throws RepositoryException {
		return node.getWeakReferences(name);
	}

	@Override
	public boolean hasNode(String relPath) throws RepositoryException {
		return (nodeSequence != null) ? nodeSequence.hasItem(relPath): node.hasNode(relPath);
	}

	@Override
	public boolean hasNodes() throws RepositoryException {
		return node.hasNodes();
	}

	@Override
	public boolean hasProperties() throws RepositoryException {
		return node.hasProperties();
	}

	@Override
	public boolean hasProperty(String relPath) throws RepositoryException {
		return node.hasProperty(relPath);
	}

	@Override
	@Deprecated
	public boolean holdsLock() throws RepositoryException {
		return node.holdsLock();
	}

	@Override
	public boolean isCheckedOut() throws RepositoryException {
		return node.isCheckedOut();
	}

	@Override
	public boolean isLocked() throws RepositoryException {
		return node.isLocked();
	}

	@Override
	public boolean isNodeType(String nodeTypeName) throws RepositoryException {
		return node.isNodeType(nodeTypeName);
	}

	@Override
	@Deprecated
	public Lock lock(boolean isDeep, boolean isSessionScoped)
			throws UnsupportedRepositoryOperationException, LockException,
			AccessDeniedException, InvalidItemStateException,
			RepositoryException {
		return node.lock(isDeep, isSessionScoped);
	}

	@Override
	@Deprecated
	public NodeIterator merge(String srcWorkspace, boolean bestEffort)
			throws NoSuchWorkspaceException, AccessDeniedException,
			MergeException, LockException, InvalidItemStateException,
			RepositoryException {
		return node.merge(srcWorkspace, bestEffort);
	}

	@Override
	public void orderBefore(String srcChildRelPath, String destChildRelPath)
			throws UnsupportedRepositoryOperationException, VersionException,
			ConstraintViolationException, ItemNotFoundException, LockException,
			RepositoryException {
		node.orderBefore(srcChildRelPath, destChildRelPath);
	}

	@Override
	public void removeMixin(String mixinName) throws NoSuchNodeTypeException,
			VersionException, ConstraintViolationException, LockException,
			RepositoryException {
		node.removeMixin(mixinName);
	}

	@Override
	public void removeShare() throws VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		node.removeShare();
	}

	@Override
	public void removeSharedSet() throws VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		node.removeSharedSet();
	}

	@Override
	@Deprecated
	public void restore(String versionName, boolean removeExisting)
			throws VersionException, ItemExistsException,
			UnsupportedRepositoryOperationException, LockException,
			InvalidItemStateException, RepositoryException {
		node.restore(versionName, removeExisting);
	}

	@Override
	@Deprecated
	public void restore(Version version, boolean removeExisting)
			throws VersionException, ItemExistsException,
			InvalidItemStateException, UnsupportedRepositoryOperationException,
			LockException, RepositoryException {
		node.restore(version, removeExisting);
	}

	@Override
	@Deprecated
	public void restore(Version version, String relPath, boolean removeExisting)
			throws PathNotFoundException, ItemExistsException,
			VersionException, ConstraintViolationException,
			UnsupportedRepositoryOperationException, LockException,
			InvalidItemStateException, RepositoryException {
		node.restore(version, relPath, removeExisting);
	}

	@Override
	@Deprecated
	public void restoreByLabel(String versionLabel, boolean removeExisting)
			throws VersionException, ItemExistsException,
			UnsupportedRepositoryOperationException, LockException,
			InvalidItemStateException, RepositoryException {
		node.restoreByLabel(versionLabel, removeExisting);
	}

	@Override
	public void setPrimaryType(String nodeTypeName)
			throws NoSuchNodeTypeException, VersionException,
			ConstraintViolationException, LockException, RepositoryException {
		node.setPrimaryType(nodeTypeName);
	}

	@Override
	public Property setProperty(String name, Value value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, Value[] values)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, values);
	}

	@Override
	public Property setProperty(String name, String[] values)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, values);
	}

	@Override
	public Property setProperty(String name, String value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	@Deprecated
	public Property setProperty(String name, InputStream value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, Binary value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, boolean value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, double value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, BigDecimal value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, long value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, Calendar value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, Node value)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value);
	}

	@Override
	public Property setProperty(String name, Value value, int type)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value, type);
	}

	@Override
	public Property setProperty(String name, Value[] values, int type)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, values, type);
	}

	@Override
	public Property setProperty(String name, String[] values, int type)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, values, type);
	}

	@Override
	public Property setProperty(String name, String value, int type)
			throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {
		return node.setProperty(name, value, type);
	}

	@Override
	@Deprecated
	public void unlock() throws UnsupportedRepositoryOperationException,
			LockException, AccessDeniedException, InvalidItemStateException,
			RepositoryException {
		node.unlock();
	}

	@Override
	public void update(String srcWorkspace) throws NoSuchWorkspaceException,
			AccessDeniedException, LockException, InvalidItemStateException,
			RepositoryException {
		node.update(srcWorkspace);
	}

	@Override
	public void accept(ItemVisitor visitor) throws RepositoryException {
		node.accept(visitor);
	}

	@Override
	public Item getAncestor(int depth) throws ItemNotFoundException,
			AccessDeniedException, RepositoryException {
		return node.getAncestor(depth);
	}

	@Override
	public int getDepth() throws RepositoryException {
		return node.getDepth();
	}

	@Override
	public String getName() throws RepositoryException {
		return node.getName();
	}

	@Override
	public Node getParent() throws ItemNotFoundException,
			AccessDeniedException, RepositoryException {
		return node.getParent();
	}

	@Override
	public String getPath() throws RepositoryException {
		return node.getPath();
	}

	@Override
	public Session getSession() throws RepositoryException {
		return node.getSession();
	}

	@Override
	public boolean isModified() {
		return node.isModified();
	}

	@Override
	public boolean isNew() {
		return node.isNew();
	}

	@Override
	public boolean isNode() {
		return node.isNode();
	}

	@Override
	public boolean isSame(Item otherItem) throws RepositoryException {
		return node.isSame(otherItem);
	}

	@Override
	public void refresh(boolean keepChanges) throws InvalidItemStateException,
			RepositoryException {
		node.refresh(keepChanges);
	}

	@Override
	public void remove() throws VersionException, LockException,
			ConstraintViolationException, AccessDeniedException,
			RepositoryException {
		node.remove();
	}

	@Override
	@Deprecated
	public void save() throws AccessDeniedException, ItemExistsException,
			ConstraintViolationException, InvalidItemStateException,
			ReferentialIntegrityException, VersionException, LockException,
			NoSuchNodeTypeException, RepositoryException {
		node.save();
	}

	private class AdaptingNodeIterator implements NodeIterator {
		Iterator<Node> actualIterator;

		private AdaptingNodeIterator() {
			// prevent use
		}

		AdaptingNodeIterator(Iterator<Node> actualIterator) {
			this();
			this.actualIterator = actualIterator;
		}

		@Override
		public long getPosition() {
			throw new UnsupportedOperationException("Impossible for this iterator.");
		}

		@Override
		public long getSize() {
			// value indicating unknown, according to RangeIterator.
			return -1;
		}

		@Override
		public void skip(long skipNum) {
			assert(skipNum >= 0);
			for (int i = 0; i < skipNum; i++) {
				// assume this throws the NoSuchElementException
				next();
			}
		}

		@Override
		public boolean hasNext() {
			return actualIterator.hasNext();
		}

		@Override
		public Node next() {
			return actualIterator.next();
		}

		@Override
		public void remove() {
			actualIterator.remove();
		}

		@Override
		public Node nextNode() {
			return actualIterator.next();
		}
	}
}
