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

import java.util.Vector;

import com.verisign.epp.codec.gen.EPPCodecComponent;
import com.verisign.epp.codec.gen.EPPResponse;
import com.verisign.epp.codec.host.EPPHostAddRemove;
import com.verisign.epp.codec.host.EPPHostAddress;
import com.verisign.epp.codec.host.EPPHostCheckCmd;
import com.verisign.epp.codec.host.EPPHostCheckResp;
import com.verisign.epp.codec.host.EPPHostCreateCmd;
import com.verisign.epp.codec.host.EPPHostDeleteCmd;
import com.verisign.epp.codec.host.EPPHostInfoCmd;
import com.verisign.epp.codec.host.EPPHostInfoResp;
import com.verisign.epp.codec.host.EPPHostStatus;
import com.verisign.epp.codec.host.EPPHostUpdateCmd;

/**
 * {@code EPPHost} is the primary client interface class used for host
 * management. An instance of {@code EPPHost} is created with an initialized
 * {@code EPPSession}, and can be used for more than one request within a single
 * thread. A set of setter methods are provided to set the attributes before a
 * call to one of the send action methods. The responses returned from the send
 * action methods are either instances of {@code EPPResponse} or instances of
 * response classes in the {@code com.verisign.epp.codec.host} package.
 *
 * @see com.verisign.epp.codec.gen.EPPResponse
 * @see com.verisign.epp.codec.host.EPPHostCreateResp
 * @see com.verisign.epp.codec.host.EPPHostInfoResp
 * @see com.verisign.epp.codec.host.EPPHostCheckResp
 */
public class EPPHost {
  /** "ok" status */
  public final static java.lang.String STAT_OK = EPPHostStatus.ELM_STATUS_OK;

  /** "pendingDelete" status */
  public final static java.lang.String STAT_PENDING_DELETE = EPPHostStatus.ELM_STATUS_PENDING_DELETE;

  /** "pendingTransfer" status */
  public final static java.lang.String STAT_PENDING_TRANSFER = EPPHostStatus.ELM_STATUS_PENDING_TRANSFER;

  /** "pendingCreate" status */
  public final static java.lang.String STAT_PENDING_CREATE = EPPHostStatus.ELM_STATUS_PENDING_CREATE;

  /** "pendingUpdate" status */
  public final static java.lang.String STAT_PENDING_UPDATE = EPPHostStatus.ELM_STATUS_PENDING_UPDATE;

  /** "clientDeleteProhibited" status */
  public final static java.lang.String STAT_CLIENT_DELETE_PROHIBITED = EPPHostStatus.ELM_STATUS_CLIENT_DELETE_PROHIBITED;

  /** "clientUpdateProhibited" status */
  public final static java.lang.String STAT_CLIENT_UPDATE_PROHIBITED = EPPHostStatus.ELM_STATUS_CLIENT_UPDATE_PROHIBITED;

  /** "linked" status */
  public final static java.lang.String STAT_LINKED = EPPHostStatus.ELM_STATUS_LINKED;

  /** "serverDeleteProhibited" status */
  public final static java.lang.String STAT_SERVER_DELETE_PROHIBITED = EPPHostStatus.ELM_STATUS_SERVER_DELETE_PROHIBITED;

  /** "serverUpdateProhibited" status */
  public final static java.lang.String STAT_SERVER_UPDATE_PROHIBITED = EPPHostStatus.ELM_STATUS_SERVER_UPDATE_PROHIBITED;

  /** Default language for status descriptions */
  public final static String DEFAULT_LANG = EPPHostStatus.ELM_DEFAULT_LANG;

  /** Host Name(s) */
  private Vector<String> hosts = new Vector<String>();

  /**
   * IP Addresses to add as a {@code Vector} of {@code EPPHostAddress}
   * instances.
   */
  private Vector<EPPHostAddress> addAddresses = null;

  /**
   * IP Addresses to remove as a {@code Vector} of {@code EPPHostAddress}
   * instances.
   */
  private Vector<EPPHostAddress> removeAddresses = null;

  /**
   * Host statuses to add as a {@code Vector} of {@code EPPHostStatus}
   * instances.
   */
  private Vector<EPPHostStatus> addStatuses = null;

  /**
   * Host statuses to remove as a {@code Vector} of {@code EPPHostStatus}
   * instances.
   */
  private Vector<EPPHostStatus> removeStatuses = null;

  /** New host name */
  private String newName = null;

  /** Authenticated session */
  private EPPSession session = null;

  /** Transaction Id provided by cliet */
  private String transId = null;

  /**
   * Extension objects associated with the command. This is a {@code Vector} of
   * {@code EPPCodecComponent} objects.
   */
  private Vector<EPPCodecComponent> extensions = null;

  /**
   * Constructs an {@code EPPHost} given an initialized EPP session.
   *
   * @param newSession
   *           Server session to use.
   */
  public EPPHost(EPPSession newSession) {
    this.session = newSession;
  }

  /**
   * Adds a command extension object.
   *
   * @param aExtension
   *           command extension object associated with the command
   */
  public void addExtension(EPPCodecComponent aExtension) {
    if (this.extensions == null) {
      this.extensions = new Vector<EPPCodecComponent>();
    }

    this.extensions.addElement(aExtension);
  }

  /**
   * Sets a command extension object.
   *
   * @param aExtension
   *           command extension object associated with the command
   *
   * @deprecated Replaced by {@link #addExtension(EPPCodecComponent)}. This
   *             method will add the extension as is done in
   *             {@link #addExtension(EPPCodecComponent)}.
   */
  @Deprecated
  public void setExtension(EPPCodecComponent aExtension) {
    this.addExtension(aExtension);
  }

  /**
   * Sets the command extension objects.
   *
   * @param aExtensions
   *           command extension objects associated with the command
   */
  public void setExtensions(Vector<EPPCodecComponent> aExtensions) {
    this.extensions = aExtensions;
  }

  /**
   * Gets the command extensions.
   *
   * @return {@code Vector} of concrete {@code EPPCodecComponent} associated
   *         with the command if exists; {@code null} otherwise.
   */
  public Vector<EPPCodecComponent> getExtensions() {
    return this.extensions;
  }

  /**
   * Adds a host name for use with a {@code send} method. Adding more than one
   * host name is only supported by {@code sendCheck}.
   *
   * @param newHostName
   *           Host name to add
   */
  public void addHostName(String newHostName) {
    this.hosts.addElement(newHostName);
  }

  /**
   * Gets the new name for the host.
   *
   * @return New host name if defined; {@code null} otherwise.
   */
  public String getNewName() {
    return this.newName;
  }

  /**
   * Sets the new name for the host.
   *
   * @param aNewName
   *           New host name
   */
  public void setNewName(String aNewName) {
    this.newName = aNewName;
  }

  /**
   * Adds an IPV4 IP Address to the host.
   *
   * @param newIPV4Address
   *           IPV4 IP Address
   */
  public void addIPV4Address(String newIPV4Address) {
    if (this.addAddresses == null) {
      this.addAddresses = new Vector<EPPHostAddress>();
    }

    this.addAddresses.addElement(new EPPHostAddress(newIPV4Address));
  }

  /**
   * Removes an IPV4 IP Address from the host.
   *
   * @param newIPV4Address
   *           IPV4 IP Address
   */
  public void removeIPV4Address(String newIPV4Address) {
    if (this.removeAddresses == null) {
      this.removeAddresses = new Vector<EPPHostAddress>();
    }

    this.removeAddresses.addElement(new EPPHostAddress(newIPV4Address));
  }

  /**
   * Adds an IPV6 address to the host
   *
   * @param newIPV6Address
   *           IPV6 Address
   */
  public void addIPV6Address(String newIPV6Address) {
    if (this.addAddresses == null) {
      this.addAddresses = new Vector<EPPHostAddress>();
    }

    this.addAddresses.addElement(new EPPHostAddress(newIPV6Address, EPPHostAddress.IPV6));
  }

  /**
   * Removes an IPV6 address from the host
   *
   * @param newIPV6Address
   *           IPV6 Address
   */
  public void removeIPV6Address(String newIPV6Address) {
    if (this.removeAddresses == null) {
      this.removeAddresses = new Vector<EPPHostAddress>();
    }

    this.removeAddresses.addElement(new EPPHostAddress(newIPV6Address, EPPHostAddress.IPV6));
  }

  /**
   * Adds a status to the host.
   *
   * @param aStatus
   *           One of the {@code STAT_} constants
   */
  public void addStatus(String aStatus) {
    if (this.addStatuses == null) {
      this.addStatuses = new Vector<EPPHostStatus>();
    }

    this.addStatuses.addElement(new EPPHostStatus(aStatus));
  }

  /**
   * Removes a status from the host.
   *
   * @param aStatus
   *           One of the {@code STAT_} constants
   */
  public void removeStatus(String aStatus) {
    if (this.removeStatuses == null) {
      this.removeStatuses = new Vector<EPPHostStatus>();
    }

    this.removeStatuses.addElement(new EPPHostStatus(aStatus));
  }

  /**
   * Adds a status to the host with a description.
   *
   * @param aStatus
   *           One of the {@code STAT_} constants
   * @param aDesc
   *           Description of the rationale for the status change
   * @param aLang
   *           Language of {@code aDesc} Use {@code DEFAULT_LANG} for the
   *           default language ("us").
   */
  public void addStatus(String aStatus, String aDesc, String aLang) {
    if (this.addStatuses == null) {
      this.addStatuses = new Vector<EPPHostStatus>();
    }

    this.addStatuses.addElement(new EPPHostStatus(aStatus, aDesc, aLang));
  }

  /**
   * Removes a status from the host with a description.
   *
   * @param aStatus
   *           One of the {@code STAT_} constants
   * @param aDesc
   *           Description of the rationale for the status change
   * @param aLang
   *           Language of {@code aDesc} Use {@code DEFAULT_LANG} for the
   *           default language ("us").
   */
  public void removeStatus(String aStatus, String aDesc, String aLang) {
    if (this.removeStatuses == null) {
      this.removeStatuses = new Vector<EPPHostStatus>();
    }

    this.removeStatuses.addElement(new EPPHostStatus(aStatus, aDesc, aLang));
  }

  /**
   * Sets the client transaction identifier.
   *
   * @param newTransId
   *           Client transaction identifier
   */
  public void setTransId(String newTransId) {
    this.transId = newTransId;
  }

  /**
   * Gets the response associated with the last command. This method can be
   * used to retrieve the server error response in the catch block of
   * EPPCommandException.
   *
   * @return Response associated with the last command
   */
  public EPPResponse getResponse() {
    return this.session.getResponse();
  }

  /**
   * Sends a Host Create Command to the server.<br>
   * <br>
   * The required attributes have been set with the following methods:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code addHostName} - Sets the host name to create. Only one host name
   * is valid.</li>
   * </ul>
   * 
   * <br>
   * <br>
   * The optional attributes have been set with the following:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code setTransId} - Sets the client transaction identifier</li>
   * <li>{@code addIPV4Address} - Add an IPV4 Address</li>
   * <li>{@code addIPV6Address} - Add an IPV6 Address</li>
   * </ul>
   * 
   *
   * @return {@code EPPResponse} containing the Host create result.
   *
   * @exception EPPCommandException
   *               Error executing the create command. Use {@code getResponse}
   *               to get the associated server error response.
   */
  public EPPResponse sendCreate() throws EPPCommandException {
    // Invalid number of Host Names?
    if (this.hosts.size() != 1) {
      throw new EPPCommandException("One Host Name is required for sendCreate()");
    }

    // Create the command
    EPPHostCreateCmd theCommand = new EPPHostCreateCmd(this.transId, (String) this.hosts.firstElement(),
          this.addAddresses);

    // Set command extension
    theCommand.setExtensions(this.extensions);

    // Reset host attributes
    resetHost();

    // process the command and response
    return this.session.processDocument(theCommand, EPPResponse.class);
  }

  /**
   * Sends a Host Check Command to the server.<br>
   * <br>
   * The required attributes have been set with the following methods:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code addHostName} - Adds a host name to check. More than one host
   * name can be checked in {@code sendCheck}</li>
   * </ul>
   * 
   * <br>
   * <br>
   * The optional attributes have been set with the following:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code setTransId} - Sets the client transaction identifier</li>
   * </ul>
   * 
   *
   * @return {@code EPPHostCheckResp} containing the Host check information.
   *
   * @exception EPPCommandException
   *               Error executing the check command. Use {@code getResponse}
   *               to get the associated server error response.
   */
  public EPPHostCheckResp sendCheck() throws EPPCommandException {
    // Invalid number of Host Names?
    if (this.hosts.size() == 0) {
      throw new EPPCommandException("At least One Host Name is required for sendCheck()");
    }

    // Create the command
    EPPHostCheckCmd theCommand = new EPPHostCheckCmd(this.transId, this.hosts);

    // Set command extension
    theCommand.setExtensions(this.extensions);

    // Reset host attributes
    resetHost();

    // process the command and response
    return (EPPHostCheckResp) this.session.processDocument(theCommand, EPPHostCheckResp.class);
  }

  /**
   * Sends a Host Info Command to the server.<br>
   * <br>
   * The required attributes have been set with the following methods:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code addHostName} - Sets the host name to get info for. Only one
   * host name is valid.</li>
   * </ul>
   * 
   * <br>
   * <br>
   * The optional attributes have been set with the following:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code setTransId} - Sets the client transaction identifier</li>
   * </ul>
   * 
   *
   * @return {@code EPPHostInfoResp} containing the Host information.
   *
   * @exception EPPCommandException
   *               Error executing the info command. Use {@code getResponse} to
   *               get the associated server error response.
   */
  public EPPHostInfoResp sendInfo() throws EPPCommandException {
    // Invalid number of Host Names?
    if (this.hosts.size() != 1) {
      throw new EPPCommandException("One Host Name is required for sendInfo()");
    }

    // Create the command
    EPPHostInfoCmd theCommand = new EPPHostInfoCmd(this.transId, (String) this.hosts.firstElement());

    // Set command extension
    theCommand.setExtensions(this.extensions);

    // Reset contact attributes
    resetHost();

    // process the command and response
    return (EPPHostInfoResp) this.session.processDocument(theCommand, EPPHostInfoResp.class);
  }

  /**
   * Sends a Host Update Command to the server.<br>
   * <br>
   * The required attributes have been set with the following methods:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code addHostName} - Sets the domain name to update. Only one domain
   * name is valid.</li>
   * </ul>
   * 
   * <br>
   * <br>
   * The optional attributes have been set with the following:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code setTransId} - Sets the client transaction identifier</li>
   * <li>{@code addIPV4Address} - Adds IPV4 Address</li>
   * <li>{@code addIPV6Address} - Adds IPV6 Address</li>
   * <li>{@code removeIPV4Address} - Removes IPV4 Address</li>
   * <li>{@code removeIPV6Address} - Removes IPV6 Address</li>
   * <li>{@code addStatus} - Add status</li>
   * <li>{@code removeStatus} - Remove status</li>
   * <li>{@code setNewName} - Renames the host</li>
   * </ul>
   * 
   * At least one update attribute needs to be set.
   *
   * @return {@code EPPResponse} containing the Host update result.
   *
   * @exception EPPCommandException
   *               Error executing the update command. Use {@code getResponse}
   *               to get the associated server error response.
   */
  public EPPResponse sendUpdate() throws EPPCommandException {
    // Invalid number of Host Names?
    if (this.hosts.size() != 1) {
      throw new EPPCommandException("One Host Name is required for sendUpdate()");
    }

    // Add attributes specified?
    EPPHostAddRemove addItems = null;

    if ((this.addAddresses != null) || (this.addStatuses != null)) {
      addItems = new EPPHostAddRemove(this.addAddresses, this.addStatuses);
    }

    // Remove attributes specified?
    EPPHostAddRemove removeItems = null;

    if ((this.removeAddresses != null) || (this.removeStatuses != null)) {
      removeItems = new EPPHostAddRemove(this.removeAddresses, this.removeStatuses);
    }

    // Change attributes specified?
    EPPHostAddRemove changeItems = null;

    if (this.newName != null) {
      changeItems = new EPPHostAddRemove(this.newName);
    }

    // Create the command
    EPPHostUpdateCmd theCommand = new EPPHostUpdateCmd(this.transId, (String) this.hosts.firstElement(), addItems,
          removeItems, changeItems);

    // Set command extension
    theCommand.setExtensions(this.extensions);

    // Reset host attributes
    resetHost();

    // process the command and response
    return this.session.processDocument(theCommand, EPPResponse.class);
  }

  /**
   * Sends a Host Delete Command to the server.<br>
   * <br>
   * The required attributes have been set with the following methods:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code addHostName} - Sets the host name to delete. Only one host name
   * is valid.</li>
   * </ul>
   * 
   * <br>
   * <br>
   * The optional attributes have been set with the following:<br>
   * <br>
   * 
   * <ul>
   * <li>{@code setTransId} - Sets the client transaction identifier</li>
   * </ul>
   * 
   *
   * @return {@code EPPResponse} containing the delete result information.
   *
   * @exception EPPCommandException
   *               Error executing the delete command. Use {@code getResponse}
   *               to get the associated server error response.
   */
  public EPPResponse sendDelete() throws EPPCommandException {
    // Invalid number of Domain Names?
    if (this.hosts.size() != 1) {
      throw new EPPCommandException("One Host Name is required for sendDelete()");
    }

    // Create the command
    EPPHostDeleteCmd theCommand = new EPPHostDeleteCmd(this.transId, (String) this.hosts.firstElement());

    // Set command extension
    theCommand.setExtensions(this.extensions);

    // Reset host attributes
    resetHost();

    // process the command and response
    return this.session.processDocument(theCommand, EPPResponse.class);
  }

  /**
   * Resets the host instance to its initial state.
   */
  protected void resetHost() {
    this.hosts = new Vector<String>();
    this.addAddresses = null;
    this.removeAddresses = null;
    this.addStatuses = null;
    this.removeStatuses = null;
    this.transId = null;
    this.extensions = null;
    this.newName = null;
  }
}
