/***********************************************************
Copyright (C) 2013 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-1307 USA

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

package com.verisign.epp.codec.relateddomainext;

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

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.verisign.epp.codec.gen.EPPCodecComponent;
import com.verisign.epp.codec.gen.EPPCodecException;
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.EPPCatFactory;

/**
 * EPPCodecComponent that encodes and decodes a &lt;relDom:infData&gt; tag
 * <p>
 * Title: EPP 1.0 Related Domain - infData tag
 * </p>
 * <p>
 * Description: The EPPRelatedDomainExtInfData object represents the collection
 * of domains in a family of related domains. As such it is composed of a
 * collection of {@link EPPRelatedDomainExtGroup} objects. <br>
 * As XML, it is represented by a &lt;relDom:infData&gt; element containing a
 * number of &lt;relDom:group&gt; elements.
 * </p>
 */
public class EPPRelatedDomainExtInfData implements EPPCodecComponent {

	/**
	 * Serial version id - increment this if the structure changes.
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * The groups of related domains in the family.
	 */
	private List<EPPRelatedDomainExtGroup> relatedDomainGroup;

	/**
	 * Log4j category for logging
	 */
	private static Logger cat = Logger.getLogger(EPPRelatedDomainExtInfData.class.getName(),
	      EPPCatFactory.getInstance().getFactory());

	/**
	 * Element tag name for the create
	 */
	public static final String ELM_NAME = EPPRelatedDomainExtFactory.NS_PREFIX + ":infData";

	/**
	 * Default constructor
	 */
	public EPPRelatedDomainExtInfData() {
	}

	/**
	 * A deep clone of the EPPRelatedDomainExtInfData.
	 * 
	 * @see java.lang.Object#clone()
	 */
	public Object clone() throws CloneNotSupportedException {
		final EPPRelatedDomainExtInfData theClone = new EPPRelatedDomainExtInfData();

		if (this.relatedDomainGroup != null) {

			theClone.relatedDomainGroup = new ArrayList<EPPRelatedDomainExtGroup>();

			for (EPPRelatedDomainExtGroup group : this.relatedDomainGroup) {

				if (group != null) {
					theClone.relatedDomainGroup.add((EPPRelatedDomainExtGroup) group.clone());
				}
				else {
					theClone.relatedDomainGroup.add(null);
				}

			}

		}
		else {
			theClone.relatedDomainGroup = null;
		}

		return theClone;
	}

	/**
	 * Populate the data of this instance with the data stored in the given
	 * Element of the DOM tree
	 * 
	 * @param aElement
	 *           The root element of the report fragment of XML
	 * @throws EPPDecodeException
	 *            Thrown if any errors occur during decoding.
	 */
	public void decode(final Element aElement) throws EPPDecodeException {
		this.relatedDomainGroup = EPPUtil.decodeCompList(aElement, EPPRelatedDomainExtFactory.NS,
		      EPPRelatedDomainExtGroup.ELM_NAME, EPPRelatedDomainExtGroup.class);
	}

	/**
	 * Append all data from this infData to the given DOM Document
	 * 
	 * @param aDocument
	 *           The DOM Document to append data to
	 * @return Encoded DOM {@code Element}
	 * @throws EPPEncodeException
	 *            Thrown when errors occur during the encode attempt or if the
	 *            instance is invalid.
	 */
	public Element encode(final Document aDocument) throws EPPEncodeException {

		if (aDocument == null) {
			throw new EPPEncodeException("aDocument is null" + " in EPPRelatedDomainExtInfData.encode(Document)");
		}

		try {
			// Validate States
			validateState();
		}
		catch (final EPPCodecException e) {
			cat.error("EPPRelatedDomainExtInfData.encode(): Invalid state on encode: " + e);
			throw new EPPEncodeException("EPPRelatedDomainExtInfData invalid state: " + e);
		}

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

		EPPUtil.encodeCompList(aDocument, root, this.relatedDomainGroup);
		return root;
	}

	public boolean equals(final Object aObj) {
		if (!(aObj instanceof EPPRelatedDomainExtInfData)) {
			return false;
		}

		final EPPRelatedDomainExtInfData theComp = (EPPRelatedDomainExtInfData) aObj;

		if (!EPPUtil.equalLists(this.relatedDomainGroup, theComp.relatedDomainGroup)) {
			cat.error("EPPRelatedDomainExtInfData.equals(): relatedDomainGroup not equal");
			return false;
		}

		return true;
	}

	/**
	 * Adds a domain represented by {@link EPPRelatedDomainExtGroup} to the list
	 * of groups.
	 * 
	 * @param aGroup
	 *           A group to add to the list.
	 */
	public void addGroup(EPPRelatedDomainExtGroup aGroup) {
		if (this.relatedDomainGroup == null) {
			this.relatedDomainGroup = new ArrayList<EPPRelatedDomainExtGroup>();
		}

		this.relatedDomainGroup.add(aGroup);
	}

	/**
	 * Gets the list of groups represented by {@link EPPRelatedDomainExtGroup}.
	 * 
	 * @return the group if defined; {@code null} otherwise.
	 */
	public List<EPPRelatedDomainExtGroup> getGroup() {
		return this.relatedDomainGroup;
	}

	/**
	 * Sets the list of groups represented by {@link EPPRelatedDomainExtGroup}.
	 * 
	 * @param aGroup
	 *           List of related groups
	 */
	public void setGroup(final List<EPPRelatedDomainExtGroup> aGroup) {
		this.relatedDomainGroup = aGroup;
	}

	/**
	 * 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 the XML namespace associated with the {@code EPPCodecComponent}.
	 * 
	 * @return XML namespace for the {@code EPPCodecComponent}.
	 */
	@Override
	public String getNamespace() {
		return EPPRelatedDomainExtFactory.NS;
	}

	/**
	 * Validate the state of the {@code EPPRelatedDomainExtInfData} 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 {@code EPPCodecException} will contain a
	 * description of the error. throws EPPCodecException State error. This will
	 * contain the name of the attribute that is not valid.
	 * 
	 * @throws EPPCodecException
	 *            Thrown if the instance is in an invalid state
	 */
	private void validateState() throws EPPCodecException {
		if ((this.relatedDomainGroup == null)) {
			throw new EPPCodecException("EPPRelatedDomainExtGroup is null.");
		}
	}

}
