/*
 * ServerQueue.java
 *
 * Created on May 11, 2003, 9:32 PM
 */

package hep.aida.ref.remote;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.*;

import hep.aida.ref.remote.interfaces.AidaTreeClient;
import hep.aida.ref.remote.interfaces.AidaUpdateEvent;

/**
 * This is thread-safe queue for UpdateEvents.
 * Events can be accumulated in the queue and then retrieved
 * in one chunk. Mainly used on the server side.
 * If client != null new thread will be started with this queue
 * to send updates to the client.
 *
 * @author  serbo
 */
public class RemoteServerQueue implements Runnable {
    
    protected AidaTreeClient client;
    protected List queue;
    protected List holdQueue;
    protected boolean keepRunning;
    protected boolean hold;
    protected Logger remoteLogger;
    
    /** Creates a new instance of UpdatableQueue */
    public RemoteServerQueue() {
        this(null);
    }
    
    public RemoteServerQueue(AidaTreeClient client) {
        queue = new ArrayList(100);
        holdQueue = new ArrayList(100);
        keepRunning = true;
        this.client = client;
        this.hold = false;
        remoteLogger = Logger.getLogger("hep.aida.ref.remote");
        remoteLogger.fine("RemoteServerQueue: START");
        if (client != null) new Thread(this).start();
    }
    
    /**
     * Once the RemoteServerQueue is closed, it can not be restarted again.
     */
    public synchronized void close() { 
        keepRunning = false;
        queue.clear();
        holdQueue.clear();
        this.notify();
    }
    
    /*
     * If hold set to "true", do not shedule events, just accumulate till
     * hold is set to "false"
     */
    public void setHold(boolean h) {
        if (h) {
            this.hold = h;
        } else {
            synchronized ( this ) {
                this.hold = h;
                queue.addAll(holdQueue);
                holdQueue.clear();
                this.notify();
            }
        }
    }
    
    public int size() { return (queue == null) ? 0 : queue.size(); }
    
    /**
     * Add events to the queue.
     */
    public void schedule(AidaUpdateEvent event) {
        remoteLogger.finest("RemoteServerQueue.schedule id="+event.id()+", path="+event.path()+", type="+event.nodeType());
        if (keepRunning) addToQueue(event);
    }
    
    /**
     * Returns the array of events that are currently in the queue.
     * Also deletes those events from the queue. Never returns null.
     */
    public AidaUpdateEvent[] getEvents() {
        AidaUpdateEvent[] events = new AidaUpdateEvent[0];
        int size = size();
        if (size == 0) {
            return events;
        }
        synchronized ( this ) {
            size = queue.size();
            events = new AidaUpdateEvent[size];
            queue.toArray(events);
	    queue.clear();
        }
        return events;
    }
    
    /**
     * This method adds event to the queue and can do some cleanup, like
     * remove multiple update events for the same path (not implemented yet), etc.
     */
    protected void addToQueue(AidaUpdateEvent event) {
        synchronized ( this ) {            
            if (hold) holdQueue.add(event);
            else {
                queue.add(event);            
                this.notify();
            }
        }
    }
    
    
    
    // Runnable methods
    
    /**
     * In Duplex mode sends updates to AidaTreeClient
     */    
    public void run() {
        int size = 0;
        AidaUpdateEvent[] events = null;
        while (keepRunning) {
            try {
                synchronized (this) {
		    if(queue.size() == 0) this.wait();
                    if (queue == null) return;
                    size = queue.size();
                    if (size > 0) {
                        events = getEvents();
                    }
                }
                remoteLogger.finest("RemoteServerQueue.run Processing: "+size);
                if (events != null || events.length > 0) {
                    if (client != null) client.stateChanged(events);
                }
            } catch (InterruptedException e2) {
                remoteLogger.log(Level.INFO, "RemoteServerOueue InterruptedException.", e2);
                remoteLogger.log(Level.FINE, "", e2.getStackTrace());
	    } catch (Exception e3) {
                remoteLogger.log(Level.INFO, "Exception in RemoteServerQueue: ", e3);
                remoteLogger.log(Level.FINE, "", e3.getStackTrace());
            } // end of try/catch
        } // end of while
    } //end of run  
    
}
