/**
 * $Id: mxSharedState.java,v 1.1 2012/11/15 13:26:50 gaudenz Exp $
 * Copyright (c) 2007-2012, JGraph Ltd
 */
package com.mxgraph.sharing;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.w3c.dom.Node;

import com.mxgraph.util.mxEventSource;
import com.mxgraph.util.mxXmlUtils;

/**
 * Implements a diagram that may be shared among multiple sessions. This
 * implementation is based only on string, it does not have a model instance.
 * The diagram is represented by its initial state and the sequence of edits
 * as applied to the diagram.
 */
public class mxSharedState extends mxEventSource
{

	/**
	 * Defines the requirements for an object that listens to changes on the
	 * shared diagram.
	 */
	public interface mxDiagramChangeListener
	{

		/**
		 * Fires when the shared diagram was changed.
		 * 
		 * @param sender Session where the change was received from.
		 * @param edits String that represents the edits.
		 */
		void diagramChanged(Object sender, String edits);
	}

	/**
	 * Holds a list of diagram change listeners.
	 */
	protected List<mxDiagramChangeListener> diagramChangeListeners;

	/**
	 * Holds the initial state of the diagram.
	 */
	protected String state;

	/**
	 * Holds the history of all changes of initial state.
	 */
	protected StringBuffer delta = new StringBuffer();

	/**
	 * Constructs a new diagram with the given state.
	 * 
	 * @param state Initial state of the diagram.
	 */
	public mxSharedState(String state)
	{
		this.state = state;
	}

	/**
	 * Returns the initial state of the diagram.
	 */
	public String getState()
	{
		return state;
	}

	/**
	 * Returns the history of all changes as a string.
	 */
	public synchronized String getDelta()
	{
		return delta.toString();
	}

	/**
	 * Appends the given string to the history and dispatches the change to all
	 * sessions that are listening to this shared diagram.
	 * 
	 * @param sender Session where the change originated from.
	 * @param delta XML that represents the change.
	 */
	public void processDelta(Object sender, Node delta)
	{
		StringBuffer edits = new StringBuffer();

		synchronized (this)
		{
			Node edit = delta.getFirstChild();

			while (edit != null)
			{
				if (edit.getNodeName().equals("edit"))
				{
					edits.append(processEdit(edit));
				}

				edit = edit.getNextSibling();
			}
		}

		String xml = edits.toString();
		addDelta(xml);
		dispatchDiagramChangeEvent(sender, xml);
	}

	/**
	 * 
	 */
	protected String processEdit(Node node)
	{
		return mxXmlUtils.getXml(node);
	}

	/**
	 * 
	 */
	public synchronized void addDelta(String xml)
	{
		// TODO: Clear delta if xml contains mxRootChange
		delta.append(xml);
	}

	/**
	 * Clears the history of all changes.
	 */
	public synchronized void resetDelta()
	{
		delta = new StringBuffer();
	}

	/**
	 * Adds the given listener to the list of diagram change listeners.
	 * 
	 * @param listener Diagram change listener to be added.
	 */
	public void addDiagramChangeListener(mxDiagramChangeListener listener)
	{
		if (diagramChangeListeners == null)
		{
			diagramChangeListeners = new ArrayList<mxDiagramChangeListener>();
		}

		diagramChangeListeners.add(listener);
	}

	/**
	 * Removes the given listener from the list of diagram change listeners.
	 * 
	 * @param listener Diagram change listener to be removed.
	 */
	public void removeDiagramChangeListener(mxDiagramChangeListener listener)
	{
		if (diagramChangeListeners != null)
		{
			diagramChangeListeners.remove(listener);
		}
	}

	/**
	 * Dispatches the given event information to all diagram change listeners.
	 * 
	 * @param sender Session where the change was received from.
	 * @param xml XML string that represents the change.
	 */
	void dispatchDiagramChangeEvent(Object sender, String edits)
	{
		if (diagramChangeListeners != null)
		{
			Iterator<mxDiagramChangeListener> it = diagramChangeListeners
					.iterator();

			while (it.hasNext())
			{
				mxDiagramChangeListener listener = it.next();
				listener.diagramChanged(sender, edits);
			}
		}
	}

}
