/***********************************************************
Copyright (C) 2004 VeriSign, Inc.

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., 59 Temple Place, Suite 330, Boston, MA 02111-0107  USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
 ***********************************************************/

package com.verisign.epp.codec.contact;

import java.util.Vector;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import com.verisign.epp.codec.gen.EPPAuthInfo;
import com.verisign.epp.codec.gen.EPPCodecException;
import com.verisign.epp.codec.gen.EPPCreateCmd;
import com.verisign.epp.codec.gen.EPPDecodeException;
import com.verisign.epp.codec.gen.EPPEncodeException;
import com.verisign.epp.codec.gen.EPPUtil;
import com.verisign.epp.util.EPPEnv;
import com.verisign.epp.util.EqualityUtil;

/**
 * Represents an EPP Contact &lt;create&gt; command, which provides a transform
 * that allows a client to create a contact object. In addition to the standard
 * EPP command elements, the &lt;create&gt; command MUST contain a
 * &lt;contact:create&gt; element that identifies the contact namespace and the
 * location of the contact schema. The &lt;contact:create&gt; element contains
 * the following child elements: <br>
 * <br>
 * <ul>
 * <li>A &lt;contact:id&gt; element that contains the server-unique identifier
 * of the contact object. Use {@code getId} and {@code setId} to get and set the
 * elements.</li>
 * <li>A &lt;contact:postalInfo&gt; element that contains the postal contacts.
 * Use {@code getPostalInfo}, {@code addPostalInfo} and {@code setPostalInfo} to
 * get, add and set the elements.</li>
 * <li>An OPTIONAL &lt;contact:i15d&gt; ("i15d" is short for
 * "internationalized") element that contains child elements whose content SHALL
 * be represented in unrestricted UTF-8. Use {@code getI15d} and {@code setI15d}
 * to get and set the elements.</li>
 * <li>An OPTIONAL &lt;contact:voice&gt; element that contains the contact's
 * voice telephone number. Use {@code getVoice} and {@code setVoice} to get and
 * set the elements.</li>
 * <li>An OPTIONAL &lt;contact:fax&gt; element that contains the contact's
 * facsimile telephone number. Use {@code getFax} and {@code setFax} to get and
 * set the elements.</li>
 * <li>A &lt;contact:email&gt; element that contains the contact's e-mail
 * address. Use {@code getEmail} and {@code setEmail} to get and set the
 * elements.</li>
 * <li>A &lt;contact:authInfo&gt; element that contains authorization
 * information associated with the contact object. This element MUST NOT be
 * provided if the querying client is not the current sponsoring client. Use
 * {@code getAuthInfo} and {@code setAuthInfo} to get and set the element.</li>
 * </ul>
 * <br>
 * {@code EPPContactCreateReponse} is the response associated with
 * {@code EPPContactCreateCmd}. <br>
 * <br>
 *
 * @see com.verisign.epp.codec.gen.EPPResponse
 * @see com.verisign.epp.codec.contact.EPPContactPostalDefinition
 */
public class EPPContactCreateCmd extends EPPCreateCmd {
	/** XML Element Name of {@code EPPContactCreateCmd} root element. */
	final static String ELM_NAME = "contact:create";

	/** XML Element Name of {@code EPPContactCreateCmd} root element. */
	private final static String ELM_CONTACT_POSTAL_INFO = "contact:postalInfo";

	/** XML Element Name of {@code EPPContactCreateCmd} root element. */
	private final static String ELM_CONTACT_AUTHINFO = "contact:authInfo";

	/** XML Element Name of {@code EPPContactCreateCmd} root element. */
	private final static String ELM_CONTACT_EMAIL = "contact:email";

	/** XML Element Name of {@code EPPContactCreateCmd} root element. */
	private final static String ELM_CONTACT_FAX = "contact:fax";

	/** XML Element Name of {@code EPPContactCreateCmd} root element. */
	private final static String ELM_CONTACT_ID = "contact:id";

	/** XML Element Name of {@code EPPContactCreateCmd} root element. */
	private final static String ELM_CONTACT_VOICE = "contact:voice";

	/** XML tag name for the {@code disclose} element. */
	private final static String ELM_CONTACT_DISCLOSE = "contact:disclose";

	/**
	 * XML Attribute Name for a phone extension, which applies to fax and voice
	 * numbers.
	 */
	private final static String ATTR_EXT = "x";

	/** postal contacts */
	private java.util.Vector postalContacts = new Vector();

	/** authorization information for contact */
	private com.verisign.epp.codec.gen.EPPAuthInfo authInfo = null;

	/** disclose information of contact */
	private com.verisign.epp.codec.contact.EPPContactDisclose disclose = null;

	/** email for contact */
	private String email = null;

	/** fax number for contact */
	private String fax = null;

	/** fax extension number for contact */
	private String faxExt = null;

	/** ID for contact */
	private String id = null;

	/** voice number for contact */
	private String voice = null;

	/** voice extension number for contact */
	private String voiceExt = null;

	/**
	 * Default constructor of EPPContactCreateCmd Allocates a new
	 * {@code EPPContactCreateCmd} with default attribute values.
	 */
	public EPPContactCreateCmd() {
	}

	/**
	 * Constructor of EPPContactCreateCmd Allocates a new
	 * {@code EPPContactCreateCmd} with the contact definition information.
	 *
	 * @param aTransId
	 *           command transaction id
	 */
	public EPPContactCreateCmd(String aTransId) {
		super(aTransId);
	}

	/**
	 * Constructor of EPPContactCreateCmd Allocates a new
	 * {@code EPPContactCreateCmd} with the contact definition information.
	 *
	 * @param aTransId
	 *           command transaction id
	 * @param aId
	 *           String ID
	 * @param aPostalContact
	 *           postalInfo element of contact
	 * @param aEmail
	 *           String email
	 * @param aAuthInfo
	 *           authorization information
	 */
	public EPPContactCreateCmd(String aTransId, String aId, EPPContactPostalDefinition aPostalContact, String aEmail,
	      EPPAuthInfo aAuthInfo) {
		super(aTransId);

		this.id = aId;
		this.postalContacts.add(aPostalContact);
		this.voice = null;
		this.fax = null;
		this.email = aEmail;
		this.authInfo = aAuthInfo;
		this.authInfo.setRootName(EPPContactMapFactory.NS, EPPContactMapFactory.ELM_CONTACT_AUTHINFO);
	}

	/**
	 * Get the EPP command Namespace associated with EPPContactCreateCmd.
	 *
	 * @return {@code EPPContactMapFactory.NS}
	 */
	@Override
	public String getNamespace() {
		return EPPContactMapFactory.NS;
	}

	/**
	 * Validate the state of the {@code EPPContactCreateCmd} instance. A valid
	 * state means that all of the required attributes have been set. If
	 * validateState returns without an exception, the state is valid. If the
	 * state is not valid, the EPPCodecException will contain a description of
	 * the error. This will contain the name of the attribute that is not valid.
	 *
	 * @throws EPPCodecException
	 *            If invalid state
	 */
	private void validateState() throws EPPCodecException {
		if (!hasId()) {
			throw new EPPCodecException("required attribute contact id is not set");
		}

		if (!hasPostalContacts()) {
			throw new EPPCodecException("required attribute contact postalInfo is not set");
		}

		if (!hasEmail()) {
			throw new EPPCodecException("required attribute contact email is not set");
		}

		if (!hasAuthInfo()) {
			throw new EPPCodecException("required attribute contact authInfo is not set");
		}
	}

	/**
	 * Validate the state of the {@code EPPContactCreateCmd} instance with
	 * relaxed validation rules. For relaxed validations contact Id, postal info
	 * type and authinfo are mandatory. A valid state means that all of the
	 * required attributes have been set. If validateState returns without an
	 * exception, the state is valid. If the state is not valid, the
	 * EPPCodecException will contain a description of the error. This will
	 * contain the name of the attribute that is not valid.
	 *
	 * @throws EPPCodecException
	 *            throws EPPCodeException if validation fails
	 */
	private void relaxedValidateState() throws EPPCodecException {
		if (!hasId()) {
			throw new EPPCodecException("required attribute contact id is not set");
		}

		if (!hasPostalContacts()) {
			throw new EPPCodecException("required attribute contact postalInfo is not set");
		}

		if (!hasAuthInfo()) {
			throw new EPPCodecException("required attribute contact authInfo is not set");
		}
	}

	/**
	 * Encode a DOM Element tree from the attributes of the EPPContactCreateCmd
	 * instance.
	 *
	 * @param aDocument
	 *           - DOM Document that is being built. Used as an Element factory.
	 * @return Root DOM Element representing the EPPContactCreateCmd instance.
	 * @exception EPPEncodeException
	 *               Unable to encode EPPContactCreateCmd instance.
	 */
	protected Element doEncode(Document aDocument) throws EPPEncodeException {
		Element currElm = null;
		Text currVal = null;

		try {
			if (EPPEnv.isContactRelaxedValidation()) {
				relaxedValidateState();
			}
			else {
				validateState();
			}
		}
		catch (EPPCodecException e) {
			throw new EPPEncodeException("Invalid state on EPPContactCreateCmd.encode: " + e);
		}

		Element root = aDocument.createElementNS(EPPContactMapFactory.NS, ELM_NAME);

		root.setAttribute("xmlns:contact", EPPContactMapFactory.NS);

		// id
		EPPUtil.encodeString(aDocument, root, this.id, EPPContactMapFactory.NS, ELM_CONTACT_ID);

		// postalInfo
		EPPUtil.encodeCompVector(aDocument, root, this.postalContacts);

		// voice
		if (hasVoice()) {
			currElm = aDocument.createElementNS(EPPContactMapFactory.NS, ELM_CONTACT_VOICE);
			currVal = aDocument.createTextNode(this.voice);

			// voiceExt
			if (hasVoiceExt()) {
				currElm.setAttribute(ATTR_EXT, this.voiceExt);
			}

			currElm.appendChild(currVal);
			root.appendChild(currElm);
		}

		// fax
		if (hasFax()) {
			currElm = aDocument.createElementNS(EPPContactMapFactory.NS, ELM_CONTACT_FAX);
			currVal = aDocument.createTextNode(this.fax);

			// faxExt
			if (hasFaxExt()) {
				currElm.setAttribute(ATTR_EXT, this.faxExt);
			}

			currElm.appendChild(currVal);
			root.appendChild(currElm);
		}

		// email
		EPPUtil.encodeString(aDocument, root, this.email, EPPContactMapFactory.NS, ELM_CONTACT_EMAIL);

		// authInfo
		EPPUtil.encodeComp(aDocument, root, this.authInfo);

		// disclose
		EPPUtil.encodeComp(aDocument, root, this.disclose);

		return root;
	}

	/**
	 * Decode the EPPContactCreateCmd attributes from the aElement DOM Element
	 * tree.
	 *
	 * @param aElement
	 *           - Root DOM Element to decode EPPContactCreateCmd from.
	 * @exception EPPDecodeException
	 *               Unable to decode aElement
	 */
	protected void doDecode(Element aElement) throws EPPDecodeException {
		Element currElm = null;

		// id
		this.id = EPPUtil.decodeString(aElement, EPPContactMapFactory.NS, ELM_CONTACT_ID);

		// postalContacts
		this.postalContacts = EPPUtil.decodeCompVector(aElement, EPPContactMapFactory.NS, ELM_CONTACT_POSTAL_INFO,
		      EPPContactPostalDefinition.class);

		// voice
		this.voice = EPPUtil.decodeString(aElement, EPPContactMapFactory.NS, ELM_CONTACT_VOICE);

		// voiceExt
		if (hasVoice()) {
			currElm = EPPUtil.getElementByTagNameNS(aElement, EPPContactMapFactory.NS, ELM_CONTACT_VOICE);
			this.voiceExt = currElm.getAttribute(ATTR_EXT);

			if (this.voiceExt.length() == 0) {
				this.voiceExt = null;
			}
		}
		else {
			this.voiceExt = null;
		}

		// fax
		this.fax = EPPUtil.decodeString(aElement, EPPContactMapFactory.NS, ELM_CONTACT_FAX);

		// faxExt
		if (hasFax()) {
			currElm = EPPUtil.getElementByTagNameNS(aElement, EPPContactMapFactory.NS, ELM_CONTACT_FAX);
			this.faxExt = currElm.getAttribute(ATTR_EXT);

			if (this.faxExt.length() == 0) {
				this.faxExt = null;
			}
		}
		else {
			this.faxExt = null;
		}

		// email
		this.email = EPPUtil.decodeString(aElement, EPPContactMapFactory.NS, ELM_CONTACT_EMAIL);

		// authInfo
		this.authInfo = (EPPAuthInfo) EPPUtil.decodeComp(aElement, EPPContactMapFactory.NS, ELM_CONTACT_AUTHINFO,
		      EPPAuthInfo.class);

		// disclose
		this.disclose = (EPPContactDisclose) EPPUtil.decodeComp(aElement, EPPContactMapFactory.NS, ELM_CONTACT_DISCLOSE,
		      EPPContactDisclose.class);
	}

	/**
	 * Compare an instance of {@code EPPContactCreateCmd} with this instance.
	 *
	 * @param aObject
	 *           Object to compare with.
	 * @return {@code true} if equal; {@code false} otherwise
	 */
	@Override
	public boolean equals(Object aObject) {
		if (!(aObject instanceof EPPContactCreateCmd) || !super.equals(aObject)) {
			return false;
		}

		EPPContactCreateCmd theComp = (EPPContactCreateCmd) aObject;

		// id
		if (!EqualityUtil.equals(this.id, theComp.id)) {
			return false;
		}

		// postalContacts
		if (!EPPUtil.equalVectors(this.postalContacts, theComp.postalContacts)) {
			return false;
		}

		// voice
		if (!EqualityUtil.equals(this.voice, theComp.voice)) {
			return false;
		}

		// voiceExt
		if (!EqualityUtil.equals(this.voiceExt, theComp.voiceExt)) {
			return false;
		}

		// fax
		if (!EqualityUtil.equals(this.fax, theComp.fax)) {
			return false;
		}

		// faxExt
		if (!EqualityUtil.equals(this.faxExt, theComp.faxExt)) {
			return false;
		}

		// email
		if (!EqualityUtil.equals(this.email, theComp.email)) {
			return false;
		}

		// authInfo
		if (!EqualityUtil.equals(this.authInfo, theComp.authInfo)) {
			return false;
		}

		// disclose
		if (!EqualityUtil.equals(this.disclose, theComp.disclose)) {
			return false;

		}
		return true;
	}

	/**
	 * Clone {@code EPPContactCreateCmd}.
	 *
	 * @return clone of {@code EPPContactCreateCmd}
	 * @exception CloneNotSupportedException
	 *               standard Object.clone exception
	 */
	@Override
	public Object clone() throws CloneNotSupportedException {
		EPPContactCreateCmd clone = (EPPContactCreateCmd) super.clone();

		if (hasPostalContacts()) {
			clone.postalContacts = (Vector) this.postalContacts.clone();

			for (int i = 0; i < this.postalContacts.size(); i++) {
				clone.postalContacts.setElementAt(((EPPContactPostalDefinition) this.postalContacts.elementAt(i)).clone(),
				      i);
			}
		}

		if (hasAuthInfo()) {
			clone.authInfo = (EPPAuthInfo) this.authInfo.clone();
		}

		if (hasDisclose()) {
			clone.disclose = (EPPContactDisclose) this.disclose.clone();
		}

		return clone;
	}

	/**
	 * Implementation of {@code Object.toString}, which will result in an
	 * indented XML {@code String} representation of the concrete
	 * {@code EPPCodecComponent}.
	 *
	 * @return Indented XML {@code String} if successful; {@code ERROR}
	 *         otherwise.
	 */
	@Override
	public String toString() {
		return EPPUtil.toString(this);
	}

	/**
	 * Returns {@code true} if the contact has postal info.
	 *
	 * @return {@code true} if the contact has postal info {@code false}
	 *         otherwise
	 */
	public boolean hasPostalContacts() {
		return (this.postalContacts != null && this.postalContacts.elements().hasMoreElements());
	}
	
	/**
	 * Get postalInfo elements of contact.
	 *
	 * @return java.util.Vector
	 */
	public Vector getPostalInfo() {
		return this.postalContacts;
	}

	/**
	 * Set contact postalInfo.
	 *
	 * @param newPostalContacts
	 *           java.util.Vector
	 */
	public void setPostalInfo(Vector newPostalContacts) {
		this.postalContacts = newPostalContacts;
	}


	/**
	 * Adds contact postalInfo.
	 *
	 * @param newPostalInfo
	 *           com.verisign.epp.codec.contact.EPPContactPostalDefinition
	 */
	public void addPostalInfo(EPPContactPostalDefinition newPostalInfo) {
		// clone necessary here
		EPPContactPostalDefinition aPostalContact = null;

		if (newPostalInfo != null) {
			try {
				aPostalContact = (EPPContactPostalDefinition) newPostalInfo.clone();
			}
			catch (CloneNotSupportedException e) {
				// Nothing needs to be done here
			}

			this.postalContacts.add(newPostalInfo);
		}
	}

	/**
	 * Returns {@code true} if the contact has auth info.
	 *
	 * @return {@code true} if the contact has auth info info {@code false}
	 *         otherwise
	 */
	public boolean hasAuthInfo() {
		return (this.authInfo != null);
	}
	
	/**
	 * Get authorization information.
	 *
	 * @return com.verisign.epp.codec.gen.EPPAuthInfo
	 */
	public EPPAuthInfo getAuthInfo() {
		return this.authInfo;
	}

	/**
	 * Set authorization information.
	 *
	 * @param newAuthInfo
	 *           com.verisign.epp.codec.gen.EPPAuthInfo
	 */
	public void setAuthInfo(EPPAuthInfo newAuthInfo) {
		if (newAuthInfo != null) {
			this.authInfo = newAuthInfo;
			this.authInfo.setRootName(EPPContactMapFactory.NS, EPPContactMapFactory.ELM_CONTACT_AUTHINFO);
		}
	}

	
	/**
	 * Returns {@code true} if the contact has disclose flag.
	 *
	 * @return {@code true} if the contact has disclose flag {@code false}
	 *         otherwise
	 */
	public boolean hasDisclose() {
		return (this.disclose != null);
	}
	
	/**
	 * Get disclose information.
	 *
	 * @return Disclose information if defined; {@code null} otherwise;
	 */
	public EPPContactDisclose getDisclose() {
		return this.disclose;
	}

	/**
	 * Set disclose information.
	 *
	 * @param newDisclose
	 *           com.verisign.epp.codec.gen.EPPContactDisclose
	 */
	public void setDisclose(EPPContactDisclose newDisclose) {
		if (newDisclose != null) {
			this.disclose = newDisclose;
			this.disclose.setRootName(ELM_CONTACT_DISCLOSE);
		}
	}

	/**
	 * Returns {@code true} if the contact has email.
	 *
	 * @return {@code true} if the contact has email {@code false} otherwise
	 */
	public boolean hasEmail() {
		return (this.email != null);
	}

	/**
	 * Get email.
	 *
	 * @return email if defined; {@code null} otherwise.
	 */
	public String getEmail() {
		return this.email;
	}

	/**
	 * Set email.
	 *
	 * @param newEmail
	 *           String
	 */
	public void setEmail(String newEmail) {
		this.email = newEmail;
	}

	/**
	 * Returns {@code true} if the contact has fax.
	 *
	 * @return {@code true} if the contact has fax {@code false} otherwise
	 */
	public boolean hasFax() {
		return (this.fax != null);
	}
	
	/**
	 * Get fax number.
	 *
	 * @return Fax number if defined; {@code null} otherwise.
	 */
	public String getFax() {
		return this.fax;
	}

	/**
	 * Set fax number.
	 *
	 * @param newFax
	 *           Fax number
	 */
	public void setFax(String newFax) {
		this.fax = newFax;
	}

	/**
	 * Returns {@code true} if the contact has fax extension.
	 *
	 * @return {@code true} if the contact has fax extension {@code false}
	 *         otherwise
	 */
	public boolean hasFaxExt() {
		return (this.faxExt != null);
	}

	
	/**
	 * Get fax number extension.
	 *
	 * @return fax number extension if defined; {@code null} otherwise.
	 */
	public String getFaxExt() {
		return this.faxExt;
	}
	
	/**
	 * Set fax number extension.
	 *
	 * @param newFaxExt
	 *           Fax number extension
	 */
	public void setFaxExt(String newFaxExt) {
		this.faxExt = newFaxExt;
	}
	
	/**
	 * Returns {@code true} if the contact has contact ID.
	 *
	 * @return {@code true} if the contact has contact ID {@code false} otherwise
	 */
	public boolean hasId() {
		return (this.id != null);
	}

	/**
	 * Gets the contact id.
	 *
	 * @return Contact id if set; {@code null} otherwise.
	 */
	public String getId() {
		return this.id;
	}
	
	/**
	 * Set the contact id.
	 *
	 * @param aId
	 *           String
	 */
	public void setId(String aId) {
		this.id = aId;
	}
	
	/**
	 * Returns {@code true} if the contact has voice.
	 *
	 * @return {@code true} if the contact has voice {@code false} otherwise
	 */
	public boolean hasVoice() {
		return (this.voice != null);
	}


	/**
	 * Get voice number.
	 *
	 * @return Voice number if defined; {@code null} otherwise.
	 */
	public String getVoice() {
		return this.voice;
	}

	/**
	 * Set contact voice number.
	 *
	 * @param newVoice
	 *           voice number
	 */
	public void setVoice(String newVoice) {
		this.voice = newVoice;
	}

	/**
	 * Returns {@code true} if the contact has voice extension.
	 *
	 * @return {@code true} if the contact has voice extension {@code false}
	 *         otherwise
	 */
	public boolean hasVoiceExt() {
		return (this.voiceExt != null);
	}	

	/**
	 * Get voice number extension.
	 *
	 * @return Voice number extension if defined; {@code null} otherwise.
	 */
	public String getVoiceExt() {
		return this.voiceExt;
	}

	/**
	 * Set contact voice extension.
	 *
	 * @param newVoiceExt
	 *           voice extension
	 */
	public void setVoiceExt(String newVoiceExt) {
		this.voiceExt = newVoiceExt;
	}
}
