/***********************************************************
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-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
***********************************************************/
package com.verisign.epp.codec.domain;

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

import com.verisign.epp.codec.gen.EPPDecodeException;
import com.verisign.epp.codec.gen.EPPEncodeException;
import com.verisign.epp.codec.gen.EPPUpdateCmd;
import com.verisign.epp.codec.gen.EPPUtil;

/**
 * Represents an EPP Domain &lt;update&gt; command. The EPP &lt;update&gt;
 * command provides a transform operation that allows a client to modify the
 * attributes of a domain object. In addition to the standard EPP command
 * elements, the &lt;update&gt; command MUST contain a &lt;domain:update&gt;
 * element that identifies the domain namespace and the location of the domain
 * schema. In addition to The &lt;domain:update&gt; element SHALL contain the
 * following child elements: <br>
 * <br>
 *
 * <ul>
 * <li>A &lt;domain:name&gt; element that contains the fully qualified domain
 * name of the object to be updated. Use {@code getName} and {@code setName} to
 * get and set the element.</li>
 * <li>An OPTIONAL &lt;domain:add&gt; element that contains attribute values to
 * be added to the domain object. Use {@code getAdd} and {@code setAdd} to get
 * and set the element.</li>
 * <li>An OPTIONAL &lt;domain:chg&gt; element that contains attribute values to
 * be change to the domain object. Use {@code getChange} and {@code setChange}
 * to get and set the element.</li>
 * <li>An OPTIONAL &lt;domain:rem&gt; element that contains attribute values to
 * be removed from the domain object. Use {@code getRemove} and
 * {@code setRemove} to get and set the element.</li>
 * </ul>
 *
 * <br>
 * {@code EPPReponse} is the response associated with
 * {@code EPPDomainUpdateCmd}. <br>
 * <br>
 *
 * @see com.verisign.epp.codec.gen.EPPResponse
 * @see com.verisign.epp.codec.domain.EPPDomainAddRemove
 */
public class EPPDomainUpdateCmd extends EPPUpdateCmd {

  /**
   * XML local name for {@code EPPDomainUpdateCmd}.
   */
  public static final String ELM_LOCALNAME = "update";

  /**
   * XML Element Name of {@code EPPDomainUpdateCmd} root element.
   */
  public static final String ELM_NAME = EPPDomainMapFactory.NS_PREFIX + ":" + ELM_LOCALNAME;

  /**
   * XML Element Name of a domain name in a {@code EPPDomainDeleteCmd}.
   */
  private final static String ELM_DOMAIN_NAME = "name";

  /**
   * Domain Name of domain to update.
   */
  private String name = null;

  /**
   * Attributes to add
   */
  private EPPDomainAddRemove add = null;

  /**
   * Attributes to remove
   */
  private EPPDomainAddRemove remove = null;

  /**
   * Attributes to change
   */
  private EPPDomainAddRemove change = null;

  /**
   * {@code EPPDomainUpdateCmd} default constructor. The name is initialized to
   * {@code null}. The name must be set before invoking {@code encode}.
   */
  public EPPDomainUpdateCmd() {
  }

  /**
   * {@code EPPDomainUpdateCmd} default constructor. The name is initialized to
   * {@code null}. The name must be set before invoking {@code encode}.
   *
   * @param aName
   *           Domain name to update
   */
  public EPPDomainUpdateCmd(String aName) {
    this.name = aName;
  }

  /**
   * {@code EPPDomainUpdateCmd} constructor that takes the required attributes
   * as arguments.
   *
   * @param aTransId
   *           Transaction Id associated with the command.
   * @param aName
   *           Domain name to update.
   * @param aAdd
   *           Attributes to add to the domain. {@code null} if no additions.
   * @param aRemove
   *           Attributes to remove from the domain. {@code null} if no
   *           removals.
   * @param aChange
   *           Attribute to change. {@code null} if no changes.
   */
  public EPPDomainUpdateCmd(String aTransId, String aName, EPPDomainAddRemove aAdd, EPPDomainAddRemove aRemove,
        EPPDomainAddRemove aChange) {
    super(aTransId);

    this.name = aName;

    setAdd(aAdd);
    setRemove(aRemove);
    setChange(aChange);
  }

  /**
   * Gets the EPP command Namespace associated with {@code EPPDomainUpdateCmd}.
   *
   * @return {@code EPPDomainMapFactory.NS}
   */
  @Override
  public String getNamespace() {
    return EPPDomainMapFactory.NS;
  }

	/**
	 * Gets the key for the domain name object, which is the domain name.
	 * 
	 * @return The domain name if set; {@code null} otherwise.
	 */
	@Override
	public String getKey() {
		return this.getName();
	}
  
  /**
   * Encode a DOM Element tree from the attributes of the
   * {@code EPPDomainUpdateCmd} instance.
   *
   * @param aDocument
   *           DOM Document that is being built. Used as an Element factory.
   *
   * @return Root DOM Element representing the {@code EPPDomainUpdateCmd}
   *         instance.
   *
   * @exception EPPEncodeException
   *               Unable to encode {@code EPPDomainUpdateCmd} instance.
   */
  @Override
  protected Element doEncode(Document aDocument) throws EPPEncodeException {
    // Validate state
    if (this.name == null) {
      throw new EPPEncodeException("name required attribute is not set");
    }

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

    // Name
    EPPUtil.encodeString(aDocument, root, this.name, EPPDomainMapFactory.NS,
          EPPDomainMapFactory.NS_PREFIX + ":" + ELM_DOMAIN_NAME);

    // Add
    EPPUtil.encodeComp(aDocument, root, this.add);

    // Remove
    EPPUtil.encodeComp(aDocument, root, this.remove);

    // Change
    EPPUtil.encodeComp(aDocument, root, this.change);

    return root;
  }

  /**
   * Decode the {@code EPPDomainUpdateCmd} attributes from the aElement DOM
   * Element tree.
   *
   * @param aElement
   *           Root DOM Element to decode {@code EPPDomainUpdateCmd} from.
   *
   * @exception EPPDecodeException
   *               Unable to decode aElement
   */
  @Override
  protected void doDecode(Element aElement) throws EPPDecodeException {
    // Domain Name
    this.name = EPPUtil.decodeString(aElement, EPPDomainMapFactory.NS, ELM_DOMAIN_NAME);

    // Add
    this.add = (EPPDomainAddRemove) EPPUtil.decodeComp(aElement, EPPDomainMapFactory.NS, EPPDomainAddRemove.ELM_ADD,
          EPPDomainAddRemove.class);

    if (this.add != null) {
      this.add.setMode(EPPDomainAddRemove.MODE_ADD);
    }

    // Remove
    this.remove = (EPPDomainAddRemove) EPPUtil.decodeComp(aElement, EPPDomainMapFactory.NS,
          EPPDomainAddRemove.ELM_REMOVE, EPPDomainAddRemove.class);

    if (this.remove != null) {
      this.remove.setMode(EPPDomainAddRemove.MODE_REMOVE);
    }

    // Change
    this.change = (EPPDomainAddRemove) EPPUtil.decodeComp(aElement, EPPDomainMapFactory.NS,
          EPPDomainAddRemove.ELM_CHANGE, EPPDomainAddRemove.class);

    if (this.change != null) {
      this.change.setMode(EPPDomainAddRemove.MODE_CHANGE);
    }
  }

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

    if (!super.equals(aObject)) {
      return false;
    }

    EPPDomainUpdateCmd theComp = (EPPDomainUpdateCmd) aObject;

    // Name
    if (!((this.name == null) ? (theComp.name == null) : this.name.equals(theComp.name))) {
      return false;
    }

    // Add
    if (!((this.add == null) ? (theComp.add == null) : this.add.equals(theComp.add))) {
      return false;
    }

    // Remove
    if (!((this.remove == null) ? (theComp.remove == null) : this.remove.equals(theComp.remove))) {
      return false;
    }

    // Change
    if (!((this.change == null) ? (theComp.change == null) : this.change.equals(theComp.change))) {
      return false;
    }

    return true;
  }

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

    if (clone.add != null) {
      clone.add = (EPPDomainAddRemove) this.add.clone();
    }

    if (clone.remove != null) {
      clone.remove = (EPPDomainAddRemove) this.remove.clone();
    }

    if (clone.change != null) {
      clone.change = (EPPDomainAddRemove) this.change.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);
  }

  /**
   * Gets the domain name to update.
   *
   * @return Domain Name if defined; {@code null} otherwise.
   */
  public String getName() {
    return this.name;
  }

  /**
   * Sets the domain name to update.
   *
   * @param aName
   *           Domain Name
   */
  public void setName(String aName) {
    this.name = aName;
  }

  /**
   * Has the add been set?
   *
   * @return {@code true} if the add has been set; {@code false} otherwise.
   */
  public boolean hasAdd() {
    return (this.add != null ? true : false);
  }

  /**
   * Gets the items to add to the domain.
   *
   * @return Object that contains the list of elements to add to the domain if
   *         defined; {@code null} otherwise.
   */
  public EPPDomainAddRemove getAdd() {
    return this.add;
  }

  /**
   * Sets the items to add to the domain.
   *
   * @param aAdd
   *           Object that contains the list of elements to add to the domain.
   */
  public void setAdd(EPPDomainAddRemove aAdd) {
    this.add = aAdd;

    if (this.add != null) {
      this.add.setMode(EPPDomainAddRemove.MODE_ADD);
    }
  }

  /**
   * Has the remove been set?
   *
   * @return {@code true} if the remove has been set; {@code false} otherwise.
   */
  public boolean hasRemove() {
    return (this.remove != null ? true : false);
  }

  /**
   * Gets the items to remove from the domain.
   *
   * @return Object that contains the list of elements to remove from the
   *         domain if defined; {@code null} otherwise.
   */
  public EPPDomainAddRemove getRemove() {
    return this.remove;
  }

  /**
   * Sets the items to remove from the domain.
   *
   * @param aRemove
   *           Object that contains the list of elements to remove from the
   *           domain.
   */
  public void setRemove(EPPDomainAddRemove aRemove) {
    this.remove = aRemove;

    if (this.remove != null) {
      this.remove.setMode(EPPDomainAddRemove.MODE_REMOVE);
    }
  }

  /**
   * Has the change been set?
   *
   * @return {@code true} if the change has been set; {@code false} otherwise.
   */
  public boolean hasChange() {
    return (this.change != null ? true : false);
  }

  /**
   * Gets the items to change to the domain.
   *
   * @return Object that contains the list of elements to change to the domain
   *         if defined; {@code null} otherwise.
   */
  public EPPDomainAddRemove getChange() {
    return this.change;
  }

  /**
   * Sets the items to change to the domain.
   *
   * @param newChange
   *           Object that contains the list of elements to change to the
   *           domain.
   */
  public void setChange(EPPDomainAddRemove newChange) {
    this.change = newChange;

    if (this.change != null) {
      this.change.setMode(EPPDomainAddRemove.MODE_CHANGE);
    }
  }

}
