package com.parc.ccn.data.content;

import java.io.IOException;

import javax.xml.stream.XMLStreamException;

import com.parc.ccn.data.ContentName;
import com.parc.ccn.data.ContentObject;
import com.parc.ccn.data.security.LinkAuthenticator;
import com.parc.ccn.data.security.PublisherPublicKeyDigest;
import com.parc.ccn.data.util.CCNEncodableObject;
import com.parc.ccn.data.util.GenericXMLEncodable;
import com.parc.ccn.data.util.XMLDecoder;
import com.parc.ccn.data.util.XMLEncodable;
import com.parc.ccn.data.util.XMLEncoder;
import com.parc.ccn.library.CCNLibrary;

/**
 * Mapping from a link to the underlying XML representation.
 * Basically a Link is a content object containing
 * a name and optionally some content authentication
 * information to specify whose value for the name
 * to take.
 * @author smetters
 *
 */
public class LinkReference extends GenericXMLEncodable implements XMLEncodable {
	
	protected static final String LINK_ELEMENT = "Link";
	protected static final String LABEL_ELEMENT = "Label"; // overlaps with WrappedKey.LABEL_ELEMENT,
															// shared dictionary entry
	
	public static class LinkObject extends CCNEncodableObject<LinkReference> {

		public LinkObject(ContentName name, LinkReference data, CCNLibrary library) throws IOException {
			super(LinkReference.class, name, data, library);
		}
		
		public LinkObject(ContentName name, PublisherPublicKeyDigest publisher,
				CCNLibrary library) throws IOException, XMLStreamException {
			super(LinkReference.class, name, publisher, library);
		}
		
		/**
		 * Read constructor -- opens existing object.
		 * @param type
		 * @param name
		 * @param library
		 * @throws XMLStreamException
		 * @throws IOException
		 * @throws ClassNotFoundException 
		 */
		public LinkObject(ContentName name, 
				CCNLibrary library) throws IOException, XMLStreamException {
			super(LinkReference.class, name, (PublisherPublicKeyDigest)null, library);
		}
		
		public LinkObject(ContentObject firstBlock,
				CCNLibrary library) throws IOException, XMLStreamException {
			super(LinkReference.class, firstBlock, library);
		}
		
		public LinkReference link() { return data(); }
	}

	
	protected ContentName _targetName;
	protected String _targetLabel;
	protected LinkAuthenticator _targetAuthenticator = null;
	
	public LinkReference(ContentName targetName, String targetLabel, LinkAuthenticator targetAuthenticator) {
		_targetName = targetName;
		if ((null != targetLabel) && (targetLabel.length() == 0))
			targetLabel = null;
		_targetLabel = targetLabel;
		_targetAuthenticator = targetAuthenticator;
	}

	public LinkReference(ContentName targetName, LinkAuthenticator targetAuthenticator) {
		_targetName = targetName;
		_targetAuthenticator = targetAuthenticator;
	}
	
	public LinkReference(ContentName targetName) {
		this(targetName, null);
	}
	
	/**
	 * Decoding constructor.
	 */
	public LinkReference() {}
	
	public ContentName targetName() { return _targetName; }
	public String targetLabel() { return _targetLabel; }
	public LinkAuthenticator targetAuthenticator() { return _targetAuthenticator; }
		
	/**
	 * XML format:
	 * @throws XMLStreamException 
	 * 
	 */
	
	public void decode(XMLDecoder decoder) throws XMLStreamException {
		decoder.readStartElement(LINK_ELEMENT);

		_targetName = new ContentName();
		_targetName.decode(decoder);
		
		if (decoder.peekStartElement(LABEL_ELEMENT)) {
			_targetLabel = decoder.readUTF8Element(LABEL_ELEMENT); 
		}

		if (decoder.peekStartElement(LinkAuthenticator.LINK_AUTHENTICATOR_ELEMENT)) {
			_targetAuthenticator = new LinkAuthenticator();
			_targetAuthenticator.decode(decoder);
		}

		decoder.readEndElement();
	}

	public void encode(XMLEncoder encoder) throws XMLStreamException {

		encoder.writeStartElement(LINK_ELEMENT);
		_targetName.encode(encoder);
		if (null != targetLabel()) {
			encoder.writeElement(LABEL_ELEMENT, targetLabel());
		}
		if (null != _targetAuthenticator)
			_targetAuthenticator.encode(encoder);
		encoder.writeEndElement();   		
	}
	
	public boolean validate() {
		return (null != targetName());
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime
				* result
				+ ((_targetAuthenticator == null) ? 0 : _targetAuthenticator
						.hashCode());
		result = prime * result
				+ ((_targetLabel == null) ? 0 : _targetLabel.hashCode());
		result = prime * result
				+ ((_targetName == null) ? 0 : _targetName.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		LinkReference other = (LinkReference) obj;
		if (_targetAuthenticator == null) {
			if (other._targetAuthenticator != null)
				return false;
		} else if (!_targetAuthenticator.equals(other._targetAuthenticator))
			return false;
		if (_targetLabel == null) {
			if (other._targetLabel != null)
				return false;
		} else if (!_targetLabel.equals(other._targetLabel))
			return false;
		if (_targetName == null) {
			if (other._targetName != null)
				return false;
		} else if (!_targetName.equals(other._targetName))
			return false;
		return true;
	}

}

@@ -927,20 +927,31 @@
 ncodable
+, Cloneable
  %7B%0D%0A
-
 %09%0D%0A%09prot
@@ -2998,24 +2998,320 @@
 ce() %7B%7D%0D%0A%09%0D%0A
+%09/**%0D%0A%09 * Copy constructor. If we want to change name or authenticator,%0D%0A%09 * should clone those as well.%0D%0A%09 */%0D%0A%09public LinkReference(LinkReference other) %7B%0D%0A%09%09_targetName = other.targetName();%0D%0A%09%09_targetLabel = other.targetLabel();%0D%0A%09%09_targetAuthenticator = other.targetAuthenticator();%0D%0A%09%7D%0D%0A%09%0D%0A
 %09public Cont
@@ -5656,16 +5656,16 @@
 false;%0D%0A
-
 %09%09return
@@ -5678,11 +5678,124 @@
 %0D%0A%09%7D%0D%0A%0D%0A
+%09@Override%0D%0A%09public Object clone() throws CloneNotSupportedException %7B%0D%0A%09%09return new LinkReference(this);%0D%0A%09%7D%0D%0A%0D%0A
 %7D%0D%0A

Copy constructor.