/***********************************************************
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.orgext;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

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.EqualityUtil;

/**
 * <code>EPPOrgExtAddRemChg</code> is used to define the organization ids to
 * either add, remove, or change. The <code>EPPOrgextAddRemChg</code> can take
 * zero or more organization ids along with the action using the {@link Action}
 * enumeration.
 */
public class EPPOrgExtAddRemChg implements EPPCodecComponent {

  /**
   * Category for logging
   */
      private static Logger cat = LoggerFactory.getLogger(EPPOrgExtAddRemChg.class);
        

  /**
   * XML root tag for <code>EPPOrgExtAddRemChg</code> add action
   */
  public static final String ELM_ADD_NAME = EPPOrgExtFactory.NS_PREFIX + ":add";
  
  /**
   * XML root tag for <code>EPPOrgExtAddRemChg</code> remove action
   */
  public static final String ELM_REM_NAME = EPPOrgExtFactory.NS_PREFIX + ":rem";
  
  /**
   * XML root tag for <code>EPPOrgExtAddRemChg</code> change action
   */
  public static final String ELM_CHG_NAME = EPPOrgExtFactory.NS_PREFIX + ":chg";
  
  /**
   * Update action, which includes:<br>
   * <ol>
   * <li>ADD - Add the org identifiers.</li>
   * <li>REM - Remove the org identifiers.</li>
   * <li>CHG - Change the org identifiers.</li>
   * </ol>
   */
  public enum Action {
    ADD("add"), REM("rem"), CHG("chg");

    private final String localElm;

    /**
     * Define the local element name of the action.
     * 
     * @param aLocalElm
     *           Local element name of action
     */
    Action(String aLocalElm) {
      this.localElm = aLocalElm;
    }

    /**
     * Get the action given the local element name.
     * 
     * @param aLocalElm
     *           <code>Action</code> enumerated local element name to convert
     *           to an enumerated <code>Action</code> instance.
     * 
     * @return Enumerated <code>Action</code> value matching the
     *         <code>aLocalElm</code> value.
     * 
     * @throws InvalidParameterException
     *            If <code>aLocalElm</code> does not match an enumerated
     *            <code>Action</code> string value.
     */
    public static Action getAction(String aLocalElm) {
      if (aLocalElm == null || aLocalElm.isEmpty()) {
        throw new InvalidParameterException("Action null or empty enum value of is not valid.");
      }
      if (aLocalElm.equals(ADD.localElm)) {
        return ADD;
      }
      else if (aLocalElm.equals(REM.localElm)) {
        return REM;
      }
      else if (aLocalElm.equals(CHG.localElm)) {
        return CHG;
      }
      else {
        throw new InvalidParameterException("Action enum value of " + aLocalElm + " is not valid.");
      }
    }

    /**
     * Convert the enumerated <code>Action</code> value to a local element
     * name <code>String</code>.
     */
    public String toString() {
      return this.localElm;
    }
  }

  /**
   * XML local name for the org identifier element
   */
  private static final String ELM_RESELLER_ID = "id";

  /**
   * Update action to take
   */
  private Action action;

  /**
   * Organization identifiers list to update (add, del, or chg)
   */
  private List<EPPOrgExtId> orgIds = new ArrayList<EPPOrgExtId>();

  /**
   * Default constructor for <code>EPPOrgExtAddRemChg</code>.
   */
  public EPPOrgExtAddRemChg() {
  }

  /**
   * Constructor for <code>EPPOrgExtAddRemChg</code> that takes the action.
   * 
   * @param aAction
   *           Update action to take using the <code>Action</code> enumerated
   *           values.
   */
  public EPPOrgExtAddRemChg(Action aAction) {
    this.setAction(aAction);
  }

  /**
   * Gets the element name of the root element of the add, remove, or change.
   * 
   * @return Root element with the appropriate XML namespace prefix if action
   *         is defined; "undefined" otherwise.
   */
  public String getElmName() {
    if (this.action != null) {
      return EPPOrgExtFactory.NS_PREFIX + ":" + this.action.toString();
    }
    else {
      return "undefined";
    }
  }

  /**
   * Constructor for <code>EPPOrgExtAddRemChg</code> that takes the action and a
   * single organization id.
   * 
   * @param aAction
   *           Update action to take using the <code>Action</code> enumerated
   *           values.
   * @param aOrgId
   *           Org identifier
   */
  public EPPOrgExtAddRemChg(Action aAction, EPPOrgExtId aOrgId) {
    this.setAction(aAction);
    this.addOrgId(aOrgId);
  }

  /**
   * Constructor for <code>EPPOrgExtAddRemChg</code> that takes the action and a
   * list of organization ids.
   * 
   * @param aAction
   *           Update action to take using the <code>Action</code> enumerated
   *           values.
   * @param aOrgIds
   *           Org identifiers
   */
  public EPPOrgExtAddRemChg(Action aAction, List<EPPOrgExtId> aOrgIds) {
    this.setAction(aAction);
    this.setOrgIds(aOrgIds);
  }

  /**
   * Gets the update action to take.
   * 
   * @return the update action using the <code>Action</code> enumerated values.
   */
  public Action getAction() {
    return this.action;
  }

  /**
   * Sets the update action to take.
   * 
   * @param aAction
   *           Update action using the <code>Action</code> enumerated values.
   */
  public void setAction(Action aAction) {
    this.action = aAction;
  }

  /**
   * Are there any organization ids?
   * 
   * @return <code>true</code> if there organization ids; <code>false</code>
   *         otherwise.
   */
  public boolean hasOrgIds() {
    return !this.orgIds.isEmpty();
  }

  /**
   * Returns the list of organization ids. An empty list indicates that there
   * are no organization ids.
   * 
   * @return List of organization ids.
   */
  public List<EPPOrgExtId> getOrgIds() {
    return this.orgIds;
  }

  /**
   * Sets the organization ids.
   * 
   * @param aOrgIds
   *           The list of organization ids. Set to <code>null</code> or an
   *           empty list to indicate that there are no organization ids.
   */
  public void setOrgIds(List<EPPOrgExtId> aOrgIds) {
    if (aOrgIds == null) {
      this.orgIds = new ArrayList<EPPOrgExtId>();
    }
    else {
      this.orgIds = aOrgIds;
    }
  }

  /**
   * Add a organization id to the list of organization ids.
   * 
   * @param aOrgId
   *           organization id to add to the list of organization ids.
   */
  public void addOrgId(EPPOrgExtId aOrgId) {
    this.orgIds.add(aOrgId);
  }

  /**
   * encode instance into a DOM element tree. A DOM Document is passed as an
   * argument and functions as a factory for DOM objects. The root element
   * associated with the instance is created and each instance attribute is
   * appended as a child node.
   * 
   * @param aDocument
   *           DOM Document, which acts is an Element factory
   * 
   * @return Element Root element associated with the object
   * 
   * @exception EPPEncodeException
   *               Error encoding <code>EPPOrgExtAddRemChg</code>
   */
  public Element encode(Document aDocument) throws EPPEncodeException {

    // Check required attributes
    if (this.action == null) {
      throw new EPPEncodeException("Undefined action in EPPOrgExtAddRemChg");
    }

    // Action
    Element root = aDocument.createElementNS(EPPOrgExtFactory.NS, EPPOrgExtFactory.NS_PREFIX + ":" + this.action);

    // Org Identifiers
    EPPUtil.encodeCompList(aDocument, root, this.orgIds);

    return root;
  }

  /**
   * decode a DOM element tree to initialize the instance attributes. The
   * <code>aElement</code> argument represents the root DOM element and is used
   * to traverse the DOM nodes for instance attribute values.
   * 
   * @param aElement
   *           <code>Element</code> to decode
   * 
   * @exception EPPDecodeException
   *               Error decoding <code>Element</code>
   */
  public void decode(Element aElement) throws EPPDecodeException {
    
    // Action
    this.action = Action.getAction(aElement.getLocalName());

    // OrgIds
    this.orgIds = EPPUtil.decodeCompList(aElement, EPPOrgExtFactory.NS, EPPOrgExtId.ELM_NAME, EPPOrgExtId.class);
  }

  /**
   * clone an <code>EPPCodecComponent</code>.
   * 
   * @return clone of concrete <code>EPPOrgExt</code>
   * 
   * @exception CloneNotSupportedException
   *               standard Object.clone exception
   */
  public Object clone() throws CloneNotSupportedException {
    EPPOrgExtAddRemChg clone = (EPPOrgExtAddRemChg) super.clone();

    return clone;
  }

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

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

    EPPOrgExtAddRemChg other = (EPPOrgExtAddRemChg) aObject;

    // Action
    if (!EqualityUtil.equals(this.action, other.action)) {
      cat.error("EPPOrgExtAddRemChg.equals(): action not equal");
      return false;
    }

    // OrgIds
    if (!EqualityUtil.equals(this.orgIds, other.orgIds)) {
      cat.error("EPPOrgExtAddRemChg.equals(): orgIds not equal");
      return false;
    }

    return true;
  }

  /**
   * Returns the XML namespace associated with the
   * <code>EPPCodecComponent</code>.
   * 
   * @return XML namespace for the <code>EPPCodecComponent</code>.
   */
  @Override
  public String getNamespace() {
    return EPPOrgExtFactory.NS;
  }

}
