/***********************************************************
Copyright (C) 2018 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.registry.v02;

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

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

/**
 * {@code EPPRegistryRegex} represents a general regular expression that
 * includes both the expression and an optional description. Since this is a
 * generic regular expression {@code EPPCodecComponent}, used by multiple
 * parent {@code EPPCodecComponent}'s, the root element must be set using
 * the {@link #setRootName(String)}.
 */
public class EPPRegistryRegex implements EPPCodecComponent {
	
	/**
	 * Logger
	 */
	private static Logger cat = Logger.getLogger(EPPRegistryRegex.class.getName(),
	      EPPCatFactory.getInstance().getFactory());


	/**
	 * Regular expression element label.
	 */
	public static final String ELM_EXPRESSION = "expression";

	/**
	 * Regular expression optional description element label.
	 */
	public static final String ELM_DESCRIPTION = "description";

	/**
	 * Language attribute of the explanation element.
	 */
	public static final String ATTR_LANG = "lang";

	/**
	 * Root element label to use.
	 */
	private String rootName = null;

	/**
	 * Regular expression attribute value.
	 */
	private String expression = null;

	/**
	 * Regular expression description attribute value.
	 */
	private String description = null;

	/**
	 * Language of the explanation attribute value.
	 */
	private String lang = null;

	/**
	 * Default constructor for {@code EPPRegistryRegex}.
	 */
	public EPPRegistryRegex() {
		super();
	}

	/**
	 * Constructor that takes the required regular expression value.
	 * 
	 * @param aExpression
	 *            Regular expression value.
	 */
	public EPPRegistryRegex(String aExpression) {
		this();
		this.expression = aExpression;
	}

	/**
	 * Constructor that takes the required regular expression value along with
	 * the optional explanation.
	 * 
	 * @param aExpression
	 *            Regular expression value.
	 * @param aDescription
	 *            Description of the regular expression.
	 */
	public EPPRegistryRegex(String aExpression, String aDescription) {
		this(aExpression);
		this.description = aDescription;
	}

	/**
	 * Constructor that takes the required regular expression value along with
	 * the optional explanation and the language of the explanation.
	 * 
	 * @param aExpression
	 *            Regular expression value.
	 * @param aDescription
	 *            Description of the regular expression.
	 * @param aLang
	 *            Language of the explanation.
	 */
	public EPPRegistryRegex(String aExpression, String aDescription,
			String aLang) {
		this(aExpression);
		this.description = aDescription;
		this.lang = aLang;
	}

	/**
	 * Encode a DOM Element tree from the attributes of the
	 * {@code EPPRegistryRegex} instance.
	 * 
	 * @param aDocument
	 *            DOM Document that is being built. Used as an Element factory.
	 * 
	 * @return Element Root DOM Element representing the
	 *         {@code EPPRegistryRegex} instance.
	 * 
	 * @exception EPPEncodeException
	 *                Unable to encode {@code EPPRegistryRegex} instance.
	 */
	public Element encode(Document aDocument) throws EPPEncodeException {
		// Required attributes set?
		if (rootName == null || rootName.trim().length() == 0) {
			throw new EPPEncodeException("rootName is not set");
		}
		if (expression == null || expression.trim().length() == 0) {
			throw new EPPEncodeException(
					"expression attribute is not set in EPPRegistryRegex.encode");
		}
				
		Element root = aDocument.createElementNS(EPPRegistryMapFactory.NS,
				EPPRegistryMapFactory.NS_PREFIX + ":" + getRootName());
		
		// Expression
		EPPUtil.encodeString(aDocument, root, this.expression,
				EPPRegistryMapFactory.NS, EPPRegistryMapFactory.NS_PREFIX + ":"
						+ ELM_EXPRESSION);
		
		// Description
		if (this.description != null && this.description.trim().length() > 0) {
			if (this.lang == null || this.lang.trim().length() == 0) {
				this.lang = "en";
			}
			Element currElm = aDocument.createElementNS(
					EPPRegistryMapFactory.NS, EPPRegistryMapFactory.NS_PREFIX
							+ ":" + ELM_DESCRIPTION);
			Text currVal = aDocument.createTextNode(description);
			
			// Language
			currElm.setAttribute(ATTR_LANG, lang);
			currElm.appendChild(currVal);
			root.appendChild(currElm);
		}

		return root;
	}

	/**
	 * Decode the {@code EPPRegistryRegex} attributes from the aElement DOM
	 * Element tree.
	 * 
	 * @param aElement
	 *            Root DOM Element to decode {@code EPPRegistryRegex} from.
	 * 
	 * @exception EPPDecodeException
	 *                Unable to decode aElement.
	 */
	public void decode(Element aElement) throws EPPDecodeException {
		this.rootName = aElement.getLocalName();

		// Expression
		this.expression = EPPUtil.decodeString(aElement,
				EPPRegistryMapFactory.NS, ELM_EXPRESSION);
		
		// Description
		this.description = EPPUtil.decodeString(aElement,
				EPPRegistryMapFactory.NS, ELM_DESCRIPTION);
		if (this.description != null) {
			Element theElm = EPPUtil.getElementByTagNameNS(aElement,
					EPPRegistryMapFactory.NS, ELM_DESCRIPTION);
			
			// Language
			this.lang = theElm.getAttribute(ATTR_LANG);
			if (this.lang == null || this.lang.trim().length() == 0) {
				this.lang = "en";
			}
		}

		if (this.expression == null || this.expression.trim().length() == 0) {
			throw new EPPDecodeException(
					"expression attribute is not set in EPPRegistryRegex.decode");
		}
	}

	/**
	 * 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.
	 */
	public String toString() {
		return EPPUtil.toString(this);
	}

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

	/**
	 * implements a deep {@code EPPRegistryRegex} compare.
	 * 
	 * @param aObject
	 *            {@code EPPRegistryRegex} instance to compare with
	 * 
	 * @return true if equal false otherwise
	 */
	public boolean equals(Object aObject) {
		if (!(aObject instanceof EPPRegistryRegex)) {
			return false;
		}

		EPPRegistryRegex theComp = (EPPRegistryRegex) aObject;
		if (!((this.rootName == null) ? (theComp.rootName == null)
				: this.rootName.equals(theComp.rootName))) {
			return false;
		}
		
		// Expression
		if (!EqualityUtil.equals(this.expression, theComp.expression)) {
			cat.error("EPPRegistryRegex.equals(): expression not equal");
			return false;
		}
		
		// Description
		if (!EqualityUtil.equals(this.description, theComp.description)) {
			cat.error("EPPRegistryRegex.equals(): description not equal");
			return false;
		}
		
		// Language
		if (this.description != null) {
			if (!EqualityUtil.equals(this.lang, theComp.lang)) {
				cat.error("EPPRegistryRegex.equals(): lang not equal");
				return false;
			}
		}

		return true;
	}

	/**
	 * Gets the regular expression value.
	 * 
	 * @return Regular expression value if defined; {@code null} otherwise.
	 */
	public String getExpression() {
		return this.expression;
	}

	/**
	 * Sets the regular expression value.
	 * 
	 * @param aExpression
	 *            Regular expression value.
	 */
	public void setExpression(String aExpression) {
		this.expression = aExpression;
	}

	/**
	 * Has description?
	 *
	 * @return {@code true} if the description is defined; {@code false}
	 *         otherwise.
	 */
	public boolean hasDescription() {
		return (this.description != null ? true : false);
	}
	
	/**
	 * Gets the description of the regular expression.
	 * 
	 * @return Regular expression description if defined; {@code null}
	 *         otherwise.
	 */
	public String getDescription() {
		return this.description;
	}

	/**
	 * Sets the description of the regular expression.
	 * 
	 * @param aDescription
	 *            Regular expression description.
	 */
	public void setDescription(String aDescription) {
		this.description = aDescription;
	}

	/**
	 * Gets the root element label (local name).
	 * 
	 * @return Root element label if defined; {@code null} otherwise.
	 */
	String getRootName() {
		return this.rootName;
	}

	/**
	 * Sets the root element label (local name).  If 
	 * the element label includes a XML namespace prefix, the 
	 * namespace prefix is trimmed.  
	 * 
	 * @param aRootName
	 *            Root element label.
	 */
	void setRootName(String aRootName) {
		if (aRootName != null) {
			int namespaceSepIndex = aRootName.indexOf(':');

			if (namespaceSepIndex != -1
					&& aRootName.length() > namespaceSepIndex) {
				aRootName = aRootName.substring(namespaceSepIndex + 1);
			}
		}
		this.rootName = aRootName;
	}

	/**
	 * Gets the language of the explanation.
	 * 
	 * @return Language of the explanation if defined; {@code null}
	 *         otherwise.
	 */
	public String getLang() {
		return this.lang;
	}

	/**
	 * Sets the language of the explanation.
	 * 
	 * @param aLang
	 *            Language of the explanation.
	 */
	public void setLang(String aLang) {
		this.lang = aLang;
	}
	
	/**
	 * Returns the XML namespace associated with the {@code EPPCodecComponent}.
	 * 
	 * @return XML namespace for the {@code EPPCodecComponent}. 
	 */
	@Override
	public String getNamespace() {
		return EPPRegistryMapFactory.NS;
	}
	
}
