/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.cloud.topology;

import java.util.concurrent.CopyOnWriteArrayList;

import com.caucho.util.L10N;

/**
 * CloudDomain represents all the clusters in a Resin configuration.
 */
public class CloudSystem
{
  private static final L10N L = new L10N(CloudSystem.class);
  
  private CloudCluster []_clusterArray = new CloudCluster[0];

  private final String _id;
  
  private final CopyOnWriteArrayList<CloudClusterListener> _listeners
    = new CopyOnWriteArrayList<CloudClusterListener>();

  /**
   * Creates a CloudDomain with an identifying id.
   */
  public CloudSystem(String id)
  {
    _id = id;
  }

  /**
   * Returns the server id.
   */
  public String getId()
  {
    return _id;
  }

  /**
   * Return the clusters in the cloud.
   */
  public CloudCluster []getClusterList()
  {
    return _clusterArray;
  }

  /**
   * Finds the server with the unique id.
   */
  public CloudCluster findCluster(String id)
  {
    for (CloudCluster cluster : getClusterList()) {
      if (id.equals(cluster.getId()))
        return cluster;
    }

    return null;
  }

  /**
   * Finds the server with the unique id.
   */
  public CloudServer findServer(String id)
  {
    for (CloudCluster cluster : getClusterList()) {
      CloudServer server = cluster.findServer(id);
      
      if (server != null)
        return server;
    }

    return null;
  }

  /**
   * Finds the server with the unique id.
   */
  public CloudServer findServer(String clusterId,
                                int podIndex,
                                int serverIndex)
  {
    CloudCluster cluster = findCluster(clusterId);
    
    if (cluster == null)
      return null;
    
    CloudPod pod = cluster.findPod(podIndex);
    
    if (pod == null)
      return null;
    
    return pod.findServer(serverIndex);
  }

  /**
   * Finds the first server with the given address and port
   */
  public CloudServer findServer(String address, int port)
  {
    for (CloudCluster cluster : getClusterList()) {
      CloudServer server = cluster.findServer(address, port);
      
      if (server != null)
        return server;
    }

    return null;
  }
  
  /**
   * Creates a new cluster.
   */
  public CloudCluster createCluster(String id)
  {
    CloudCluster cluster;
    
    synchronized (this) {
      cluster = findCluster(id);
      
      if (cluster != null) {
        throw new IllegalStateException(L.l("'{0}' is a duplicate cluster id",
                                            id));
      }
      
      
      cluster = new CloudCluster(this, id);
      
      CloudCluster []clusterArray = new CloudCluster[_clusterArray.length + 1];
      System.arraycopy(_clusterArray, 0, clusterArray, 0, _clusterArray.length);
      
      clusterArray[_clusterArray.length] = cluster;
      _clusterArray = clusterArray;
    }
    
    cluster.init();
    
    for (CloudClusterListener listener : _listeners) {
      listener.onClusterAdd(cluster);
    }
    
    return cluster;
  }
  
  /**
   * Creates a new cluster.
   */
  public boolean removeCluster(String id)
  {
    CloudCluster cluster = null;
    
    synchronized (this) {
      int index;
      for (index = _clusterArray.length - 1; index >= 0; index--) {
        if (_clusterArray[index].getId().equals(id)) {
          cluster = _clusterArray[index];
          break;
        }
      }
      
      if (cluster == null)
        return false;
      
      if (cluster.getPodList().length != 0) {
        throw new IllegalStateException(L.l("{0} may not be removed because it has a non-empty pod list",
                                            cluster));
        
      }
      
      CloudCluster []clusterArray = new CloudCluster[_clusterArray.length - 1];
      System.arraycopy(_clusterArray, 0, clusterArray, 0, index);
      
      if (index < clusterArray.length) {
        System.arraycopy(_clusterArray, index + 1, clusterArray, index, 
                         _clusterArray.length - index - 1);
      }
      
      _clusterArray = clusterArray;
    }
    
    for (CloudClusterListener listener : _listeners) {
      listener.onClusterRemove(cluster);
    }
    
    cluster.destroy();
    
    return true;
  }
  
  //
  // listeners
  //
  
  /**
   * Adds a listener to detect server add and removed.
   */
  public void addClusterListener(CloudClusterListener listener)
  {
    if (listener == null)
      throw new NullPointerException();
    
    if (! _listeners.contains(listener))
      _listeners.add(listener);
    
    for (CloudCluster cluster : getClusterList()) {
      listener.onClusterAdd(cluster);
    }
  }
  
  /**
   * Removes a listener to detect server add and removed.
   */
  public void removeClusterListener(CloudClusterListener listener)
  {
    _listeners.remove(listener);
  }

  //
  // lifecycle
  //
  
  public void start()
  {
    for (CloudCluster cluster : getClusterList()) {
      cluster.start();
    }
  }
  

  @Override
  public String toString()
  {
    return (getClass().getSimpleName() + "[" + getId() + "]");
  }
}
