/*
 * Copyright DataStax, Inc.
 *
 * Licensed 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 com.datastax.oss.driver.internal.core.metadata;

import com.datastax.oss.driver.api.core.metadata.EndPoint;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.NotThreadSafe;

@Immutable
public class DefaultNodeInfo implements NodeInfo {
  public static Builder builder() {
    return new Builder();
  }

  private final EndPoint endPoint;
  private final InetSocketAddress broadcastRpcAddress;
  private final InetSocketAddress broadcastAddress;
  private final InetSocketAddress listenAddress;
  private final String datacenter;
  private final String rack;
  private final String cassandraVersion;
  private final String partitioner;
  private final Set<String> tokens;
  private final Map<String, Object> extras;
  private final UUID hostId;
  private final UUID schemaVersion;

  private DefaultNodeInfo(Builder builder) {
    this.endPoint = builder.endPoint;
    this.broadcastRpcAddress = builder.broadcastRpcAddress;
    this.broadcastAddress = builder.broadcastAddress;
    this.listenAddress = builder.listenAddress;
    this.datacenter = builder.datacenter;
    this.rack = builder.rack;
    this.cassandraVersion = builder.cassandraVersion;
    this.partitioner = builder.partitioner;
    this.tokens = (builder.tokens == null) ? Collections.emptySet() : builder.tokens;
    this.hostId = builder.hostId;
    this.schemaVersion = builder.schemaVersion;
    this.extras = (builder.extras == null) ? Collections.emptyMap() : builder.extras;
  }

  @NonNull
  @Override
  public EndPoint getEndPoint() {
    return endPoint;
  }

  @NonNull
  @Override
  public Optional<InetSocketAddress> getBroadcastRpcAddress() {
    return Optional.ofNullable(broadcastRpcAddress);
  }

  @NonNull
  @Override
  public Optional<InetSocketAddress> getBroadcastAddress() {
    return Optional.ofNullable(broadcastAddress);
  }

  @NonNull
  @Override
  public Optional<InetSocketAddress> getListenAddress() {
    return Optional.ofNullable(listenAddress);
  }

  @Override
  public String getDatacenter() {
    return datacenter;
  }

  @Override
  public String getRack() {
    return rack;
  }

  @Override
  public String getCassandraVersion() {
    return cassandraVersion;
  }

  @Override
  public String getPartitioner() {
    return partitioner;
  }

  @Override
  public Set<String> getTokens() {
    return tokens;
  }

  @Override
  public Map<String, Object> getExtras() {
    return extras;
  }

  @NonNull
  @Override
  public UUID getHostId() {
    return hostId;
  }

  @Override
  public UUID getSchemaVersion() {
    return schemaVersion;
  }

  @NotThreadSafe
  public static class Builder {
    private EndPoint endPoint;
    private InetSocketAddress broadcastRpcAddress;
    private InetSocketAddress broadcastAddress;
    private InetSocketAddress listenAddress;
    private String datacenter;
    private String rack;
    private String cassandraVersion;
    private String partitioner;
    private Set<String> tokens;
    private Map<String, Object> extras;
    private UUID hostId;
    private UUID schemaVersion;

    public Builder withEndPoint(@NonNull EndPoint endPoint) {
      this.endPoint = endPoint;
      return this;
    }

    public Builder withBroadcastRpcAddress(@Nullable InetSocketAddress address) {
      this.broadcastRpcAddress = address;
      return this;
    }

    public Builder withBroadcastAddress(@Nullable InetSocketAddress address) {
      this.broadcastAddress = address;
      return this;
    }

    public Builder withListenAddress(@Nullable InetSocketAddress address) {
      this.listenAddress = address;
      return this;
    }

    public Builder withDatacenter(@Nullable String datacenter) {
      this.datacenter = datacenter;
      return this;
    }

    public Builder withRack(@Nullable String rack) {
      this.rack = rack;
      return this;
    }

    public Builder withCassandraVersion(@Nullable String cassandraVersion) {
      this.cassandraVersion = cassandraVersion;
      return this;
    }

    public Builder withPartitioner(@Nullable String partitioner) {
      this.partitioner = partitioner;
      return this;
    }

    public Builder withTokens(@Nullable Set<String> tokens) {
      this.tokens = tokens;
      return this;
    }

    public Builder withHostId(@NonNull UUID hostId) {
      this.hostId = hostId;
      return this;
    }

    public Builder withSchemaVersion(@Nullable UUID schemaVersion) {
      this.schemaVersion = schemaVersion;
      return this;
    }

    public Builder withExtra(@NonNull String key, @Nullable Object value) {
      if (value != null) {
        if (this.extras == null) {
          this.extras = new HashMap<>();
        }
        this.extras.put(key, value);
      }
      return this;
    }

    public DefaultNodeInfo build() {
      return new DefaultNodeInfo(this);
    }
  }
}