/* Audio.java

	Purpose:
		
	Description:
		
	History:
		Wed Nov 16 11:48:27     2005, Created by tomyeh

Copyright (C) 2005 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zul;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.zkoss.util.media.Media;
import org.zkoss.zk.au.out.AuInvoke;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.ext.render.DynamicMedia;
import org.zkoss.zul.impl.Utils;
import org.zkoss.zul.impl.XulElement;

/**
 * An audio clip.
 *
 * <p>An extension to XUL.
 * Only works for browsers supporting HTML5 audio tag (since ZK 7.0.0).
 *
 * @author tomyeh
 */
public class Audio extends XulElement {
	protected List<String> _src = new ArrayList<String>();
	/** The audio. _src and _audio cannot be nonnull at the same time. */
	private org.zkoss.sound.Audio _audio;
	/** Count the version of {@link #_audio}. */
	private byte _audver;
	private boolean _autoplay, _controls, _loop, _muted;
	private String _preload;

	public Audio() {
	}

	public Audio(String src) {
		setSrc(src);
	}

	/** Plays the audio at the client.
	 */
	public void play() {
		response("ctrl", new AuInvoke(this, "play"));
	}

	/** Stops the audio at the client.
	 */
	public void stop() {
		response("ctrl", new AuInvoke(this, "stop"));
	}

	/** Pauses the audio at the client.
	 */
	public void pause() {
		response("ctrl", new AuInvoke(this, "pause"));
	}

	/** Returns the src.
	 * <p>Default: [].
	 */
	public List<String> getSrc() {
		return _src;
	}

	/** Sets the src.
	 *
	 * <p>Calling this method implies setContent(null).
	 * In other words, the last invocation of {@link #setSrc} overrides
	 * the previous {@link #setContent}, if any.
	 * @see #setContent
	 */
	public void setSrc(String src) {
		List<String> list = new ArrayList<String>();
		if (src.contains(",")) {
			list = new ArrayList<String>(Arrays.asList(src.split("\\s*,\\s*")));
		} else {
			list.add(src.trim());
		}
		if (_audio != null || !_src.equals(list)) {
			_audio = null;
			setSrc(list);
		}

	}

	/** Sets the source list.
	 * 
	 * @since 7.0.0
	 */
	public void setSrc(List<String> src) {
		if (!src.equals(_src)) {
			_src = src;
			smartUpdate("src", new EncodedSrc());
		}
	}

	/** Returns whether to auto start playing the audio.
	 *
	 * <p>Default: false;
	 * @deprecated As of release 7.0.0, use {@link #isAutoplay} instead.
	 */
	public boolean isAutostart() {
		return isAutoplay();
	}

	/** Sets whether to auto start playing the audio.
	 * 
	 * @deprecated As of release 7.0.0, use {@link #setAutoplay} instead.
	 */
	public void setAutostart(boolean autostart) {
		setAutoplay(autostart);
	}

	/** Returns whether to auto start playing the audio.
	 *
	 * <p>Default: false.
	 * @since 7.0.0
	 */
	public boolean isAutoplay() {
		return _autoplay;
	}

	/** Sets whether to auto start playing the audio.
	 * 
	 * @since 7.0.0
	 */
	public void setAutoplay(boolean autoplay) {
		if (_autoplay != autoplay) {
			_autoplay = autoplay;
			smartUpdate("autoplay", _autoplay);
		}
	}

	/** Returns whether and how the audio should be loaded.
	 * "none" or "metadata" or "auto" or null
	 * <p>Default: null.
	 * @since 7.0.0
	 */
	public String getPreload() {
		return _preload;
	}

	/** Sets whether and how the audio should be loaded.
	 * Refer to <a href="http://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload">Preload Attribute Description</a> for details.
	 * @param preload which could be one of "none", "metadata", "auto".
	 * @since 7.0.0
	 */
	public void setPreload(String preload) {
		if ("none".equalsIgnoreCase(preload)) {
			preload = "none";
		} else if ("metadata".equalsIgnoreCase(preload)) {
			preload = "metadata";
		} else {
			preload = "auto";
		}
		if (!preload.equals(_preload)) {
			_preload = preload;
			smartUpdate("preload", _preload);
		}
	}

	/** Returns whether to display the audio controls.
	 *
	 * <p>Default: false.
	 * @since 7.0.0
	 */
	public boolean isControls() {
		return _controls;
	}

	/** Sets whether to display the audio controls.
	 * @since 7.0.0
	 */
	public void setControls(boolean controls) {
		if (_controls != controls) {
			_controls = controls;
			smartUpdate("controls", _controls);
		}
	}

	/** Returns whether to play the audio repeatedly.
	 *
	 * <p>Default: false.
	 * @since 3.6.1
	 */
	public boolean isLoop() {
		return _loop;
	}

	/** Sets whether to play the audio repeatedly.
	 * 
	 * @since 3.6.1
	 */
	public void setLoop(boolean loop) {
		if (_loop != loop) {
			_loop = loop;
			smartUpdate("loop", _loop);
		}
	}

	/** Returns whether to mute the audio.
	 *
	 * <p>Default: false.
	 * @since 7.0.0
	 */
	public boolean isMuted() {
		return _muted;
	}

	/** Sets whether to mute the audio.
	 * 
	 * @since 7.0.0
	 */
	public void setMuted(boolean muted) {
		if (_muted != muted) {
			_muted = muted;
			smartUpdate("muted", _muted);
		}
	}

	/** Sets the content directly.
	 * 
	 * <p>Default: null.
	 *
	 * <p>Calling this method implies setSrc(null).
	 * In other words, the last invocation of {@link #setContent} overrides
	 * the previous {@link #setSrc}, if any.
	 *
	 * @param audio the audio to display.
	 * @see #setSrc
	 */
	public void setContent(org.zkoss.sound.Audio audio) {
		if (_src != null || audio != _audio) {
			_audio = audio;
			_src = null;
			if (_audio != null)
				++_audver; //enforce browser to reload
			smartUpdate("src", new EncodedSrc());
		}
	}

	/** Returns the content set by {@link #setContent}.
	 * <p>Note: it won't fetch what is set thru by {@link #setSrc}.
	 * It simply returns what is passed to {@link #setContent}.
	 */
	public org.zkoss.sound.Audio getContent() {
		return _audio;
	}

	private List<String> getEncodedSrc() {
		final Desktop dt = getDesktop();
		List<String> list = new ArrayList<String>();
		if (_audio != null) {
			list.add(getAudioSrc());
		} else if (dt != null) {
			for (String src : _src) {
				list.add(dt.getExecution().encodeURL(src));
			}
		}
		return list;
	}

	/** Returns the encoded URL for the current audio content.
	 * Don't call this method unless _audio is not null;
	 */
	private String getAudioSrc() {
		return Utils.getDynamicMediaURI(this, _audver, _audio.getName(), _audio.getFormat());
	}

	// super
	protected void renderProperties(org.zkoss.zk.ui.sys.ContentRenderer renderer) throws java.io.IOException {
		super.renderProperties(renderer);

		render(renderer, "src", getEncodedSrc());
		render(renderer, "autoplay", _autoplay);
		render(renderer, "preload", _preload);
		render(renderer, "controls", _controls);
		render(renderer, "loop", _loop);
		render(renderer, "muted", _muted);
	}

	/** Default: not childable.
	 */
	protected boolean isChildable() {
		return false;
	}

	// ComponentCtrl
	public Object getExtraCtrl() {
		return new ExtraCtrl();
	}

	/** A utility class to implement {@link #getExtraCtrl}.
	 * It is used only by component developers.
	 */
	protected class ExtraCtrl extends XulElement.ExtraCtrl implements DynamicMedia {
		//-- DynamicMedia --//
		public Media getMedia(String pathInfo) {
			return _audio;
		}
	}

	private class EncodedSrc implements org.zkoss.zk.au.DeferredValue {
		public Object getValue() {
			return getEncodedSrc();
		}
	}
}
