/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * 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 jetbrains.buildServer.clouds.vmware.vmrun.remote.server;

import com.intellij.openapi.diagnostic.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import jetbrains.buildServer.clouds.vmware.vmrun.remote.RemoteTask;
import jetbrains.buildServer.clouds.vmware.vmrun.remote.RemoteTaskResult;
import jetbrains.buildServer.clouds.vmware.vmrun.remote.VMRunServer;
import jetbrains.buildServer.util.ExceptionUtil;
import org.jetbrains.annotations.NotNull;

/**
 * @author Eugene Petrenko
 *         Created: 08.12.2009 12:05:12
 */
public class Server<T> implements VMRunServer {
  private static final long TIMEOUT = 20*60;
  private static final Logger LOG = Logger.getInstance(Server.class.getName());

  private final Class<T> myInterface;
  private final ConcurrentMap<String, RemoteClient<T>> myClients = new ConcurrentHashMap<String, RemoteClient<T>>();

  public Server(final Class<T> anInterface, @NotNull final ScheduledExecutorService service) {
    myInterface = anInterface;
    if (!anInterface.isInterface()) {
      throw new IllegalArgumentException("<T> should be an interface");
    }

    service.scheduleWithFixedDelay(ExceptionUtil.catchAll("VMWare server cleaup clients", new Runnable() {
      public void run() {
        try {
          final long now = new Date().getTime();
          final List<String> toRemove = new ArrayList<String>();
          for (RemoteClient<T> client : myClients.values()) {
            if (!client.hasPendingTasks() && client.getLastCommunicationTimestamp().getTime() + TIMEOUT * 1000 < now) {
              LOG.debug("Client " + client.getId() + " is removed on timeout.");
              toRemove.add(client.getId());
              client.dispose();
            }
          }

          for (String id : toRemove) {
            myClients.remove(id);
          }
        } catch (Throwable t) {
          LOG.warn("Failed to cleanup alive clients list");
        }
      }
    }), TIMEOUT, TIMEOUT, TimeUnit.SECONDS);
  }

  @NotNull
  private RemoteClient<T> findClient(String clientInfo) {
    RemoteClient<T> cli = new RemoteClient<T>(clientInfo, myInterface);
    final RemoteClient<T> ret = myClients.putIfAbsent(clientInfo, cli);
    return ret == null ? cli : ret;
  }

  @NotNull
  public Collection<RemoteTask> getTasks(@NotNull final String clientInfo) {
    final RemoteClient<T> cli = findClient(clientInfo);
    return cli.getPendingTasks();
  }

  public void postResults(@NotNull final String clientInfo, @NotNull final Collection<RemoteTaskResult> result) {
    final RemoteClient cli = findClient(clientInfo);
    cli.acceptTasks(result);
  }

  public Collection<T> getAliveClients() {
    List<T> list = new ArrayList<T>();
    for (RemoteClient<T> client : myClients.values()) {
      if (client.isAlive()) {
        list.add(client.proxy());
      }
    }
    return list;
  }
}
