/*
 * 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.jclouds.openstack.neutron.v2.domain;

import java.beans.ConstructorProperties;

import javax.inject.Named;

import org.jclouds.javax.annotation.Nullable;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;

/**
 * Contains a mapping between a MAC address and an IP address.
 */
public class SecurityGroup {

   private String id;
   @Named("tenant_id")
   private String tenantId;
   private String name;
   private String description;
   @Named("security_group_rules")
   private ImmutableList<Rule> rules;

   @ConstructorProperties({"id", "tenant_id", "name", "description", "security_group_rules"})
   protected SecurityGroup(String id, String tenantId, String name, String description,
         ImmutableList<Rule> rules) {
      this.id = id;
      this.tenantId = tenantId;
      this.name = name;
      this.description = description;
      this.rules = rules;
   }

   private SecurityGroup(SecurityGroup securityGroup) {
      this(securityGroup.id,
            securityGroup.tenantId,
            securityGroup.name,
            securityGroup.description,
            securityGroup.rules
      );
   }

   private SecurityGroup() {}

   /**
    * @return The identifier for this Security Group.
    */
   @Nullable
   public String getId() {
      return id;
   }

   /**
    * @return The identifier of the tenant for this Security Group.
    */
   @Nullable
   public String getTenantId() {
      return tenantId;
   }

   /**
    * @return The name of the Security Group.
    */
   @Nullable
   public String getName() {
      return name;
   }

   /**
    * @return The description of the Security Group.
    */
   @Nullable
   public String getDescription() {
      return description;
   }

   /**
    * @return The collection of rules for this Security Group.
    */
   public ImmutableList<Rule> getRules() {
      return rules != null ? rules : ImmutableList.<Rule>of();
   }

   @Override
   public boolean equals(Object o) {
      if (this == o)
         return true;
      if (o == null || getClass() != o.getClass())
         return false;

      SecurityGroup that = (SecurityGroup) o;

      return Objects.equal(this.id, that.id) &&
            Objects.equal(this.tenantId, that.tenantId) &&
            Objects.equal(this.name, that.name) &&
            Objects.equal(this.description, that.description) &&
            Objects.equal(this.rules, that.rules);
   }

   @Override
   public int hashCode() {
      return Objects.hashCode(id, tenantId, name, description, rules);
   }

   @Override
   public String toString() {
      return MoreObjects.toStringHelper(this)
            .add("id", id)
            .add("tenantId", tenantId)
            .add("name", name)
            .add("description", description)
            .add("securityGroupRules", rules)
            .toString();
   }

   /*
    * Methods to get the Create and Update builders follow
    */

   /**
    * @return the Builder for creating a new SecurityGroup
    */
   public static CreateBuilder createBuilder() {
      return new CreateBuilder();
   }

   private abstract static class Builder<ParameterizedBuilderType> {
      // Keep track of the builder's state.
      protected SecurityGroup securityGroup;

      private Builder() {
         securityGroup = new SecurityGroup();
      }

      protected abstract ParameterizedBuilderType self();

      /**
       * The tenant id for this Security Group. Usually can only be specified by administrators.
       *
       * @return the Builder.
       * @see SecurityGroup#getTenantId()
       */
      public ParameterizedBuilderType tenantId(String tenantId) {
         securityGroup.tenantId = tenantId;
         return self();
      }

      /**
       * The name for this Security Group.
       *
       * @return the Builder.
       * @see SecurityGroup#getName()
       */
      public ParameterizedBuilderType name(String name) {
         securityGroup.name = name;
         return self();
      }

      /**
       * The description for this Security Group.
       *
       * @return the Builder.
       * @see SecurityGroup#getDescription()
       */
      public ParameterizedBuilderType description(String description) {
         securityGroup.description = description;
         return self();
      }
   }

   /**
    * Create and Update builders (inheriting from Builder)
    */
   public static class CreateBuilder extends Builder<CreateBuilder> {
      /**
       * Supply required properties for creating a Builder
       */
      private CreateBuilder() {
      }

      /**
       * @return a CreateSecurityGroup constructed with this Builder.
       */
      public CreateSecurityGroup build() {
         return new CreateSecurityGroup(securityGroup);
      }

      protected CreateBuilder self() {
         return this;
      }
   }

   /**
    * Create and Update options - extend the domain class, passed to API update and create calls.
    * Essentially the same as the domain class. Ensure validation and safe typing.
    */
   public static class CreateSecurityGroup extends SecurityGroup {
      private CreateSecurityGroup(SecurityGroup securityGroup) {
         super(securityGroup);
      }
   }
}
