/*
 * Copyright 2017 Huawei Technologies Co., Ltd
 *
 * 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 io.servicecomb.serviceregistry.client;

import static io.servicecomb.serviceregistry.api.Const.REGISTRY_APP_ID;
import static io.servicecomb.serviceregistry.api.Const.REGISTRY_SERVICE_NAME;
import static io.servicecomb.serviceregistry.api.Const.REGISTRY_VERSION;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.servicecomb.foundation.common.net.IpPort;
import io.servicecomb.foundation.common.net.URIEndpointObject;
import io.servicecomb.serviceregistry.cache.CacheEndpoint;
import io.servicecomb.serviceregistry.cache.InstanceCache;
import io.servicecomb.serviceregistry.cache.InstanceCacheManager;
import io.servicecomb.serviceregistry.config.ServiceRegistryConfig;

public class IpPortManager {
  private static final Logger LOGGER = LoggerFactory.getLogger(IpPortManager.class);

  private ServiceRegistryConfig serviceRegistryConfig;

  private InstanceCacheManager instanceCacheManager;

  private String defaultTransport = "rest";

  private ArrayList<IpPort> defaultIpPort;

  private InstanceCache instanceCache = null;

  private AtomicInteger currentAvailbleIndex = new AtomicInteger(0);

  private Object lock = new Object();

  public IpPortManager(ServiceRegistryConfig serviceRegistryConfig, InstanceCacheManager instanceCacheManager) {
    this.serviceRegistryConfig = serviceRegistryConfig;
    this.instanceCacheManager = instanceCacheManager;

    defaultTransport = serviceRegistryConfig.getTransport();
    defaultIpPort = serviceRegistryConfig.getIpPort();
    if (defaultIpPort.size() == 0) {
      throw new IllegalArgumentException("Service center address is required to start the application.");
    }
  }

  // we have to do this operation after the first time setup has already done
  public void initAutoDiscovery() {
    if (this.serviceRegistryConfig.isRegistryAutoDiscovery()) {
      instanceCache = instanceCacheManager.getOrCreate(REGISTRY_APP_ID,
          REGISTRY_SERVICE_NAME,
          REGISTRY_VERSION);
    }
  }

  public IpPort getAvailableAddress(boolean invalidate) {
    // This should be very fast in normal case. It's fine to work slow when need to try another instance.
    synchronized (lock) {
      if (invalidate) {
        currentAvailbleIndex.incrementAndGet();
      }
      if (currentAvailbleIndex.get() < defaultIpPort.size()) {
        return returnWithLog(invalidate, defaultIpPort.get(currentAvailbleIndex.get()));
      }
      List<CacheEndpoint> endpoints = getDiscoveredIpPort();
      if (currentAvailbleIndex.get() >= defaultIpPort.size() + endpoints.size()) {
        currentAvailbleIndex.set(0);
        return returnWithLog(invalidate, defaultIpPort.get(0));
      }
      CacheEndpoint nextEndpoint = endpoints.get(currentAvailbleIndex.get() - defaultIpPort.size());
      return returnWithLog(invalidate, new URIEndpointObject(nextEndpoint.getEndpoint()));
    }
  }

  private IpPort returnWithLog(boolean needLog, IpPort result) {
    if (needLog) {
      LOGGER.info("Using next service center address {}:{}.", result.getHostOrIp(), result.getPort());
    }
    return result;
  }

  private List<CacheEndpoint> getDiscoveredIpPort() {
    if (instanceCache == null) {
      return new ArrayList<>(0);
    }
    instanceCache = instanceCacheManager.getOrCreate(REGISTRY_APP_ID,
        REGISTRY_SERVICE_NAME,
        REGISTRY_VERSION);
    return instanceCache.getOrCreateTransportMap().get(defaultTransport);
  }
}

@@ -1840,47 +1840,8 @@
 );%0A%0A
-  private Object lock = new Object();%0A%0A
   pu
@@ -2719,138 +2719,19 @@
     
-// This should be very fast in normal case. It's fine to work slow when need to try another instance.%0A    synchronized (lock) %7B%0A  
+int index;%0A
     
@@ -2746,33 +2746,39 @@
 lidate) %7B%0A      
- 
+index =
  currentAvailble
@@ -2810,19 +2810,24 @@
     
-  %7D
+%7D else %7B
 %0A      i
 f (c
@@ -2814,35 +2814,39 @@
 %7D else %7B%0A      i
-f (
+ndex = 
 currentAvailbleI
@@ -2855,16 +2855,38 @@
 ex.get()
+;%0A    %7D%0A%0A    if (index
  %3C defau
@@ -2900,26 +2900,24 @@
 t.size()) %7B%0A
-  
       return
@@ -2965,42 +2965,21 @@
 get(
-currentAvailbleIndex.get()
+index
 ));%0A    
   %7D%0A
@@ -2970,30 +2970,26 @@
 ndex));%0A    
- 
- 
 %7D%0A
-  
     List%3CCac
@@ -3043,40 +3043,17 @@
     
-  
 if (
-currentAvailbleIndex.get()
+index
  %3E= 
@@ -3091,26 +3091,24 @@
 s.size()) %7B%0A
-  
       curren
@@ -3132,26 +3132,24 @@
 t(0);%0A      
-  
 return retur
@@ -3188,26 +3188,24 @@
 rt.get(0));%0A
-  
     %7D%0A      
@@ -3202,18 +3202,16 @@
   %7D%0A    
-  
 CacheEnd
@@ -3249,34 +3249,13 @@
 get(
-currentAvailbleIndex.get()
+index
  - d
@@ -3276,18 +3276,16 @@
 ize());%0A
-  
     retu
@@ -3365,22 +3365,16 @@
 nt()));%0A
-    %7D%0A
   %7D%0A%0A  p

remove lock