/*
 * RemoteServer.java
 *
 * Created on October 22, 2003, 9:01 PM
 */

package hep.aida.ref.remote;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.*;

import hep.aida.dev.IDevTree;

import hep.aida.ref.remote.interfaces.AidaTreeClient;
import hep.aida.ref.remote.interfaces.AidaTreeServant;
import hep.aida.ref.remote.interfaces.AidaTreeServer;

/**
 * This is implementation of the AidaTreeServer.
 * This class does not have any remote transport layer (RMI, CORBA, etc.),
 * so special adapter classes, like RmiRemoteServer, are used to provide
 * such functionality.
 * Default is to support duplex mode.
 *
 * @author  serbo
 */

public class RemoteServer implements AidaTreeServer {
    
    private IDevTree tree;
    private String treeName;
    private boolean duplex;
    private boolean appendAxisType;
    private Map servantHash;
    private boolean acceptNewConnections;
    protected Logger remoteLogger;
    
    /** Creates a new instance of RemoteServer */
    public RemoteServer(IDevTree tree) {
        this(tree, true);
    }
    
    public RemoteServer(IDevTree tree, boolean duplex) {
        this(tree, duplex, false);
    }
    public RemoteServer(IDevTree tree, boolean duple, boolean appendAxisType) {
        this.tree = tree;
        this.duplex = duplex;
        this.appendAxisType = appendAxisType;
        treeName = tree.storeName();
        servantHash = new Hashtable();
        acceptNewConnections = true;
        remoteLogger = Logger.getLogger("hep.aida.ref.remote");
    }
    
    
    // Service methods
    
    private AidaTreeServant connect(java.lang.Object clientRef) {
        remoteLogger.info("New connection from Client:  "+clientRef+", acceptNewConnections="+acceptNewConnections);
        if (clientRef == null) {
            throw new RemoteConnectionException("Can not connect with NULL Client Reference.");
        } 
        AidaTreeServant servant = null;
        if (!acceptNewConnections) return servant;
        synchronized ( servantHash ) {
            servant = (AidaTreeServant) servantHash.get(clientRef);
            if (servant != null) {
                throw new RemoteConnectionException("This client is already connected. Please disconnect first.\nClient: "+clientRef.toString());
            } else {
                if (clientRef instanceof String) servant = new RemoteServant(tree, (String) clientRef);
                else if (clientRef instanceof AidaTreeClient) 
                    servant = new RemoteServant(tree, (AidaTreeClient) clientRef);
                ((RemoteServant) servant).setAppendAxisType(appendAxisType);
                servantHash.put(clientRef, servant);
            }
        }
        return servant;
    }

    /**
     * Disconnect servant for a particular client.
     * RemoteServer remains functional after that
     */
    private boolean disconnect (java.lang.Object clientRef) {
        remoteLogger.info("\tDisconnecting Client: "+clientRef+", acceptNewConnections="+acceptNewConnections);
        AidaTreeServant servant = (AidaTreeServant) servantHash.get(clientRef);
        if (servant != null) {
            if (servant instanceof RemoteServant) ((RemoteServant) servant).close();
            servantHash.remove(clientRef);
            return true;
        }
        return false;
    }

    /**
     * Disconnect servants for all client.
     * RemoteServer remains functional after that
     */
    private void disconnectAll() {
        synchronized ( servantHash ) {
            if (!servantHash.isEmpty()) {
                Set keySet = servantHash.keySet();
                int size = keySet.size();
                Object[] a = new Object[size];
                keySet.toArray(a);
                for (int i=0; i<size; i++) {
                    Object clientRef = a[i];
                    disconnect(clientRef);
                }
                servantHash.clear();
            }
        }
    }
    
    /**
     * Disconnect servants for all client, server and release all
     * allocated resources. RemoteServer does not function after that
     */
    public void close() {
        acceptNewConnections = false;
        disconnectAll();
        servantHash.clear();
        servantHash = null;
        tree = null;
    }
    
    
    // AidaTreeServer methods
    
    public AidaTreeServant connectDuplex(AidaTreeClient client) {
        return connect(client);
    }
    
    public AidaTreeServant connectNonDuplex(String clientID) {
        return connect(clientID);
    }
    
    public boolean disconnectDuplex(AidaTreeClient client) {
        return disconnect(client);
    }
    
    public boolean disconnectNonDuplex(String clientID) {
        return disconnect(clientID);
    }
    
    public boolean supportDuplexMode() { return duplex; }
    
    public String treeName() { return treeName; }
    
}
