/*
 * Kurento Commons MSControl: Simplified Media Control API for the Java Platform based on jsr309
 * Copyright (C) 2011  Tikal Technologies
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.kurento.mscontrol.commons;

import java.util.Collection;

import com.kurento.mediaspec.Direction;

/**
 * A Joinable is suitable for media composition.<br>
 * A Joinable object is either a NetworkConnection or a MediaComponent.<br>
 * Joinables can be joined/unjoined, to make the media stream flow between them.
 * Join operations can be limited both to a stream (i.e audio,video, see
 * {@link MediaType}) and direction (i.e. SEND, RECV, DUPLEX, see
 * {@link Direction}).<br>
 * The join direction can be changed by calling join again, with a different
 * <code>Direction</code>.
 */
public abstract class Joinable {

	Joinable() {

	}

	public abstract Collection<MediaSrc> getMediaSrcs();

	public abstract Collection<MediaSink> getMediaSinks();

	/**
	 * Establish a media stream between this object and <code>other</code>.
	 * <p>
	 * 
	 * This method does not return error if at least one connection of any
	 * MediaType and/or Direction can be established. <br/>
	 * This method is offered for simplify applications, but the control of the
	 * links is lost by the application because no notification of what links
	 * succeed or failed is done.
	 * 
	 * <h3><b>Joining an object to multiple other objects</b></h3>
	 * <p>
	 * The application is allowed to join objectA to objectB, objectC, etc. The
	 * resulting relationship is:
	 * <ul>
	 * <li>objectA will send data to all others (broadcast to objectB, objectC,
	 * etc.)
	 * <li>objectA will only receive from the last joined object
	 * </ul>
	 * 
	 * For example:<br>
	 * <code>ObjectA.join(ObjectB)</code><br>
	 * <code>ObjectA.join(ObjectC)</code><br>
	 * <code>ObjectA.join(ObjectD)</code><br>
	 * ObjectA sends the same stream(s) to ObjectB, ObjectC and ObjectD.<br>
	 * ObjectA only receives from ObjectD.
	 * <p>
	 * 
	 * @param other
	 *            Joinable object to connect
	 * @throws MediaSessionException
	 * @throws java.lang.IllegalStateException
	 *             if the object has been released
	 */
	public void join(Joinable other) throws MediaSessionException {
		boolean joined = false;

		Collection<MediaSrc> srcs = getMediaSrcs();
		Collection<MediaSink> sinks = other.getMediaSinks();

		for (MediaSrc src : srcs) {
			for (MediaSink sink : sinks) {
				src.connect(sink);
				joined = true;
			}
		}

		srcs = other.getMediaSrcs();
		sinks = getMediaSinks();

		for (MediaSrc src : srcs) {
			for (MediaSink sink : sinks) {
				src.connect(sink);
				joined = true;
			}
		}

		if (!joined) {
			throw new MediaSessionException(
					"Unable to stablish at least one connection");
		}
	}

	/**
	 * Disconnect any media streams flowing between this object and other's.
	 * <p>
	 * 
	 * <b>Note:</b> Changing the direction (e.g. changing from DUPLEX to RECV),
	 * is obtained by calling join again, with the desired direction.
	 * 
	 * @param other
	 *            Joinable object to disconnect
	 * @throws MediaSessionException
	 * @throws java.lang.IllegalStateException
	 *             if the object has been released
	 */
	public void unjoin(Joinable other) throws MediaSessionException {
		other.unjoinRecv(other);
		unjoinRecv(this);
	}

	private void unjoinRecv(Joinable other) throws MediaSessionException {
		Collection<MediaSrc> srcs = other.getMediaSrcs();
		Collection<MediaSink> sinks = getMediaSinks();

		for (MediaSink sink : sinks) {
			MediaSrc src = sink.getConnectedSrc();
			if (src != null && srcs.contains(src)) {
				src.disconnect(sink);
			}
		}
	}

}

@@ -2801,252 +2801,317 @@
 ined
- = false;%0A%0A%09%09Collection%3CMediaSrc%3E srcs = getMediaSrcs(
+;%0A%0A%09%09joined = joinSend(other
 );%0A
+%0A
 %09%09
-Collection%3CMediaSink%3E sinks = other.getMediaSinks();%0A%0A%09%09for (MediaSrc src : srcs) %7B%0A%09%09%09for (MediaSink sink : sinks) %7B%0A%09%09%09%09src.connect(sink);%0A%09%09%09%09joined = true;%0A%09%09%09%7D%0A%09%09%7D%0A%0A%09%09
+joined %7C= other.joinSend(this);%0A%0A%09%09if (!joined) %7B%0A%09%09%09throw new MediaSessionException(%0A%09%09%09%09%09%22Unable to stablish at least one connection%22);%0A%09%09%7D%0A%09%7D%0A%0A%09private boolean joinSend(Joinable other) throws MediaSessionException %7B%0A%09%09boolean joined = false;%0A%09%09Collection%3CMediaSrc%3E 
 srcs = 
-other.
 getM
@@ -3124,24 +3124,46 @@
 cs();%0A%09%09
+Collection%3CMediaSink%3E 
 sinks = 
 getMedia
@@ -3146,32 +3146,38 @@
 iaSink%3E sinks = 
+other.
 getMediaSinks();
@@ -3238,24 +3238,83 @@
  : sinks) %7B%0A
+%09%09%09%09if (src.getMediaType().equals(sink.getMediaType())) %7B%0A%09
 %09%09%09%09src.conn
@@ -3328,16 +3328,17 @@
 k);%0A%09%09%09%09
+%09
 joined =
@@ -3351,123 +3351,37 @@
 %0A%09%09%09
+%09
 %7D%0A
+%09
 %09%09%7D%0A
-%0A
 %09%09
-if (!joined) %7B%0A%09%09%09throw new MediaSessionException(%0A%09%09%09%09%09%22Unable to stablish at least one connection%22);%0A%09%09%7D
+%7D%0A%0A%09%09return joined;
 %0A%09%7D%0A

Improve implementation of join