/*
 * 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.aws.ec2.config;
import static org.jclouds.reflect.Reflection2.typeToken;

import java.util.Map;

import javax.inject.Singleton;

import org.jclouds.aws.ec2.AWSEC2Api;
import org.jclouds.aws.ec2.AWSEC2AsyncApi;
import org.jclouds.aws.ec2.AWSEC2AsyncClient;
import org.jclouds.aws.ec2.AWSEC2Client;
import org.jclouds.aws.ec2.domain.AWSRunningInstance;
import org.jclouds.aws.ec2.domain.SpotInstanceRequest;
import org.jclouds.aws.ec2.features.AWSAMIAsyncApi;
import org.jclouds.aws.ec2.features.AWSAMIApi;
import org.jclouds.aws.ec2.features.AWSInstanceAsyncApi;
import org.jclouds.aws.ec2.features.AWSInstanceApi;
import org.jclouds.aws.ec2.features.AWSKeyPairAsyncApi;
import org.jclouds.aws.ec2.features.AWSKeyPairApi;
import org.jclouds.aws.ec2.features.AWSSecurityGroupAsyncApi;
import org.jclouds.aws.ec2.features.AWSSecurityGroupApi;
import org.jclouds.aws.ec2.features.MonitoringAsyncApi;
import org.jclouds.aws.ec2.features.MonitoringApi;
import org.jclouds.aws.ec2.features.PlacementGroupAsyncApi;
import org.jclouds.aws.ec2.features.PlacementGroupApi;
import org.jclouds.aws.ec2.features.SpotInstanceAsyncApi;
import org.jclouds.aws.ec2.features.SpotInstanceApi;
import org.jclouds.aws.ec2.functions.SpotInstanceRequestToAWSRunningInstance;
import org.jclouds.aws.ec2.options.AWSRunInstancesOptions;
import org.jclouds.aws.ec2.services.AWSAMIAsyncClient;
import org.jclouds.aws.ec2.services.AWSAMIClient;
import org.jclouds.aws.ec2.services.AWSInstanceAsyncClient;
import org.jclouds.aws.ec2.services.AWSInstanceClient;
import org.jclouds.aws.ec2.services.AWSKeyPairAsyncClient;
import org.jclouds.aws.ec2.services.AWSKeyPairClient;
import org.jclouds.aws.ec2.services.AWSSecurityGroupAsyncClient;
import org.jclouds.aws.ec2.services.AWSSecurityGroupClient;
import org.jclouds.aws.ec2.services.MonitoringAsyncClient;
import org.jclouds.aws.ec2.services.MonitoringClient;
import org.jclouds.aws.ec2.services.PlacementGroupAsyncClient;
import org.jclouds.aws.ec2.services.PlacementGroupClient;
import org.jclouds.aws.ec2.services.SpotInstanceAsyncClient;
import org.jclouds.aws.ec2.services.SpotInstanceClient;
import org.jclouds.ec2.EC2Api;
import org.jclouds.ec2.EC2AsyncApi;
import org.jclouds.ec2.EC2AsyncClient;
import org.jclouds.ec2.EC2Client;
import org.jclouds.ec2.config.EC2RestClientModule;
import org.jclouds.ec2.features.AMIAsyncApi;
import org.jclouds.ec2.features.AMIApi;
import org.jclouds.ec2.features.AvailabilityZoneAndRegionAsyncApi;
import org.jclouds.ec2.features.AvailabilityZoneAndRegionApi;
import org.jclouds.ec2.features.ElasticBlockStoreAsyncApi;
import org.jclouds.ec2.features.ElasticBlockStoreApi;
import org.jclouds.ec2.features.ElasticIPAddressAsyncApi;
import org.jclouds.ec2.features.ElasticIPAddressApi;
import org.jclouds.ec2.features.InstanceAsyncApi;
import org.jclouds.ec2.features.InstanceApi;
import org.jclouds.ec2.features.SecurityGroupAsyncApi;
import org.jclouds.ec2.features.SecurityGroupApi;
import org.jclouds.ec2.features.SubnetApi;
import org.jclouds.ec2.features.SubnetAsyncApi;
import org.jclouds.ec2.features.TagApi;
import org.jclouds.ec2.features.TagAsyncApi;
import org.jclouds.ec2.features.WindowsApi;
import org.jclouds.ec2.features.WindowsAsyncApi;
import org.jclouds.ec2.options.RunInstancesOptions;
import org.jclouds.ec2.services.AMIAsyncClient;
import org.jclouds.ec2.services.AMIClient;
import org.jclouds.ec2.services.AvailabilityZoneAndRegionAsyncClient;
import org.jclouds.ec2.services.AvailabilityZoneAndRegionClient;
import org.jclouds.ec2.services.ElasticBlockStoreAsyncClient;
import org.jclouds.ec2.services.ElasticBlockStoreClient;
import org.jclouds.ec2.services.ElasticIPAddressAsyncClient;
import org.jclouds.ec2.services.ElasticIPAddressClient;
import org.jclouds.ec2.services.InstanceAsyncClient;
import org.jclouds.ec2.services.InstanceClient;
import org.jclouds.ec2.services.SecurityGroupAsyncClient;
import org.jclouds.ec2.services.SecurityGroupClient;
import org.jclouds.ec2.services.WindowsAsyncClient;
import org.jclouds.ec2.services.WindowsClient;
import org.jclouds.rest.ConfiguresRestClient;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;

/**
 * Configures the EC2 connection.
 * 
 * @author Adrian Cole
 */
@ConfiguresRestClient
public class AWSEC2RestClientModule extends EC2RestClientModule<AWSEC2Client, AWSEC2AsyncClient> {

   public static final Map<Class<?>, Class<?>> DELEGATE_MAP = ImmutableMap.<Class<?>, Class<?>> builder()
         .put(AWSAMIClient.class, AWSAMIAsyncClient.class)
         .put(ElasticIPAddressClient.class, ElasticIPAddressAsyncClient.class)
         .put(AWSInstanceClient.class, AWSInstanceAsyncClient.class)
         .put(AWSKeyPairClient.class, AWSKeyPairAsyncClient.class)
         .put(AWSSecurityGroupClient.class, AWSSecurityGroupAsyncClient.class)
         .put(PlacementGroupClient.class, PlacementGroupAsyncClient.class)
         .put(MonitoringClient.class, MonitoringAsyncClient.class)
         .put(WindowsClient.class, WindowsAsyncClient.class)
         .put(AvailabilityZoneAndRegionClient.class, AvailabilityZoneAndRegionAsyncClient.class)
         .put(ElasticBlockStoreClient.class, ElasticBlockStoreAsyncClient.class)
         .put(SpotInstanceClient.class, SpotInstanceAsyncClient.class)
         .put(AWSAMIApi.class, AWSAMIAsyncApi.class)
         .put(ElasticIPAddressApi.class, ElasticIPAddressAsyncApi.class)
         .put(AWSInstanceApi.class, AWSInstanceAsyncApi.class)
         .put(AWSKeyPairApi.class, AWSKeyPairAsyncApi.class)
         .put(AWSSecurityGroupApi.class, AWSSecurityGroupAsyncApi.class)
         .put(PlacementGroupApi.class, PlacementGroupAsyncApi.class)
         .put(MonitoringApi.class, MonitoringAsyncApi.class)
         .put(AvailabilityZoneAndRegionApi.class, AvailabilityZoneAndRegionAsyncApi.class)
         .put(ElasticBlockStoreApi.class, ElasticBlockStoreAsyncApi.class)
         .put(SpotInstanceApi.class, SpotInstanceAsyncApi.class)
         .put(WindowsApi.class, WindowsAsyncApi.class)
         .put(TagApi.class, TagAsyncApi.class)
         .put(SubnetApi.class, SubnetAsyncApi.class)
         .build();

   public AWSEC2RestClientModule() {
      super(typeToken(AWSEC2Client.class), typeToken(AWSEC2AsyncClient.class), DELEGATE_MAP);
   }

   @Singleton
   @Provides
   EC2Client provide(AWSEC2Client in) {
      return in;
   }

   @Singleton
   @Provides
   EC2AsyncClient provide(AWSEC2AsyncClient in) {
      return in;
   }

   @Singleton
   @Provides
   InstanceClient getInstanceServices(AWSEC2Client in) {
      return in.getInstanceServices();
   }

   @Singleton
   @Provides
   InstanceAsyncClient getInstanceServices(AWSEC2AsyncClient in) {
      return in.getInstanceServices();
   }

   @Singleton
   @Provides
   SecurityGroupClient getSecurityGroupServices(AWSEC2Client in) {
      return in.getSecurityGroupServices();
   }

   @Singleton
   @Provides
   SecurityGroupAsyncClient getSecurityGroupServices(AWSEC2AsyncClient in) {
      return in.getSecurityGroupServices();
   }

   @Singleton
   @Provides
   AMIClient getAMIServices(AWSEC2Client in) {
      return in.getAMIServices();
   }

   @Singleton
   @Provides
   AMIAsyncClient getAMIServices(AWSEC2AsyncClient in) {
      return in.getAMIServices();
   }

   @Override
   protected void configure() {
      bind(RunInstancesOptions.class).to(AWSRunInstancesOptions.class);
      bind(new TypeLiteral<Function<SpotInstanceRequest, AWSRunningInstance>>() {
      }).to(SpotInstanceRequestToAWSRunningInstance.class);
      super.configure();
   }
}
