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

import java.util.HashMap;

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



/**
 * Describe data collection purposes, data recipients, and data retention. Each
 * &lt;statement&gt; element MUST contain a &lt;purpose&gt; element, a
 * &lt;recipient&gt; element, and a &lt;retention&gt; element <br>
 * <br>
 * Use methods {@code getPurpose} and {@code setPurpose } to get and set the
 * purpose element(s), Use {@code getRecipient} and {@code setRecipient} to get
 * and set the recipient element. Use methods {@code getRetention>} and
 * {@code setRetention} to get and set the retention element.
 *
 * @see com.verisign.epp.codec.gen.EPPDcp
 */
public class EPPStatement implements EPPCodecComponent {
  /**
   * Constant used with the Retention attribute that means data persists per
   * business practices.
   */
  public static final short RETENTION_BUSINESS = 0;

  /**
   * Constant used with the Retention attribute that means data persists
   * indefinitely.
   */
  public static final short RETENTION_INDEFINITE = 1;

  /**
   * Constant used with the Retention attribute that means data persists per
   * legal requirements.
   */
  public static final short RETENTION_LEGAL = 2;

  /**
   * Constant used with the Retention attribute that means Data is not
   * persistent, and is not retained for more than a brief period of time
   * necessary to make use of it during the course of a single online
   * interaction
   */
  public static final short RETENTION_NONE = 3;

  /**
   * Constant used with the Retention attribute that means Data persists to
   * meet the stated purpose
   */
  public static final short RETENTION_STATED = 4;

  /** Look up retention constant given retention value tag name */
  private static HashMap retentionElmHash;

  /** Default XML root tag name for {@code statement}element. */
  final static String ELM_NAME = "statement";

  /** Default XML root tag name for {@code retention}element. */
  private static final String ELM_RETENTION = "retention";

  /** XML tag name for the rentention {@code business} element. */
  private static final String ELM_RETENTION_BUSINESS = "business";

  /** XML tag name for the rentention {@code indefinite} element. */
  private static final String ELM_RETENTION_INDEFINITE = "indefinite";

  /** XML tag name for the rentention {@code legal} element. */
  private static final String ELM_RETENTION_LEGAL = "legal";

  /** XML tag name for the rentention {@code none} element. */
  private static final String ELM_RETENTION_NONE = "none";

  /** XML tag name for the rentention {@code stated} element. */
  private static final String ELM_RETENTION_STATED = "stated";

  /**
   * Elements that match the {@code RETENTION_} constant values of the Access
   * attribute.
   */
  private final static String[] retentionElms = { ELM_RETENTION_BUSINESS, ELM_RETENTION_INDEFINITE,
        ELM_RETENTION_LEGAL, ELM_RETENTION_NONE, ELM_RETENTION_STATED };

  static {
    // Setup access element hash
    retentionElmHash = new HashMap();

    retentionElmHash.put(EPPUtil.getLocalName(ELM_RETENTION_BUSINESS), Short.valueOf(RETENTION_BUSINESS));
    retentionElmHash.put(EPPUtil.getLocalName(ELM_RETENTION_INDEFINITE), Short.valueOf(RETENTION_INDEFINITE));
    retentionElmHash.put(EPPUtil.getLocalName(ELM_RETENTION_LEGAL), Short.valueOf(RETENTION_LEGAL));
    retentionElmHash.put(EPPUtil.getLocalName(ELM_RETENTION_NONE), Short.valueOf(RETENTION_NONE));
    retentionElmHash.put(EPPUtil.getLocalName(ELM_RETENTION_STATED), Short.valueOf(RETENTION_STATED));
  }

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

  /** {@code EPPPurpose} instances. */
  private EPPPurpose purpose = null;

  /** {@code EPPRecipient} instances. */
  private EPPRecipient recipient = null;

  /** Describes data retention practices. */
  private short retention = RETENTION_BUSINESS;

  /**
   * Default constructor.
   */
  public EPPStatement() {
  }

  /**
   * Allocates a new {@code EPPStatement} and sets all of the required
   * attributes to the arguments values.
   *
   * @param aRecipient
   *           Describes the recipients of collected data
   * @param aPurpose
   *           Describe the purposes for which data is collected
   * @param aRetention
   *           Describes data retention practices using one of the
   *           {@code RETENTION_} constants.
   */
  public EPPStatement(EPPRecipient aRecipient, EPPPurpose aPurpose, short aRetention) {
    this.recipient = aRecipient;
    this.purpose = aPurpose;
    this.retention = aRetention;
  }

  /*
   * Gets the Recipient object associated with the EPPStatement object.
   *
   * @return Recipients associated with the collected data.
   */
  public EPPRecipient getRecipient() {
    return this.recipient;
  }

  /*
   * Sets the Recipient object,and associate it with the {@code EPPStatement}
   * object
   *
   * @param aRecipient Describes the recipients of collected data
   */
  public void setRecipient(EPPRecipient aRecipient) {
    this.recipient = aRecipient;
  }

  /*
   * Gets the purpose object associated with the {@code EPPStatement} object.
   *
   * @return Purposes for which data is collected
   */
  public EPPPurpose getPurpose() {
    return this.purpose;
  }

  /*
   * Sets the Purpose object,and associate it with the {@code EPPStatement}
   * object
   *
   * @param aPurpose Describe the purposes for which data is collected
   */
  public void setPurpose(EPPPurpose apurpouse) {
    this.purpose = apurpouse;
  }

  /*
   * Gets the data retention policy.
   *
   * @return One of the {@code RETENTION_} constants.
   */
  public short getRetention() {
    return this.retention;
  }

  /*
   * Sets the data retention policy.
   *
   * @param aRetention One of the {@code RETENTION_} constants.
   */
  public void setRetention(short aRetention) {
    this.retention = aRetention;
  }

  /**
   * encode {@code EPPStatement} into a DOM element tree. The "statment"
   * element is created and the attribute nodes are appended as children.
   *
   * @param aDocument
   *           DOM Document being built
   *
   * @return statement root element tree.
   *
   * @exception EPPEncodeException
   *               Error encoding the DOM element tree.
   */
  public Element encode(Document aDocument) throws EPPEncodeException {
    Element theElm = null;
    Element root = aDocument.createElementNS(EPPCodec.NS, ELM_NAME);

    // Validate that all required attributes are set.
    if ((this.purpose == null) || (this.recipient == null)) {
      cat.error("EPPStatement.encode(): purpose, recipient, and retention must not be null");
      throw new EPPEncodeException("EPPStatement.encode(): purpose, recipient, and retention must not be null");
    }

    EPPUtil.encodeComp(aDocument, root, this.purpose);

    EPPUtil.encodeComp(aDocument, root, this.recipient);

    // Access
    if ((retention < 0) || (retention >= retentionElms.length)) {
      cat.error("EPPStatement.encode(): Unknown retention value of " + retention);
      throw new EPPEncodeException("EPPStatement.encode(): Unknown retention value of " + retention);
    }

    Element theRetentionElm = aDocument.createElementNS(EPPCodec.NS, ELM_RETENTION);
    root.appendChild(theRetentionElm);
    theElm = aDocument.createElementNS(EPPCodec.NS, retentionElms[retention]);
    theRetentionElm.appendChild(theElm);

    return root;
  }

  /**
   * decode {@code EPPStatement} from a DOM element tree. The {@code aElement}
   * argument needs to be the &lt;statement&gt; element
   *
   * @param aElement
   *           root element tree.
   *
   * @exception EPPDecodeException
   *               Error decoding the DOM element tree.
   */
  public void decode(Element aElement) throws EPPDecodeException {
    Element theElm = null;

    // Purpose
    this.purpose = (EPPPurpose) EPPUtil.decodeComp(aElement, EPPCodec.NS, EPPPurpose.ELM_NAME, EPPPurpose.class);

    // Recipient
    this.recipient = (EPPRecipient) EPPUtil.decodeComp(aElement, EPPCodec.NS, EPPRecipient.ELM_NAME,
          EPPRecipient.class);

    // Retention
    theElm = EPPUtil.getElementByTagNameNS(aElement, EPPCodec.NS, ELM_RETENTION);

    if (theElm == null) {
      cat.error("EPPStatement.decode(): could not find retention element");
      throw new EPPDecodeException("EPPStatement could not find retention element");
    }

    Element theRetentionValueElm = EPPUtil.getFirstElementChild(theElm);

    if (theRetentionValueElm == null) {
      cat.error("EPPStatement.decode(): could not find retention element value");
      throw new EPPDecodeException("EPPStatement could not find retention element value");
    }

    Short theRetentionValue = (Short) retentionElmHash.get(theRetentionValueElm.getLocalName());

    if (theRetentionValue == null) {
      cat.error("EPPStatement.decode(): could not find valid retention element value");
      throw new EPPDecodeException("EPPStatement could not find valid retention element value");
    }

    this.retention = theRetentionValue.shortValue();
  }

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

      return false;
    }

    EPPStatement theStatment = (EPPStatement) aObject;

    // Purpose
    if (!((this.purpose == null) ? (theStatment.purpose == null) : this.purpose.equals(theStatment.purpose))) {
      cat.error("EPPStatement.equals(): purpose not equal");

      return false;
    }

    // Recipient
    if (!((this.recipient == null) ? (theStatment.recipient == null) :
          this.recipient.equals(theStatment.recipient))) {
      cat.error("EPPStatement.equals(): recipient not equal");

      return false;
    }

    // Retention
    if (this.retention != theStatment.retention) {
      cat.error("EPPStatement.equals(): retention not equal");

      return false;
    }

    return true;
  }

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

    clone.purpose = (EPPPurpose) this.purpose.clone();
    clone.recipient = (EPPRecipient) this.recipient.clone();
    clone.retention = this.retention;

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