/*
 * Created on May 3, 2005
 */
package org.flexdock.docking.state;

import java.awt.Rectangle;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;

import org.flexdock.docking.Dockable;
import org.flexdock.docking.floating.frames.DockingFrame;
import org.flexdock.util.UUID;

/**
 * This class models a grouping of <code>Dockables</code> within a floating <code>DockingFrame</code>.
 * The <code>FloatingGroup</code> tracks the current screen bounds of the <code>DockingFrame</code> and
 * the ID for each <code>Dockable</code> within the group.  The <code>FloatingGroup</code>
 * may be persisted to external storage and recreated across JVM sessions.  This allows the 
 * <code>DockingFrame</code> to be recreated and displayed with the previous screen bounds and all
 * of the <code>Dockables</code> contained within the group to be restored to their previous state
 * within the <code>DockingFrame</code>.
 * <br/>
 * In addition to providing persistent state across application sessions, the <code>FloatingGroup</code>
 * allows for a floating <code>Dockable</code> to be closed and then later restored to its original 
 * floating state within the same JVM session.  The currently installed <code>FloatManager</code> may 
 * use <code>FloatingGroups</code> to determine which visible <code>DockingFrame</code> into which to
 * restore a closed <code>Dockable</code>, or to create and display a new <code>DockingFrame</code>
 * to the same end on an as-needed basis.  
 * 
 * @author Christopher Butler
 */
public class FloatingGroup implements Cloneable, Serializable {
    
    private String name;
	private Rectangle windowBounds;
	private transient DockingFrame frame;
	private HashSet dockables; // contains String dockableIds
	
	/**
	 * Creates a new <code>FloatingGroup</code> with the specified <code>groupName</code>.  This 
	 * group may be looked up from the currently installed <code>FloatManager</code> using this
	 * <code>groupName</code>.  <code>groupName</code> should be unique to this group.
	 * 
	 * @param groupName the unique identifier for this <code>FloatingGroup</code>
	 * @see FloatManager#getGroup(String)
	 */
	public FloatingGroup(String groupName) {
	    name = groupName==null? UUID.randomUUID().toString():groupName;
		dockables = new HashSet();
	}
	
	private FloatingGroup(String groupName, HashSet dockableSet) {
		name = groupName;
		dockables = dockableSet;
	}
	
	/**
	 * Returns the cached screen bounds of the <code>DockingFrame</code> associated with this 
	 * <code>FloatingGroup</code>.  If no screen bounds have been previously cached, this method
	 * returns <code>null</code>.  Otherwise, this method returns a clone of the cached 
	 * <code>Rectangle</code> so that its fields may not be directly modified.
	 *
	 * @return the cached screen bounds of the <code>DockingFrame</code> associated with this 
	 * <code>FloatingGroup</code>.
	 * @see #setBounds(Rectangle)
	 */
	public Rectangle getBounds() {
		return windowBounds==null? null: (Rectangle)windowBounds.clone();
	}
	
	/**
	 * Sets the screen bounds representing the <code>DockingFrame</code> associated with this 
	 * <code>FloatingGroup</code>.  If <code>rect</code> is <code>null</code>, then the cached
	 * screen bounds are set to <code>null</code>.  Otherwise, the field values are copied from the
	 * specified <code>Rectangle</code> into the cached screen bounds rather than updating the 
	 * internal object reference.  This is done to prevent the cached screen bounds' fields from
	 * subsequently being modified directly.
	 * 
	 * @param rect the new screen bounds representing the <code>DockingFrame</code> associated with this 
	 * <code>FloatingGroup</code>.
	 * @see #getBounds()
	 */
	public void setBounds(Rectangle rect) {
		if(rect==null) {
			windowBounds = null;
		}
		else {
			if(windowBounds==null)
				windowBounds = (Rectangle)rect.clone();
			else
				windowBounds.setBounds(rect);
		}
	}
	
	/**
	 * Returns the name of this <code>FloatingGroup</code>.  This value may be used as a key to lookup
	 * this <code>FloatingGroup</code> from the currently installed <code>FloatManager</code> by
	 * invoking its <code>getGroup(String groupName)</code> method.
	 * 
	 * @return the name of this <code>FloatingGroup</code>.
	 * @see #FloatingGroup(String)
	 * @see FloatManager#getGroup(String)
	 */
	public String getName() {
		return name;
	}

	/**
	 * Returns a reference to the <code>DockingFrame</code> associated with this <code>FloatingGroup</code>.
	 * This method allows easy lookup to establish an association with a <code>Dockable</code> and
	 * a <code>DockingFrame</code>.  When attempting to find an existing <code>DockingFrame</code> into which
	 * to restore a hidden <code>Dockable</code>, the <code>Dockable's</code> <code>FloatingGroup</code>
	 * may be looked up by the currently installed <code>FloatManager</code> by calling its 
	 * <code>getGroup(Dockable dockable)</code> method.  Once the group has been resolved, the actual
	 * <code>DockingFrame</code> reference may be obtained by this method and the <code>Dockable</code>
	 * may be restored to the screen.  Or, this method may return <code>null</code> and the 
	 * <code>DockingFrame</code> will have to be recreated before the <code>Dockable</code> can be restored.
	 * 
	 * @return a reference to the <code>DockingFrame</code> associated with this <code>FloatingGroup</code>.
	 * @see #setFrame(DockingFrame)
	 * @see FloatManager#getGroup(Dockable)
	 */
	public DockingFrame getFrame() {
		return frame;
	}
	
	/**
	 * Sets a reference to the <code>DockingFrame</code> associated with this <code>FloatingGroup</code>.
	 * This method allows help enable easy lookup to establish an association with a <code>Dockable</code> and
	 * a <code>DockingFrame</code>.  When attempting to find an existing <code>DockingFrame</code> into which
	 * to restore a hidden <code>Dockable</code>, the <code>Dockable's</code> <code>FloatingGroup</code>
	 * may be looked up by the currently installed <code>FloatManager</code> by calling its 
	 * <code>getGroup(Dockable dockable)</code> method.  Once the group has been resolved, the actual
	 * <code>DockingFrame</code> reference may be obtained by calling <code>getFrame()</code> and the 
	 * <code>Dockable</code> may be restored to the screen.  Or, <code>getFrame()</code>may return 
	 * <code>null</code> and the <code>DockingFrame</code> will have to be recreated before the 
	 * <code>Dockable</code> can be restored.  This method establishes the association between 
	 * <code>FloatingGroup</code> and <code>DockingFrame</code>.
	 * 
	 * @param frame the <code>DockingFrame</code> to be associated with this <code>FloatingGroup</code>.
	 * @see #getFrame()
	 * @see FloatManager#getGroup(Dockable)
	 */
	public void setFrame(DockingFrame frame) {
		this.frame = frame;
	}
	
	public void addDockable(String dockableId) {
		dockables.add(dockableId);
	}
	
    public Iterator getDockableIterator() {
        return this.dockables.iterator();
    }
    
	public void removeDockable(String dockableId) {
		dockables.remove(dockableId);
	}
	
	public int getDockableCount() {
		return dockables.size();
	}
	
	public void destroy() {
		dockables.clear();
		setFrame(null);
		setBounds(null);
	}
	
	public Object clone() {
		HashSet set = (HashSet)dockables.clone();
		FloatingGroup clone = new FloatingGroup(name, set);
		clone.frame = frame;
		clone.windowBounds = (Rectangle)windowBounds.clone();
		return clone;
	}
	
}
