/*
 * Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.hadoop.yarn.service.api.records;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * An Service resource has the following attributes.
 **/
@InterfaceAudience.Public
@InterfaceStability.Unstable
@ApiModel(description = "An Service resource has the following attributes.")
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "name", "version", "description", "state", "resource",
    "number_of_containers", "lifetime", "containers" })
public class Service extends BaseResource {
  private static final long serialVersionUID = -4491694636566094885L;

  private String name = null;
  private String id = null;
  private Artifact artifact = null;
  private Resource resource = null;
  @JsonProperty("launch_time")
  @XmlElement(name = "launch_time")
  private Date launchTime = null;
  @JsonProperty("number_of_running_containers")
  @XmlElement(name = "number_of_running_containers")
  private Long numberOfRunningContainers = null;
  private Long lifetime = null;
  private List<Component> components = new ArrayList<>();
  private Configuration configuration = new Configuration();
  private ServiceState state = null;
  private Map<String, String> quicklinks = new HashMap<>();
  private String queue = null;
  @JsonProperty("kerberos_principal")
  @XmlElement(name = "kerberos_principal")
  private KerberosPrincipal kerberosPrincipal = new KerberosPrincipal();
  private String version = null;
  private String description = null;
  private String dockerClientConfig = null;
  private List<String> dependencies = new ArrayList<String>();

  /**
   * A unique service name.
   **/
  public Service name(String name) {
    this.name = name;
    return this;
  }

  @ApiModelProperty(example = "null", required = true, value = "A unique service name.")
  @JsonProperty("name")
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  /**
   * A unique service id.
   **/
  public Service id(String id) {
    this.id = id;
    return this;
  }

  @ApiModelProperty(example = "null", value = "A unique service id.")
  @JsonProperty("id")
  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  @ApiModelProperty(example = "null", required = true,
      value = "Version of the service.")
  @JsonProperty("version")
  public String getVersion() {
    return version;
  }

  public void setVersion(String version) {
    this.version = version;
  }

  /**
   * Version of the service.
   */
  public Service version(String version) {
    this.version = version;
    return this;
  }

  @ApiModelProperty(example = "null", value = "Description of the service.")
  @JsonProperty("description")
  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  /**
   * Description of the service.
   */
  public Service description(String description) {
    this.description = description;
    return this;
  }

  /**
   * Artifact of single-component services. Mandatory if components
   * attribute is not specified.
   **/
  public Service artifact(Artifact artifact) {
    this.artifact = artifact;
    return this;
  }

  @ApiModelProperty(example = "null", value = "Artifact of single-component services. Mandatory if components attribute is not specified.")
  @JsonProperty("artifact")
  public Artifact getArtifact() {
    return artifact;
  }

  public void setArtifact(Artifact artifact) {
    this.artifact = artifact;
  }

  /**
   * Resource of single-component services or the global default for
   * multi-component services. Mandatory if it is a single-component
   * service and if cpus and memory are not specified at the Service
   * level.
   **/
  public Service resource(Resource resource) {
    this.resource = resource;
    return this;
  }

  @ApiModelProperty(example = "null", value = "Resource of single-component services or the global default for multi-component services. Mandatory if it is a single-component service and if cpus and memory are not specified at the Service level.")
  @JsonProperty("resource")
  public Resource getResource() {
    return resource;
  }

  public void setResource(Resource resource) {
    this.resource = resource;
  }

  /**
   * The time when the service was created, e.g. 2016-03-16T01:01:49.000Z.
   **/
  public Service launchTime(Date launchTime) {
    this.launchTime = launchTime == null ? null : (Date) launchTime.clone();
    return this;
  }

  @ApiModelProperty(example = "null", value = "The time when the service was created, e.g. 2016-03-16T01:01:49.000Z.")
  public Date getLaunchTime() {
    return launchTime == null ? null : (Date) launchTime.clone();
  }

  public void setLaunchTime(Date launchTime) {
    this.launchTime = launchTime == null ? null : (Date) launchTime.clone();
  }

  /**
   * In get response this provides the total number of running containers for
   * this service (across all components) at the time of request. Note, a
   * subsequent request can return a different number as and when more
   * containers get allocated until it reaches the total number of containers or
   * if a flex request has been made between the two requests.
   **/
  public Service numberOfRunningContainers(Long numberOfRunningContainers) {
    this.numberOfRunningContainers = numberOfRunningContainers;
    return this;
  }

  @ApiModelProperty(example = "null", value = "In get response this provides the total number of running containers for this service (across all components) at the time of request. Note, a subsequent request can return a different number as and when more containers get allocated until it reaches the total number of containers or if a flex request has been made between the two requests.")
  public Long getNumberOfRunningContainers() {
    return numberOfRunningContainers;
  }

  public void setNumberOfRunningContainers(Long numberOfRunningContainers) {
    this.numberOfRunningContainers = numberOfRunningContainers;
  }

  /**
   * Life time (in seconds) of the service from the time it reaches the
   * RUNNING_BUT_UNREADY state (after which it is automatically destroyed by YARN). For
   * unlimited lifetime do not set a lifetime value.
   **/
  public Service lifetime(Long lifetime) {
    this.lifetime = lifetime;
    return this;
  }

  @ApiModelProperty(example = "null", value = "Life time (in seconds) of the service from the time it reaches the RUNNING_BUT_UNREADY state (after which it is automatically destroyed by YARN). For unlimited lifetime do not set a lifetime value.")
  @JsonProperty("lifetime")
  public Long getLifetime() {
    return lifetime;
  }

  public void setLifetime(Long lifetime) {
    this.lifetime = lifetime;
  }

  /**
   * Components of an service.
   **/
  public Service components(List<Component> components) {
    this.components = components;
    return this;
  }

  @ApiModelProperty(example = "null", value = "Components of an service.")
  @JsonProperty("components")
  public List<Component> getComponents() {
    return components;
  }

  public void setComponents(List<Component> components) {
    this.components = components;
  }

  public void addComponent(Component component) {
    components.add(component);
  }

  public Component getComponent(String name) {
    for (Component component : components) {
      if (component.getName().equals(name)) {
        return component;
      }
    }
    return null;
  }

  /**
   * Config properties of an service. Configurations provided at the
   * service/global level are available to all the components. Specific
   * properties can be overridden at the component level.
   **/
  public Service configuration(Configuration configuration) {
    this.configuration = configuration;
    return this;
  }

  @ApiModelProperty(example = "null", value = "Config properties of an service. Configurations provided at the service/global level are available to all the components. Specific properties can be overridden at the component level.")
  @JsonProperty("configuration")
  public Configuration getConfiguration() {
    return configuration;
  }

  public void setConfiguration(Configuration configuration) {
    this.configuration = configuration;
  }

  /**
   * State of the service. Specifying a value for this attribute for the
   * POST payload raises a validation error. This attribute is available only in
   * the GET response of a started service.
   **/
  public Service state(ServiceState state) {
    this.state = state;
    return this;
  }

  @ApiModelProperty(example = "null", value = "State of the service. Specifying a value for this attribute for the POST payload raises a validation error. This attribute is available only in the GET response of a started service.")
  @JsonProperty("state")
  public ServiceState getState() {
    return state;
  }

  public void setState(ServiceState state) {
    this.state = state;
  }

  /**
   * A blob of key-value pairs of quicklinks to be exported for an service.
   **/
  public Service quicklinks(Map<String, String> quicklinks) {
    this.quicklinks = quicklinks;
    return this;
  }

  @ApiModelProperty(example = "null", value = "A blob of key-value pairs of quicklinks to be exported for an service.")
  @JsonProperty("quicklinks")
  public Map<String, String> getQuicklinks() {
    return quicklinks;
  }

  public void setQuicklinks(Map<String, String> quicklinks) {
    this.quicklinks = quicklinks;
  }

  /**
   * The YARN queue that this service should be submitted to.
   **/
  public Service queue(String queue) {
    this.queue = queue;
    return this;
  }

  @ApiModelProperty(example = "null", value = "The YARN queue that this service should be submitted to.")
  @JsonProperty("queue")
  public String getQueue() {
    return queue;
  }

  public void setQueue(String queue) {
    this.queue = queue;
  }

  @ApiModelProperty(example = "null", value = "A list of dependent services.")
  @XmlElement(name = "dependencies")
  @JsonProperty("dependencies")
  public List<String> getDependencies() {
    return dependencies;
  }

  public void setDependencies(List<String>
      dependencies) {
    this.dependencies = dependencies;
  }

  public Service kerberosPrincipal(KerberosPrincipal kerberosPrincipal) {
    this.kerberosPrincipal = kerberosPrincipal;
    return this;
  }

  /**
   * The Kerberos Principal of the service.
   * @return kerberosPrincipal
   **/
  @ApiModelProperty(value = "The Kerberos Principal of the service")
  public KerberosPrincipal getKerberosPrincipal() {
    return kerberosPrincipal;
  }

  public void setKerberosPrincipal(KerberosPrincipal kerberosPrincipal) {
    this.kerberosPrincipal = kerberosPrincipal;
  }

  @JsonProperty("docker_client_config")
  @XmlElement(name = "docker_client_config")
  @SuppressWarnings("checkstyle:hiddenfield")
  public Service dockerClientConfig(String dockerClientConfig) {
    this.dockerClientConfig = dockerClientConfig;
    return this;
  }

  /**
   * The Docker client config for the service.
   * @return dockerClientConfig
   */
  @ApiModelProperty(value = "The Docker client config for the service")
  public String getDockerClientConfig() {
    return dockerClientConfig;
  }

  public void setDockerClientConfig(String dockerClientConfig) {
    this.dockerClientConfig = dockerClientConfig;
  }

  @Override
  public boolean equals(java.lang.Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Service service = (Service) o;
    return Objects.equals(this.name, service.name);
  }

  @Override
  public int hashCode() {
    return Objects.hash(name);
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class Service {\n")

        .append("    name: ").append(toIndentedString(name)).append("\n")
        .append("    id: ").append(toIndentedString(id)).append("\n")
        .append("    version: ").append(toIndentedString(version)).append("\n")
        .append("    description: ").append(toIndentedString(description))
        .append("\n")
        .append("    artifact: ").append(toIndentedString(artifact))
        .append("\n")
        .append("    resource: ").append(toIndentedString(resource))
        .append("\n")
        .append("    launchTime: ").append(toIndentedString(launchTime))
        .append("\n")
        .append("    numberOfRunningContainers: ")
        .append(toIndentedString(numberOfRunningContainers)).append("\n")
        .append("    lifetime: ").append(toIndentedString(lifetime))
        .append("\n")
        .append("    components: ").append(toIndentedString(components))
        .append("\n")
        .append("    configuration: ").append(toIndentedString(configuration))
        .append("\n")
        .append("    state: ").append(toIndentedString(state)).append("\n")
        .append("    quicklinks: ").append(toIndentedString(quicklinks))
        .append("\n")
        .append("    queue: ").append(toIndentedString(queue)).append("\n")
        .append("    kerberosPrincipal: ")
        .append(toIndentedString(kerberosPrincipal)).append("\n")
        .append("    dockerClientConfig: ")
        .append(toIndentedString(dockerClientConfig)).append("\n")
        .append("    dependencies: ")
        .append(toIndentedString(dependencies)).append("\n")
        .append("}");
    return sb.toString();
  }

  /**
   * Convert the given object to string with each line indented by 4 spaces
   * (except the first line).
   */
  private String toIndentedString(java.lang.Object o) {
    if (o == null) {
      return "null";
    }
    return o.toString().replace("\n", "\n    ");
  }
}
