/***********************************************************
Copyright (C) 2021 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-0107  USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
 ***********************************************************/
package com.verisign.epp.codec.maintenance.v1_0;

import java.util.Date;

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

/**
 * Represents a maintenance list item that includes the following child
 * elements:<br>
 * <ul>
 * <li>&lt;maint:id&gt; element that is the maintenance identifier.</li>
 * <li>&lt;maint:start&gt; element that is the maintenance start date and
 * time.</li>
 * <li>&lt;maint:end&gt; element that is the maintenance end date and time.</li>
 * <li>&lt;maint:crDate&gt; element that represents the created date and time
 * for the maintenance.</li>
 * <li>An OPTIONAL &lt;maint:upDate&gt; element that represents the updated date
 * and time for the maintenance.</li>
 * </ul>
 */
public class EPPMaintenanceListItem implements EPPCodecComponent {

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

  /**
   * XML local name for {@code EPPMaintenanceItem}.
   */
  public static final String ELM_LOCALNAME = "listItem";

  /**
   * XML root tag for {@code EPPMaintenanceItem}.
   */
  public static final String ELM_NAME = EPPMaintenanceMapFactory.NS_PREFIX + ":" + ELM_LOCALNAME;

  /**
   * XML tag name for the start date attribute.
   */
  private final static String ELM_START_DATE = "start";

  /**
   * XML tag name for the end date attribute.
   */
  private final static String ELM_END_DATE = "end";

  /**
   * XML tag name for the created date attribute.
   */
  private final static String ELM_CREATED_DATE = "crDate";

  /**
   * XML tag name for the updated date attribute.
   */
  private final static String ELM_LAST_UPDATED_DATE = "upDate";

  /**
   * Maintenance notification identifier.
   */
  private EPPMaintenanceId maintenanceId;

  /**
   * Maintenance start date and time.
   */
  private Date startDate;

  /**
   * Maintenance end date and time.
   */
  private Date endDate;

  /**
   * Maintenance created date
   */
  private Date createdDate;

  /**
   * Optional maintenance last updated date
   */
  private Date lastUpdatedDate;

  /**
   * Default constructor for {@code EPPMaintenanceItem}. All the the attributes
   * default to {@code null}. Must call required setter methods before invoking
   * {@link #encode(Document)}, which include:<br>
   * <br>
   * <ul>
   * <li>maintenance identifier - {@link #setMaintenanceId(EPPMaintenanceId)}</li>
   * <li>maintenance created date - {@link #setCreatedDate(Date)}</li>
   * </ul>
   */
  public EPPMaintenanceListItem() {
  }

  /**
   * Constructor for {@code EPPMaintenanceItem} with all of the required
   * attributes as parameters.
   *
   * @param aMaintenanceId
   *           Maintenance identifier
   * @param aStartDate
   *           Maintenance start date and time
   * @param aEndDate
   *           Maintenance end date and time
   * @param aCreatedDate
   *           Maintenance created date
   */
  public EPPMaintenanceListItem(EPPMaintenanceId aMaintenanceId, Date aStartDate, Date aEndDate, Date aCreatedDate) {
    this.maintenanceId = aMaintenanceId;
    this.startDate = aStartDate;
    this.endDate = aEndDate;
    this.createdDate = aCreatedDate;
  }

  /**
   * Constructor for {@code EPPMaintenanceItem} with all of the attributes as
   * parameters.
   *
   * @param aMaintenanceId
   *           Maintenance identifier
   * @param aStartDate
   *           Maintenance start date. Set to {@code null} if undefined.
   * @param aEndDate
   *           Maintenance end date. Set to {@code null} if undefined.
   * @param aCreatedDate
   *           Maintenance created date
   * @param aLastUpdatedDate
   *           Maintenance last updated date. Set to {@code null} if undefined.
   */
  public EPPMaintenanceListItem(EPPMaintenanceId aMaintenanceId, Date aStartDate, Date aEndDate, Date aCreatedDate,
        Date aLastUpdatedDate) {
    this.maintenanceId = aMaintenanceId;
    this.createdDate = aCreatedDate;
    this.startDate = aStartDate;
    this.endDate = aEndDate;
    this.lastUpdatedDate = aLastUpdatedDate;
  }

  /**
   * Gets the maintenance identifier.
   *
   * @return The maintenance identifier if defined; {@code null} otherwise.
   */
  public EPPMaintenanceId getMaintenanceId() {
    return this.maintenanceId;
  }

  /**
   * Sets the maintenance identifier.
   *
   * @param aMaintenanceId
   *           The maintenance identifier.
   */
  public void setMaintenanceId(EPPMaintenanceId aMaintenanceId) {
    this.maintenanceId = aMaintenanceId;
  }

  /**
   * Is the start date defined?
   *
   * @return {@code true} if the start date is defined; {@code false}
   *         otherwise.
   */
  public boolean hasStartdDate() {
    return (this.startDate != null ? true : false);
  }

  /**
   * Gets the optional maintenance start date and time.
   *
   * @return start date and time if defined; {@code null} otherwise.
   */
  public Date getStartDate() {
    return this.startDate;
  }

  /**
   * Sets the optional maintenance start date and time.
   *
   * @param aStartDate
   *           Maintenance start date and time. Set to {@code null} if
   *           undefined.
   */
  public void setStartDate(Date aStartDate) {
    this.startDate = aStartDate;
  }

  /**
   * Is the end date defined?
   *
   * @return {@code true} if the end date is defined; {@code false} otherwise.
   */
  public boolean hasEndDate() {
    return (this.endDate != null ? true : false);
  }

  /**
   * Gets the optional maintenance end date and time.
   *
   * @return end date and time if defined; {@code null} otherwise.
   */
  public Date getEndDate() {
    return this.endDate;
  }

  /**
   * Sets the optional maintenance end date and time.
   *
   * @param aEndDate
   *           Maintenance end date and time. Set to {@code null} if undefined.
   */
  public void setEndDate(Date aEndDate) {
    this.endDate = aEndDate;
  }

  /**
   * Gets the maintenance created date.
   *
   * @return Maintenance created date if defined; {@code null} otherwise.
   */
  public Date getCreatedDate() {
    return this.createdDate;
  }

  /**
   * Sets the maintenance created date.
   *
   * @param aCreatedDate
   *           Maintenance created date
   */
  public void setCreatedDate(Date aCreatedDate) {
    this.createdDate = aCreatedDate;
  }

  /**
   * Is the last updated date defined?
   *
   * @return {@code true} if the last updated date is defined; {@code false}
   *         otherwise.
   */
  public boolean hasLastUpdatedDate() {
    return (this.lastUpdatedDate != null ? true : false);
  }

  /**
   * Gets the optional maintenance last updated date.
   *
   * @return Maintenance last updated date if defined; {@code null} otherwise.
   */
  public Date getLastUpdatedDate() {
    return this.lastUpdatedDate;
  }

  /**
   * Sets the maintenance last updated date.
   *
   * @param aLastUpdatedDate
   *           Maintenance last updated date
   */
  public void setLastUpdatedDate(Date aLastUpdatedDate) {
    this.lastUpdatedDate = aLastUpdatedDate;
  }

  /**
   * Validate the state of the {@code EPPMaintenanceItem} instance. A valid
   * state means that all of the required attributes have been set. If
   * validateState returns without an exception, the state is valid. If the
   * state is not valid, the EPPCodecException will contain a description of
   * the error. throws EPPCodecException State error. This will contain the
   * name of the attribute that is not valid.
   *
   * @throws EPPCodecException
   *            On validation error
   */
  void validateState() throws EPPCodecException {

    if (this.maintenanceId == null) {
      throw new EPPCodecException("maintenanceId required attribute is not set");
    }

    if (this.startDate == null) {
      throw new EPPCodecException("startDate required attribute is not set");
    }

    if (this.endDate == null) {
      throw new EPPCodecException("endDate required attribute is not set");
    }

    if (this.createdDate == null) {
      throw new EPPCodecException("createdDate required attribute is not set");
    }
  }

  /**
   * Encode a DOM Element tree from the attributes of the
   * {@code EPPMaintenanceItem} instance.
   *
   * @param aDocument
   *           DOM Document that is being built. Used as an Element factory.
   *
   * @return Root DOM Element representing the {@code EPPMaintenanceItem}
   *         instance.
   *
   * @exception EPPEncodeException
   *               Unable to encode {@code EPPMaintenanceItem} instance.
   */
  @Override
  public Element encode(Document aDocument) throws EPPEncodeException {
    try {
      validateState();
    }
    catch (EPPCodecException e) {
      throw new EPPEncodeException("Invalid state on EPPMaintenanceItem.encode: " + e);
    }

    // Create root element
    Element root = aDocument.createElementNS(EPPMaintenanceMapFactory.NS, ELM_NAME);

    // Identifier
    EPPUtil.encodeComp(aDocument, root, this.maintenanceId);

    // Start Date
    EPPUtil.encodeTimeInstant(aDocument, root, this.startDate, EPPMaintenanceMapFactory.NS,
          EPPMaintenanceMapFactory.NS_PREFIX + ":" + ELM_START_DATE);

    // End Date
    EPPUtil.encodeTimeInstant(aDocument, root, this.endDate, EPPMaintenanceMapFactory.NS,
          EPPMaintenanceMapFactory.NS_PREFIX + ":" + ELM_END_DATE);

    // Created Date
    EPPUtil.encodeTimeInstant(aDocument, root, this.createdDate, EPPMaintenanceMapFactory.NS,
          EPPMaintenanceMapFactory.NS_PREFIX + ":" + ELM_CREATED_DATE);

    // Last Updated Date
    EPPUtil.encodeTimeInstant(aDocument, root, this.lastUpdatedDate, EPPMaintenanceMapFactory.NS,
          EPPMaintenanceMapFactory.NS_PREFIX + ":" + ELM_LAST_UPDATED_DATE);

    return root;
  }

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

    // Identifier
    this.maintenanceId = (EPPMaintenanceId) EPPUtil.decodeComp(aElement, EPPMaintenanceMapFactory.NS,
          EPPMaintenanceId.ELM_LOCALNAME, EPPMaintenanceId.class);

    // Start Date
    this.startDate = EPPUtil.decodeTimeInstant(aElement, EPPMaintenanceMapFactory.NS, ELM_START_DATE);

    // End Date
    this.endDate = EPPUtil.decodeTimeInstant(aElement, EPPMaintenanceMapFactory.NS, ELM_END_DATE);

    // Created Date
    this.createdDate = EPPUtil.decodeTimeInstant(aElement, EPPMaintenanceMapFactory.NS, ELM_CREATED_DATE);

    // Last Updated Date
    this.lastUpdatedDate = EPPUtil.decodeTimeInstant(aElement, EPPMaintenanceMapFactory.NS, ELM_LAST_UPDATED_DATE);
  }

  /**
   * implements a deep {@code EPPMaintenanceItem} compare.
   *
   * @param aObject
   *           {@code EPPMaintenanceItem} instance to compare with
   *
   * @return {@code true} of {@code aObject} is equal to instance;
   *         {@code false} otherwise.
   */
  @Override
  public boolean equals(Object aObject) {

    if (!(aObject instanceof EPPMaintenanceListItem)) {
      return false;
    }

    EPPMaintenanceListItem other = (EPPMaintenanceListItem) aObject;

    // Identifier
    if (!EqualityUtil.equals(this.maintenanceId, other.maintenanceId)) {
      cat.error("EPPMaintenanceItem.equals(): maintenanceId not equal");
      return false;
    }

    // Start Date
    if (!EqualityUtil.equals(this.startDate, other.startDate)) {
      cat.error("EPPMaintenanceItem.equals(): startDate not equal");
      return false;
    }

    // End Date
    if (!EqualityUtil.equals(this.endDate, other.endDate)) {
      cat.error("EPPMaintenanceItem.equals(): endDate not equal");
      return false;
    }

    // Created Date
    if (!EqualityUtil.equals(this.createdDate, other.createdDate)) {
      cat.error("EPPMaintenanceItem.equals(): createdDate not equal");
      return false;
    }

    // Last Updated Date
    if (!EqualityUtil.equals(this.lastUpdatedDate, other.lastUpdatedDate)) {
      cat.error("EPPMaintenanceItem.equals(): lastUpdatedDate not equal");
      return false;
    }

    return true;
  }

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

    clone = (EPPMaintenanceListItem) super.clone();

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

}
