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

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

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

/**
 * Represents authorization information that is used for validate.
 */
public class EPPValidateAuthInfo implements EPPCodecComponent {

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

  /**
   * password auth info type
   */
  public final static short TYPE_PW = 0;

  /**
   * Extensible auth info type.
   */
  public final static short TYPE_EXT = 1;

  /**
   * Constant for the AuthInfo local name
   */
  public static final String ELM_LOCALNAME = "authInfo";

  /**
   * Constant for the authInfo tag
   */
  public static final String ELM_NAME = EPPValidateMapFactory.NS_PREFIX + ":" + ELM_LOCALNAME;

  /**
   * XML Element name password authorization type
   */
  protected final static String ELM_PW = "pw";

  /**
   * XML Element name extensible authorization type
   */
  protected final static String ELM_EXT = "ext";

  /**
   * XML Element roid attribute name of {@code EPPValidateAuthInfo} root
   * element.
   */
  protected final static String ATTR_ROID = "roid";

  /**
   * Password authorization information.
   */
  protected String password = null;

  /**
   * Extension authorization information.
   */
  protected EPPCodecComponent ext = null;

  /**
   * Type, and default value is {@code TYPE_PW}
   */
  protected short type = TYPE_PW;

  /**
   * roid.
   */
  protected String roid = null;

  /**
   * Default constructor that must have the password or extension attributes
   * set before calling {@code encode}.
   */
  public EPPValidateAuthInfo() {
  }

  /**
   * Constructor that takes just the authorization password.
   *
   * @param aPassword
   *           Authorization password
   */
  public EPPValidateAuthInfo(String aPassword) {
    this.setPassword(aPassword);
  }

  /**
   * Constructor the authorization password and the roid.
   *
   * @param aRoid
   *           Roid of the Registrant
   * @param aPassword
   *           Authorization password
   */
  public EPPValidateAuthInfo(String aPassword, String aRoid) {
    this.setPassword(aPassword);
    this.roid = aRoid;
  }

  /**
   * Constructor that takes just the authorization extension.
   *
   * @param aExt
   *           Extension authorization element
   */
  public EPPValidateAuthInfo(EPPCodecComponent aExt) {
    this.setExt(aExt);
  }

  /**
   * Get Registry Object IDentifier (ROID).
   *
   * @return Registry Object IDentifier (ROID)
   */
  public String getRoid() {
    return this.roid;
  }

  /**
   * Set Registry Object IDentifier (ROID).
   *
   * @param aRoid
   *           The Registry Object IDentifier (ROID) value.
   */
  public void setRoid(String aRoid) {
    this.roid = aRoid;
  }

  /**
   * Gets the password authorization information.
   *
   * @return Authorization password
   */
  public String getPassword() {
    return this.password;
  }

  /**
   * Sets the password authorization information.
   *
   * @param aPassword
   *           Authorization password
   */
  public void setPassword(String aPassword) {
    this.password = aPassword;
    this.type = TYPE_PW;
  }

  /**
   * Gets the extension authorization.
   *
   * @return Authorization extension
   */
  public EPPCodecComponent getExt() {
    return this.ext;
  }

  /**
   * Sets the extension authorization information.
   *
   * @param aExt
   *           Authorization extension
   */
  public void setExt(EPPCodecComponent aExt) {
    this.ext = aExt;
    this.type = TYPE_EXT;
  }

  /**
   * Get the type of the auth info.
   *
   * @return One of the {@code TYPE_} constants.
   */
  public short getType() {
    return this.type;
  }

  /**
   * Set auth info type. The default value is {@code TYPE_PW}.
   *
   * @param aType
   *           One of the {@code TYPE_} constants
   */
  public void setType(short aType) {
    this.type = aType;
  }

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

    clone = (EPPValidateAuthInfo) super.clone();

    if (this.ext != null) {
      clone.ext = (EPPCodecComponent) this.ext.clone();
    }

    return clone;
  }

  /**
   * Encode a DOM Element tree from the attributes of the EPPValidateAuthInfo
   * instance.
   *
   * @param aDocument
   *           - DOM Document that is being built. Used as an Element factory.
   *
   * @return Element - Root DOM Element representing the EPPValidateAuthInfo
   *         instance.
   *
   * @exception EPPEncodeException
   *               - Unable to encode EPPValidateAuthInfo instance.
   */
  @Override
  public Element encode(Document aDocument) throws EPPEncodeException {
    Element root = aDocument.createElementNS(EPPValidateMapFactory.NS, ELM_NAME);

    switch (this.type) {
      case TYPE_PW:

        if (this.password == null) {
          throw new EPPEncodeException("EPPValidateAuthInfo: password is null on call to encode");
        }

        Element thePasswordElm = aDocument.createElementNS(EPPValidateMapFactory.NS_CONTACT,
              EPPValidateMapFactory.NS_PREFIX_CONTACT + ":" + ELM_PW);
        thePasswordElm.appendChild(aDocument.createTextNode(this.password));

        // roid
        if (this.roid != null) {
          thePasswordElm.setAttribute(ATTR_ROID, this.roid);
        }

        root.appendChild(thePasswordElm);

        break;

      case TYPE_EXT:

        if (this.ext == null) {
          throw new EPPEncodeException("EPPValidateAuthInfo: ext is null on call to encode");
        }

        Element theExtElm = aDocument.createElementNS(EPPValidateMapFactory.NS_CONTACT,
              EPPValidateMapFactory.NS_PREFIX_CONTACT + ":" + ELM_EXT);
        EPPUtil.encodeComp(aDocument, theExtElm, this.ext);
        root.appendChild(theExtElm);

        break;

      default:
        throw new EPPEncodeException("EPPValidateAuthInfo: invalid type" + this.type);
    }

    return root;
  }

  /**
   * Decode the EPPValidateAuthInfo attributes from the aElement DOM Element
   * tree.
   *
   * @param aElement
   *           - Root DOM Element to decode EPPDomainContact from.
   *
   * @exception EPPDecodeException
   *               - Unable to decode aElement.
   */
  @Override
  public void decode(Element aElement) throws EPPDecodeException {

    Element theTypeElm = EPPUtil.getFirstElementChild(aElement);

    // password provided?
    if (theTypeElm == null) {
      throw new EPPDecodeException("EPPValidateAuthInfo.decode could not find type child element");
    }
    else if (theTypeElm.getLocalName().equals(ELM_PW)) {
      this.type = TYPE_PW;

      // Get password value
      Node textNode = theTypeElm.getFirstChild();
      if (textNode != null) {
        this.password = textNode.getNodeValue();
      }
      else {
        this.password = "";
      }

      // Get roid
      if (theTypeElm.getAttribute(ATTR_ROID).equals("")) {
        this.roid = null;
      }
      else {
        this.roid = theTypeElm.getAttribute(ATTR_ROID);
      }
    } // extension type
    else if (theTypeElm.getLocalName().equals(ELM_EXT)) {
      this.type = TYPE_EXT;

      Element theExtElm = EPPUtil.getFirstElementChild(theTypeElm);

      // Create extension
      try {
        this.ext = EPPFactory.getInstance().createExtension(theExtElm);
      }
      catch (EPPCodecException e) {
        throw new EPPDecodeException("EPPValidateAuthInfo.decode unable to create authInfo extension object: " + e);
      }
      this.ext.decode(theExtElm);
    }
    else {
      throw new EPPDecodeException(
            "EPPValidateAuthInfo.decode invalid type child element tag name of " + theTypeElm.getTagName());
    }
  }

  /**
   * implements a deep {@code EPPValidateAuthInfo} compare.
   *
   * @param aObject
   *           {@code EPPValidateAuthInfo} instance to compare with
   *
   * @return {@code true} if equal; {@code false} otherwise
   */
  @Override
  public boolean equals(Object aObject) {
    if (!(aObject instanceof EPPValidateAuthInfo)) {
      cat.error(
            "EPPValidateAuthInfo.equals(): " + aObject.getClass().getName() + " not EPPValidateAuthInfo instance");

      return false;
    }

    EPPValidateAuthInfo theComp = (EPPValidateAuthInfo) aObject;

    // Password
    if (!EqualityUtil.equals(this.password, theComp.password)) {
      cat.error("EPPValidateAuthInfo.equals(): password not equal");
      return false;
    }

    // Ext
    if (!EqualityUtil.equals(this.ext, theComp.ext)) {
      cat.error("EPPValidateAuthInfo.equals(): ext not equal");
      return false;
    }

    // Type
    if (!EqualityUtil.equals(this.type, theComp.type)) {
      cat.error("EPPValidateAuthInfo.equals(): type not equal");
      return false;
    }

    // ROID
    if (!EqualityUtil.equals(this.roid, theComp.roid)) {
      cat.error("EPPValidateAuthInfo.equals(): roid not equal");
      return false;
    }

    return true;
  }

  /**
   * 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 EPPCodec.NS;
  }
}
