/*
 * Copyright (C) 2014 Indeed 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.indeed.imhotep.client;

import com.google.common.collect.Maps;
import com.indeed.util.core.DataLoadingRunnable;
import com.indeed.imhotep.DatasetInfo;
import com.indeed.imhotep.ImhotepRemoteSession;
import org.apache.log4j.Logger;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * @author jsgroth
 */
class ImhotepClientShardListReloader extends DataLoadingRunnable {
    private static final Logger log = Logger.getLogger(ImhotepClientShardListReloader.class);

    private final HostsReloader hostsReloader;
    private final ExecutorService rpcExecutor;

    private final Object shardListRpcLock = new Object();

    private volatile Map<Host, List<DatasetInfo>> shardList = Collections.emptyMap();

    ImhotepClientShardListReloader(HostsReloader hostsReloader, ExecutorService rpcExecutor) {
        super("ImhotepClientShardListReloader");
        
        this.hostsReloader = hostsReloader;
        this.rpcExecutor = rpcExecutor;
    }

    @Override
    public boolean load() {
        try {
            final Map<Host, List<DatasetInfo>> newShardList = shardListRpc();
            if (newShardList.isEmpty()) {
                log.error("unable to retrieve shard list from any imhotep daemons");
                loadFailed();
                return false;
            }
            if (!newShardList.equals(shardList)) {
                shardList = newShardList;
            }
            return true;
        } catch (Exception e) {
            log.error("Error reloading hosts", e);
            loadFailed();
            return false;
        }
    }

    public Map<Host, List<DatasetInfo>> getShardList() {
        return shardList;
    }

    private Map<Host, List<DatasetInfo>> shardListRpc() {
        synchronized (shardListRpcLock) {
            final List<Host> hosts = hostsReloader.getHosts();

            final Map<Host, Future<List<DatasetInfo>>> futures = Maps.newHashMap();
            for (final Host host : hosts) {
                final Future<List<DatasetInfo>> future = rpcExecutor.submit(new Callable<List<DatasetInfo>>() {
                    @Override
                    public List<DatasetInfo> call() throws Exception {
                        return ImhotepRemoteSession.getShardInfoList(host.hostname, host.port);
                    }
                });
                futures.put(host, future);
            }

            final Map<Host, List<DatasetInfo>> ret = Maps.newHashMapWithExpectedSize(hosts.size());
            for (final Host host : futures.keySet()) {
                try {
                    final List<DatasetInfo> shardList = futures.get(host).get();
                    ret.put(host, shardList);
                } catch (ExecutionException e) {
                    log.error("error getting shard list from " + host, e);
                } catch (InterruptedException e) {
                    log.error("error getting shard list from " + host, e);
                }
            }

            return ret;
        }
    }
}
