/***********************************************************
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.Enumeration;
import java.util.Vector;

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

/**
 * Identifies the features supported by the server, including:
 * 
 * <ul>
 * <li>One or more &lt;version&gt; elements that contain the protocol versions
 * supported by the server. Use {@code getVersions} and
 * {@code setVersion(s)} to get and set the element(s).</li>
 * <li>One or more &lt;lang&gt; elements that contain the identifiers of the text
 * response languages known by the server. Language identifiers MUST be
 * structured as documented in
 * <a href="http://www.ietf.org/rfc/rfc1766.txt?number=1766">[RFC1766]</a>. Only
 * language identifiers listed in [ISO639] may be used. Use
 * {@code getLangs} and {@code setLang(s)} to get and set the
 * element(s).</li>
 * <li>One or more object-specific &lt;obj:service&gt; elements that identify the
 * objects that the server is capable of managing. Use
 * {@code getObjectServices} and {@code setObjectServices} to get and
 * set the element(s). An optional &lt;svcExtension&gt; element that contains one
 * or more &lt;extURI&gt; elements that contains namespace URIS representin object
 * extensions supported by the server.</li>
 * </ul>
 *
 *
 * @see com.verisign.epp.codec.gen.EPPFactory
 * @see com.verisign.epp.codec.gen.EPPGreeting
 * @see com.verisign.epp.codec.gen.EPPService
 */
public class EPPServiceMenu implements EPPCodecComponent {
  /** Flag for inspecting whether the service is an ObjectService */
  public boolean OBJ_SERVICE = false;

  /** Flag for inspecting whether the service is an ExtensionService */
  public boolean EXT_SERVICE = false;

  /**
   * Vector of version {@code String} instances. Each version String is
   * associated with a &lt;version&gt; XML element.
   */
  private Vector<String> versions;

  /**
   * Vector of language {@code String} instances. Each language String is
   * associated with a &lt;lang&gt; XML element.
   */
  private Vector<String> langs;

  /**
   * Vector of {@code EPPService} instances. Each service is associated
   * with a &lt;objuri&gt; XML element.
   */
  private Vector<EPPService> services;

  /**
   * Vector of {@code EPPService} instances. Each
   * extensionservice is associated with a &lt;exturi&gt; XML element.
   */
  private Vector<EPPService> extservices;

  /**
   * Default XML root tag name for {@code EPPServiceMenu}. This is the tag
   * name used in {@code EPPGreeting}.
   */
  private final String ELM_NAME = "svcMenu";

  /** XML tag name for the {@code versions} attribute. */
  private final String ELM_VERSION = "version";

  /** XML tag name for the {@code langs} attribute. */
  private final String ELM_LANG = "lang";

  /** XML tag name for the {@code objUri} Element. */
  private final String ELM_OBJURI = "objURI";

  /** XML tag name for the {@code SvcExtension} Element. */
  private final String ELM_EXT = "svcExtension";

  /** XML tag name for the {@code ExtensionUri} Element. */
  private final String ELM_EXTURI = "extURI";

  /**
   * Allocates a new {@code EPPServiceMenu} with default attribute values.
   * The defaults include the following: <br>
   * <br>
   * 
   * <ul>
   * <li>versions is set to a {@code Vector} with a single value defined
   * by the constant EPPCodec.VERSION.</li>
   * <li>langs is set to a {@code Vector} with a single "en" value.</li>
   * <li>services is set to the services returned from {@code EPPFactory}
   * </li>
   * <li>extservices is set to the extension services returned from
   * {@code EPPFactory}</li>
   * </ul>
   * 
   * <br>
   * The {@code encode} method can be invoked with the default values set
   * by this constructor.
   */
  public EPPServiceMenu() {
    versions = new Vector<String>();
    versions.addElement(EPPCodec.VERSION);

    langs = new Vector<String>();
    langs.addElement("en");

    services = EPPFactory.getInstance().getServices();
    extservices = EPPFactory.getInstance().getExtensions();
  }


  /**
   * Gets the list of supported/desired EPP versions. An EPP Client uses this
   * method to get the list of supported EPP versions of the EPP Server.
   *
   * @return Vector of version {@code String}'s
   */
  public Vector<String> getVersions() {
    return versions;
  }


  /**
   * Sets the supported versions.
   *
   * @param someVersions
   *           Vector of version{@code String}'s supported by the server.
   */
  public void setVersions(Vector<String> someVersions) {
    versions = someVersions;
  }

  /**
   * Gets the single EPP version. A non-{@code null} version will be
   * returned only if there is one version defined.
   *
   * @return Single version if there is only one version; {@code null}
   *         otherwise.
   */
  public String getVersion() {
    String retVal = null;

    if (versions.size() == 1) {
      retVal = (String) versions.elementAt(0);
    }

    return retVal;
  }

  /**
   * Sets the EPP versions to an individual EPP version.
   *
   * @param aVersion
   *           Version to set versions to.
   */
  public void setVersion(String aVersion) {
    versions = new Vector<String>();

    versions.addElement(aVersion);
  }

  /**
   * Gets the list of supported/desired language(s). Language identifiers MUST
   * be structured as documented in [RFC1766].
   *
   * @return Vector of language {@code String}'s
   */
  public Vector<String> getLangs() {
    return langs;
  }

  /**
   * Sets the supported languages.
   *
   * @param aLangs
   *           Vector of language{@code String}'s supported by the server.
   */
  public void setLangs(Vector<String> aLangs) {
    langs = aLangs;
  }

  /**
   * Gets the single EPP language. A non-{@code null} language will be
   * returned only if there is one language defined.
   *
   * @return Single language if there is only one language; {@code null}
   *         otherwise.
   */
  public String getLang() {
    String retVal = null;

    if (langs.size() == 1) {
      retVal = (String) langs.elementAt(0);
    }

    return retVal;
  }

  /**
   * Sets the languages to an individual language.
   *
   * @param aLang
   *           Language supported/desired.
   */
  public void setLang(String aLang) {
    langs = new Vector<String>();
    langs.addElement(aLang);
  }

  /**
   * Gets the list of supported/desired object services. An EPP Client will
   * retrieve the list of object services supported by the EPP Server. An EPP
   * Server will retrieve the list of object services desired by the EPP
   * Client.
   *
   * @return Vector of {@code EPPService} instances.
   */
  public Vector<EPPService> getObjectServices() {
    return services;
  }

  /**
   * Sets the list of supported/desired object services. An EPP Client will set
   * the list of object services desired. An EPP Server will set the list of
   * supported object services.
   *
   * @param aObjServices
   *           Vector of{@code EPPService} instances.
   */
  public void setObjectServices(Vector<EPPService> aObjServices) {
    services = aObjServices;
  }

  /**
   * Gets the list of supported/desired extension services. An EPP Client will
   * retrieve the list of extension services supported by the EPP Server. An
   * EPP Server will retrieve the list of extension services desired by the EPP
   * Client.
   *
   * @return Vector of {@code EPPService} instances.
   */
  public Vector<EPPService> getExtensionServices() {
    return extservices;
  }

  /**
   * Sets the list of supported/desired extension services. An EPP Client will
   * set the list of extension services desired. An EPP Server will set the
   * list of supported extension services.
   *
   * @param aExtServices
   *           Vector of {@code EPPService} instances.
   */
  public void setExtensionServices(Vector<EPPService> aExtServices) {
    extservices = aExtServices;
  }

  /**
   * Does the service menu include a specified service based on the Namespace
   * URI?
   * 
   * @param aNamespaceURI
   *           Service Namespace URI to search for
   * 
   * @return {@code true} if the service menu includes the service
   *         Namespace URI; {@code false} otherwise.
   */
  public boolean hasService(String aNamespaceURI) {
    boolean found = false;

    if (this.services != null) {
      Enumeration<EPPService> theSvcEnum = this.services.elements();
      while (!found && theSvcEnum.hasMoreElements()) {
        EPPService theExtService = theSvcEnum.nextElement();

        if (theExtService.getNamespaceURI().equals(aNamespaceURI)) {
          found = true;
        }
      }
    }

    return found;
  }

  /**
   * Does the service menu extension services include a specified extension
   * service based on the Namespace URI?
   * 
   * @param aNamespaceURI
   *           Extension service Namespace URI to search for
   * 
   * @return {@code true} if the service menu extension services include
   *         the extension service Namespace URI; {@code false} otherwise.
   */
  public boolean hasExtensionService(String aNamespaceURI) {
    boolean found = false;

    if (this.extservices != null) {
      Enumeration<EPPService> theExtSvcEnum = this.extservices.elements();
      while (!found && theExtSvcEnum.hasMoreElements()) {
        EPPService theExtService = theExtSvcEnum.nextElement();

        if (theExtService.getNamespaceURI().equals(aNamespaceURI)) {
          found = true;
        }
      }
    }

    return found;
  }

  /**
   * encode {@code EPPServiceMenu} into a DOM element tree. The
   * "service-menu" or "services" element is created and the attribute nodes
   * are appended as children.
   *
   * @param aDocument
   *           DOCUMENT ME!
   *
   * @return service menu root element tree.
   *
   * @exception EPPEncodeException
   *               Error encoding the DOM element tree.
   */
  public Element encode(Document aDocument) throws EPPEncodeException {
    Element root = aDocument.createElementNS(EPPCodec.NS, ELM_NAME);

    // Versions
    EPPUtil.encodeVector(aDocument, root, versions, EPPCodec.NS, ELM_VERSION);

    // Languages
    EPPUtil.encodeVector(aDocument, root, langs, EPPCodec.NS, ELM_LANG);

    // Object Services
    EPPUtil.encodeCompVector(aDocument, root, services);

    // Extension Services
    if ((extservices != null) && extservices.elements().hasMoreElements()) {
      Element svcExtension = aDocument.createElementNS(EPPCodec.NS, ELM_EXT);
      EPPUtil.encodeCompVector(aDocument, svcExtension, extservices);
      root.appendChild(svcExtension);
    }

    return root;
  }

  /**
   * decode {@code EPPServiceMenu} from a DOM element tree. The
   * {@code aElement} argument needs to be the &lt;service-menu&gt; element
   * for a {@code EPPGreeting} and &lt;services&gt; element for a
   * {@code EPPLoginCmd}.
   *
   * @param aElement
   *           root element tree.
   *
   * @exception EPPDecodeException
   *               Error decoding the DOM element tree.
   */
  public void decode(Element aElement) throws EPPDecodeException {
    // Versions
    versions = EPPUtil.decodeVector(aElement, EPPCodec.NS, ELM_VERSION);

    // Languages
    langs = EPPUtil.decodeVector(aElement, EPPCodec.NS, ELM_LANG);

    // Services
    services = new Vector<EPPService>();

    // extension services
    extservices = new Vector<EPPService>();

    NodeList serviceElms = aElement.getElementsByTagNameNS(EPPCodec.NS, EPPUtil.getLocalName(ELM_OBJURI));

    for (int i = 0; i < serviceElms.getLength(); i++) {
      EPPService objService = new EPPService(); 
      
      // dummy EPP object service to set the type of service
      objService.setServiceType(EPPService.OBJ_SERVICE);

      objService.decode((Element) serviceElms.item(i));

      services.addElement(objService);
    }

    // decode the extension services now if any exist
    NodeList svcExtensionNodeList = aElement.getElementsByTagNameNS(EPPCodec.NS, EPPUtil.getLocalName(ELM_EXT));

    if (svcExtensionNodeList.getLength() != 0) {
      Element svcExtension = null;

      for (int i = 0; i < svcExtensionNodeList.getLength(); i++) {
        svcExtension = (Element) svcExtensionNodeList.item(i);

        NodeList extensionElms = svcExtension.getElementsByTagNameNS(EPPCodec.NS, EPPUtil.getLocalName(ELM_EXTURI));

        for (int k = 0; k < extensionElms.getLength(); k++) {
          EPPService extService = new EPPService(); 
          
          // dummy EPP extension service to set the type of service
          extService.setServiceType(EPPService.EXT_SERVICE);
          extService.decode((Element) extensionElms.item(k));
          extservices.addElement(extService);
        }

      }

    }

  }

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

    EPPServiceMenu theServiceMenu = (EPPServiceMenu) aObject;

    // versions
    if (!EPPUtil.equalVectors(versions, theServiceMenu.versions)) {
      return false;
    }

    // langs
    if (!EPPUtil.equalVectors(langs, theServiceMenu.langs)) {
      return false;
    }

    // services
    if (!EPPUtil.equalVectors(services, theServiceMenu.services)) {
      return false;
    }

    // extservices
    if (!EPPUtil.equalVectors(extservices, theServiceMenu.extservices)) {
      return false;
    }

    return true;
  }

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

    clone = (EPPServiceMenu) super.clone();

    clone.versions = (Vector<String>) versions.clone();

    clone.langs = (Vector<String>) langs.clone();

    // Services
    clone.services = (Vector) services.clone();

    for (int i = 0; i < services.size(); i++)
      clone.services.setElementAt((EPPService) (services.elementAt(i)).clone(), i);

    // extservices
    if ((extservices != null) && extservices.elements().hasMoreElements()) {
      clone.extservices = (Vector) extservices.clone();

      for (int i = 0; i < extservices.size(); i++) {
        clone.extservices.setElementAt((EPPService) (extservices.elementAt(i)).clone(), i);
      }
    }

    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;
  }
  
}
