/**
 * Licensed to jclouds, Inc. (jclouds) under one or more
 * contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  jclouds 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.jclouds.compute.domain.internal;

import static com.google.common.base.Preconditions.checkNotNull;

import java.net.URI;
import java.util.Map;
import java.util.Set;

import org.jclouds.javax.annotation.Nullable;

import org.jclouds.compute.domain.ComputeType;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeState;
import org.jclouds.compute.domain.OperatingSystem;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.domain.LoginCredentials.Builder;

import com.google.common.collect.ImmutableSet;

/**
 * @author Adrian Cole
 * @author Ivan Meredith
 */
public class NodeMetadataImpl extends ComputeMetadataImpl implements NodeMetadata {

   /** The serialVersionUID */
   private static final long serialVersionUID = 7924307572338157887L;

   private final NodeState state;
   private final int loginPort;
   private final Set<String> publicAddresses;
   private final Set<String> privateAddresses;
   @Nullable
   private final LoginCredentials credentials;
   @Nullable
   private final String group;
   @Nullable
   private final String imageId;
   @Nullable
   private final Hardware hardware;
   @Nullable
   private final OperatingSystem os;
   @Nullable
   private final String hostname;

   /**
    * <h4>will be removed in jclouds 1.4.0</h4>
    */
   @Deprecated
   public NodeMetadataImpl(String providerId, String name, String id, Location location, URI uri,
         Map<String, String> userMetadata, Set<String> tags, @Nullable String group, @Nullable Hardware hardware,
         @Nullable String imageId, @Nullable OperatingSystem os, NodeState state, int loginPort,
         Iterable<String> publicAddresses, Iterable<String> privateAddresses, @Nullable String adminPassword,
         @Nullable Credentials credentials, String hostname) {
      super(ComputeType.NODE, providerId, name, id, location, uri, userMetadata, tags);
      this.group = group;
      this.hardware = hardware;
      this.imageId = imageId;
      this.os = os;
      this.state = checkNotNull(state, "state");
      this.loginPort = loginPort;
      this.publicAddresses = ImmutableSet.copyOf(checkNotNull(publicAddresses, "publicAddresses"));
      this.privateAddresses = ImmutableSet.copyOf(checkNotNull(privateAddresses, "privateAddresses"));
      this.hostname = hostname;
      Builder builder = LoginCredentials.builder(credentials);
      if (adminPassword != null) {
         builder.authenticateSudo(true);
         builder.password(adminPassword);
      }
      this.credentials = builder.build();
   }

   public NodeMetadataImpl(String providerId, String name, String id, Location location, URI uri,
         Map<String, String> userMetadata, Set<String> tags, @Nullable String group, @Nullable Hardware hardware,
         @Nullable String imageId, @Nullable OperatingSystem os, NodeState state, int loginPort,
         Iterable<String> publicAddresses, Iterable<String> privateAddresses, @Nullable LoginCredentials credentials,
         String hostname) {
      super(ComputeType.NODE, providerId, name, id, location, uri, userMetadata, tags);
      this.group = group;
      this.hardware = hardware;
      this.imageId = imageId;
      this.os = os;
      this.state = checkNotNull(state, "state");
      this.loginPort = loginPort;
      this.publicAddresses = ImmutableSet.copyOf(checkNotNull(publicAddresses, "publicAddresses"));
      this.privateAddresses = ImmutableSet.copyOf(checkNotNull(privateAddresses, "privateAddresses"));
      this.credentials = credentials;
      this.hostname = hostname;
   }
   
   /**
    * {@inheritDoc}
    */
   @Override
   public String getGroup() {
      return group;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public Hardware getHardware() {
      return hardware;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   @Deprecated
   public String getAdminPassword() {
      return (credentials != null && credentials.shouldAuthenticateSudo()) ? credentials.getPassword() : null;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public LoginCredentials getCredentials() {
      return credentials;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public Set<String> getPublicAddresses() {
      return publicAddresses;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public Set<String> getPrivateAddresses() {
      return privateAddresses;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public NodeState getState() {
      return state;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public int getLoginPort() {
      return this.loginPort;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String getImageId() {
      return imageId;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public OperatingSystem getOperatingSystem() {
      return os;
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String getHostname() {
      return hostname;
   }

   @Override
   public String toString() {
      return "[id=" + getId() + ", providerId=" + getProviderId() + ", group=" + getGroup() + ", name=" + getName()
            + ", location=" + getLocation() + ", uri=" + getUri() + ", imageId=" + getImageId() + ", os="
            + getOperatingSystem() + ", state=" + getState() + ", loginPort=" + getLoginPort() + ", hostname="
            + getHostname() + ", privateAddresses=" + privateAddresses + ", publicAddresses=" + publicAddresses
            + ", hardware=" + getHardware() + ", loginUser=" + ((credentials != null) ? credentials.identity : null)
            + ", userMetadata=" + getUserMetadata() + ", tags=" + tags + "]";
   }

   @Override
   public int hashCode() {
      final int prime = 31;
      int result = super.hashCode();
      result = prime * result + loginPort;
      result = prime * result + ((privateAddresses == null) ? 0 : privateAddresses.hashCode());
      result = prime * result + ((publicAddresses == null) ? 0 : publicAddresses.hashCode());
      result = prime * result + ((group == null) ? 0 : group.hashCode());
      result = prime * result + ((hostname == null) ? 0 : hostname.hashCode());
      result = prime * result + ((imageId == null) ? 0 : imageId.hashCode());
      result = prime * result + ((hardware == null) ? 0 : hardware.hashCode());
      result = prime * result + ((os == null) ? 0 : os.hashCode());
      result = prime * result + ((credentials == null) ? 0 : credentials.hashCode());
      return result;
   }

   @Override
   public boolean equals(Object obj) {
      if (this == obj)
         return true;
      if (!super.equals(obj))
         return false;
      if (getClass() != obj.getClass())
         return false;
      NodeMetadataImpl other = (NodeMetadataImpl) obj;
      if (loginPort != other.loginPort)
         return false;
      if (privateAddresses == null) {
         if (other.privateAddresses != null)
            return false;
      } else if (!privateAddresses.equals(other.privateAddresses))
         return false;
      if (publicAddresses == null) {
         if (other.publicAddresses != null)
            return false;
      } else if (!publicAddresses.equals(other.publicAddresses))
         return false;
      if (hostname == null) {
         if (other.hostname != null)
            return false;
      } else if (!hostname.equals(other.hostname))
         return false;
      if (group == null) {
         if (other.group != null)
            return false;
      } else if (!group.equals(other.group))
         return false;
      if (imageId == null) {
         if (other.imageId != null)
            return false;
      } else if (!imageId.equals(other.imageId))
         return false;
      if (hardware == null) {
         if (other.hardware != null)
            return false;
      } else if (!hardware.equals(other.hardware))
         return false;
      if (os == null) {
         if (other.os != null)
            return false;
      } else if (!os.equals(other.os))
         return false;
      if (credentials == null) {
         if (other.credentials != null)
            return false;
      } else if (!credentials.equals(other.credentials))
         return false;
      return true;
   }

}
