package com.senseidb.search.node;

import com.linkedin.norbert.javacompat.network.RequestBuilder;
import com.linkedin.norbert.network.ResponseIterator;
import com.linkedin.norbert.network.common.TimeoutIterator;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

import com.linkedin.norbert.NorbertException;
import com.linkedin.norbert.javacompat.cluster.ClusterClient;
import com.linkedin.norbert.javacompat.cluster.Node;
import com.linkedin.norbert.javacompat.network.PartitionedNetworkClient;
import com.senseidb.cluster.routing.RoutingInfo;
import com.senseidb.search.req.SenseiRequest;
import com.senseidb.search.req.SenseiSystemInfo;
import com.senseidb.svc.impl.SysSenseiCoreServiceImpl;

public class SenseiSysBroker extends AbstractConsistentHashBroker<SenseiRequest, SenseiSystemInfo>
{
  private final static Logger logger = Logger.getLogger(SenseiSysBroker.class);
  private final static long TIMEOUT_MILLIS = 8000L;
  private long _timeoutMillis = TIMEOUT_MILLIS;
  private final Comparator<String> _versionComparator;
  private final boolean allowPartialMerge;

  protected Set<Node> _nodes = Collections.EMPTY_SET;

  public SenseiSysBroker(PartitionedNetworkClient<String> networkClient, ClusterClient clusterClient, Comparator<String> versionComparator, boolean allowPartialMerge) throws NorbertException
  {
    super(networkClient, SysSenseiCoreServiceImpl.JAVA_SERIALIZER);
    _versionComparator = versionComparator;
    this.allowPartialMerge = allowPartialMerge;
    clusterClient.addListener(this);
    logger.info("created broker instance " + networkClient + " " + clusterClient);
  }

  @Override
  public SenseiSystemInfo mergeResults(SenseiRequest request, List<SenseiSystemInfo> resultList)
  {
    SenseiSystemInfo result = new SenseiSystemInfo();
    if (resultList == null)
      return result;

    for (SenseiSystemInfo res : resultList)
    {
      result.setNumDocs(result.getNumDocs()+res.getNumDocs());
      result.setSchema(res.getSchema());
      if (result.getLastModified() < res.getLastModified())
        result.setLastModified(res.getLastModified());
      if (result.getVersion() == null || _versionComparator.compare(result.getVersion(), res.getVersion()) < 0)
        result.setVersion(res.getVersion());
      if (res.getFacetInfos() != null)
        result.setFacetInfos(res.getFacetInfos());
      if (res.getClusterInfo() != null) {
        if (result.getClusterInfo() != null)
          result.getClusterInfo().addAll(res.getClusterInfo());
        else
          result.setClusterInfo(res.getClusterInfo());
      }
    }

    return result;
  }


  @Override
  protected List<SenseiSystemInfo> doCall(final SenseiRequest req) throws ExecutionException {
    final List<SenseiSystemInfo> resultList = new ArrayList<SenseiSystemInfo>();
    ResponseIterator<SenseiSystemInfo> responseIterator =
        buildIterator(_networkClient.sendRequestToOneReplica(getRouteParam(req), new RequestBuilder<Integer, SenseiRequest>() {
          @Override
          public SenseiRequest apply(Node node, Set<Integer> nodePartitions) {
            synchronized (req) {
              req.setPartitions(node.getPartitionIds());
              return req;
            }
          }
        }, _serializer));

    while(responseIterator.hasNext()) {
      resultList.add(responseIterator.next());
    }

    logger.debug(String.format("There are %d responses", resultList.size()));

    return resultList;

  }

  @Override
  public SenseiSystemInfo getEmptyResultInstance()
  {
    return new SenseiSystemInfo();
  }

  @Override
  public void setTimeoutMillis(long timeoutMillis){
    _timeoutMillis = timeoutMillis;
  }

  @Override
  public long getTimeoutMillis(){
    return _timeoutMillis;
  }

  public void handleClusterConnected(Set<Node> nodes)
  {
    _partitions = getPartitions(nodes);
    _nodes = nodes;
    logger.info("handleClusterConnected(): Received the list of nodes from norbert " + nodes.toString());
    logger.info("handleClusterConnected(): Received the list of partitions from router " + _partitions.toString());
  }

  public void handleClusterDisconnected()
  {
    logger.info("handleClusterDisconnected() called");
    _partitions = new IntOpenHashSet();
    _nodes = Collections.EMPTY_SET;
  }

  public void handleClusterNodesChanged(Set<Node> nodes)
  {
    _partitions = getPartitions(nodes);
    _nodes = nodes;
    logger.info("handleClusterNodesChanged(): Received the list of nodes from norbert " + nodes.toString());
    logger.info("handleClusterNodesChanged(): Received the list of partitions from router " + _partitions.toString());
  }

  @Override
  public void handleClusterShutdown()
  {
    logger.info("handleClusterShutdown() called");
  }

  @Override
  public boolean allowPartialMerge() {
    return allowPartialMerge;
  }
}


@@ -3235,83 +3235,153 @@
     
-ResponseIterator%3CSenseiSystemInfo%3E responseIterator =%0A        buildIterator
+List%3CFuture%3CSenseiSystemInfo%3E%3E futures = new ArrayList%3CFuture%3CSenseiSystemInfo%3E%3E(_nodes.size());%0A    for(Node n : _nodes)%0A    %7B%0A      futures.add
 (_ne
@@ -3409,224 +3409,117 @@
 stTo
-OneReplica(getRouteParam
+Node
 (req
-)
 , n
-ew RequestBuilder%3CInteger, SenseiRequest%3E() %7B
+, _serializer));
 %0A    
-  
+%7D%0A
     
-@Override%0A          public SenseiRequest apply(Node node, Set%3CInteger%3E nodePartitions) %7B%0A            synchronized (req) %7B
+for(Future%3CSenseiSystemInfo%3E future : futures)%0A    %7B%0A      try
 %0A      
+%7B%0A
     
@@ -3528,45 +3528,60 @@
   re
-q.setPartitions(node.getPartitionIds(
+sultList.add(future.get(2000L, TimeUnit.MILLISECONDS
 ));%0A
@@ -3590,53 +3590,42 @@
     
+%7D%0A
       
-  return req;%0A            %7D%0A   
+catch(Exception e)%0A
       
- %7D
+%7B
 %0A   
@@ -3633,113 +3633,61 @@
     
-%7D, _serializer));%0A%0A    while(responseIterator.hasNext()) %7B%0A      resultList.add(responseIterator.next());
+logger.error(%22Failed to get the sysinfo%22, e);%0A      %7D
 %0A   

Fix sysinfo.