/**
 * Copyright (C) Azureus Software, Inc, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

package com.aelitis.azureus.ui.swt.views.skin;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.*;

import org.gudy.azureus2.core3.disk.DiskManagerFileInfo;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.download.ForceRecheckListener;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils;
import org.gudy.azureus2.ui.swt.TextViewerWindow;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.shells.MessageBoxShell;
import org.gudy.azureus2.ui.swt.views.utils.ManagerUtils;

import com.aelitis.azureus.activities.VuzeActivitiesEntry;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.download.*;
import com.aelitis.azureus.core.torrent.PlatformTorrentUtils;
import com.aelitis.azureus.core.util.LaunchManager;
import com.aelitis.azureus.core.vuzefile.VuzeFile;
import com.aelitis.azureus.core.vuzefile.VuzeFileComponent;
import com.aelitis.azureus.core.vuzefile.VuzeFileHandler;
import com.aelitis.azureus.ui.UserPrompterResultListener;
import com.aelitis.azureus.ui.selectedcontent.DownloadUrlInfo;
import com.aelitis.azureus.ui.selectedcontent.ISelectedContent;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.UIFunctionsSWT;
import com.aelitis.azureus.ui.swt.feature.FeatureManagerUI;
import com.aelitis.azureus.ui.swt.player.PlayerInstallWindow;
import com.aelitis.azureus.ui.swt.player.PlayerInstaller;
import com.aelitis.azureus.ui.swt.skin.SWTSkin;
import com.aelitis.azureus.ui.swt.skin.SWTSkinObjectContainer;
import com.aelitis.azureus.ui.swt.utils.TorrentUIUtilsV3;
import com.aelitis.azureus.util.DLReferals;
import com.aelitis.azureus.util.DataSourceUtils;
import com.aelitis.azureus.util.PlayUtils;

/**
 * @author TuxPaper
 * @created Oct 12, 2006
 *
 */
public class TorrentListViewsUtils
{
	private static StreamManagerDownload	current_stream;
	private static TextViewerWindow			stream_viewer;
	
	public static void playOrStreamDataSource(Object ds,
			boolean launch_already_checked) {
		String referal = DLReferals.DL_REFERAL_UNKNOWN;
		if (ds instanceof VuzeActivitiesEntry) {
			referal = DLReferals.DL_REFERAL_PLAYDASHACTIVITY;
		} else if (ds instanceof DownloadManager) {
			referal = DLReferals.DL_REFERAL_PLAYDM;
		} else if (ds instanceof ISelectedContent) {
			referal = DLReferals.DL_REFERAL_SELCONTENT;
		}
		playOrStreamDataSource(ds, referal, launch_already_checked, true );
	}

	public static void playOrStreamDataSource(Object ds, String referal,
			boolean launch_already_checked, boolean complete_only) {

		DiskManagerFileInfo fileInfo = DataSourceUtils.getFileInfo(ds);
		if (fileInfo != null) {
			playOrStream(fileInfo.getDownloadManager(), fileInfo.getIndex(),
					complete_only, launch_already_checked, referal);
		}else{
		
			DownloadManager dm = DataSourceUtils.getDM(ds);
			if (dm == null) {
				downloadDataSource(ds, true, referal);
			} else {
				playOrStream(dm, -1, complete_only, launch_already_checked, referal);
			}
		}
	}

	public static void downloadDataSource(Object ds, boolean playNow,
			String referal) {
		TOTorrent torrent = DataSourceUtils.getTorrent(ds);
		
		if ( torrent != null ){
				// handle encapsulated vuze file
			try{
				Map torrent_map = torrent.serialiseToMap();
				
				torrent_map.remove( "info" );
				
				VuzeFile vf = VuzeFileHandler.getSingleton().loadVuzeFile( torrent_map );
			
				if ( vf != null ){
					
					VuzeFileHandler.getSingleton().handleFiles( new VuzeFile[]{ vf }, VuzeFileComponent.COMP_TYPE_NONE );
					
					return;
				}
			}catch( Throwable e ){
				
			}
		}
		
		// we want to re-download the torrent if it's ours, since the existing
		// one is likely stale
		if (torrent != null && !DataSourceUtils.isPlatformContent(ds)) {
			TorrentUIUtilsV3.addTorrentToGM(torrent);
		} else {
			DownloadUrlInfo dlInfo = DataSourceUtils.getDownloadInfo(ds);
			if (dlInfo != null) {
				TorrentUIUtilsV3.loadTorrent(dlInfo, playNow, false, true);
				return;
			}

			String hash = DataSourceUtils.getHash(ds);
			if (hash != null) {
				dlInfo = new DownloadUrlInfo(UrlUtils.parseTextForMagnets(hash));
				dlInfo.setReferer(referal);
				TorrentUIUtilsV3.loadTorrent(dlInfo, playNow, false, true);
				return;
			}
		}
	}

	// VuzePlayer (2011) calls this
	public static void playOrStream(final DownloadManager dm,
			final int file_index, final boolean complete_only,
			boolean launch_already_checked) {
		playOrStream(dm, file_index, complete_only, launch_already_checked, null);
	}

	
	private static void playOrStream(final DownloadManager dm,
			final int file_index, final boolean complete_only,
			boolean launch_already_checked, final String referal) {
			
		if (dm == null) {
			return;
		}
		
		if ( launch_already_checked ){
		
			_playOrStream(dm, file_index, complete_only, referal);
			
		}else{
			
			LaunchManager	launch_manager = LaunchManager.getManager();
			
			LaunchManager.LaunchTarget target = launch_manager.createTarget( dm );
				
			launch_manager.launchRequest(
				target,
				new LaunchManager.LaunchAction()
				{
					public void
					actionAllowed()
					{
						Utils.execSWTThread(
							new Runnable()
							{
								public void
								run()
								{
									_playOrStream(dm, file_index, complete_only, referal);
								}
							});
					}
					
					public void
					actionDenied(
						Throwable		reason )
					{
						Debug.out( "Launch request denied", reason );
					}
				});
		}
	}

	private static void _playOrStream(final DownloadManager dm,
			final int file_index, final boolean complete_only, final String referal) {

		if (dm == null) {
			return;
		}

		//		if (!canPlay(dm)) {
		//			return false;
		//		}

		final TOTorrent torrent = dm.getTorrent();
		if (torrent == null) {
			return;
		}
		
		if (file_index == -1) {
			final int[] playableFileIndexes = PlayUtils.getExternallyPlayableFileIndexes(
					PluginCoreUtils.wrap(dm), complete_only);
			if (playableFileIndexes.length == 1) {

				int open_result = openInEMP(dm,file_index,complete_only,referal);
				
				if ( open_result == 0 ){
					PlatformTorrentUtils.setHasBeenOpened(dm, true);
				}
			} else if (playableFileIndexes.length > 1) {
				VuzeMessageBox mb = new VuzeMessageBox(MessageText.getString("ConfigView.option.dm.dblclick.play"), null,
						new String[] {
							MessageText.getString("iconBar.play"),
							MessageText.getString("Button.cancel")
						}, 0);
				final Map<Integer, Integer> mapPositionToFileInfo = new HashMap<Integer, Integer>();
				final int[] selectedIndex = {
					-1
				};

				mb.setSubTitle(MessageText.getString("play.select.content"));
				mb.setListener(new VuzeMessageBoxListener() {

					public void shellReady(Shell shell, SWTSkinObjectContainer soExtra) {
						SWTSkin skin = soExtra.getSkin();

						Composite composite = soExtra.getComposite();
						final Table table = new Table(composite, SWT.V_SCROLL
								| SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.SINGLE);
						table.setBackground(composite.getBackground());
						table.addSelectionListener(new SelectionListener() {

							public void widgetSelected(SelectionEvent e) {
								selectedIndex[0] = table.getSelectionIndex();
							}

							public void widgetDefaultSelected(SelectionEvent e) {
							}
						});
						FormData formData = Utils.getFilledFormData();
						formData.bottom.offset = -20;
						table.setLayoutData(formData);
						table.setHeaderVisible(false);
						table.addListener(SWT.MeasureItem, new Listener() {
							public void handleEvent(Event event) {
								int w = table.getClientArea().width - 5;
								if (w == 0) {
									return;
								}
								if (event.width < w) {
									event.width = w;
								}
							}
						});
						
						String prefix = dm.getSaveLocation().toString();
						int i = 0;
						DiskManagerFileInfo[] fileInfos = dm.getDiskManagerFileInfoSet().getFiles();
						for (int fileIndex : playableFileIndexes) {
							if (fileIndex < 0 || fileIndex >= fileInfos.length) {
								continue;
							}
							File f = fileInfos[fileIndex].getFile(true);
							String path = f.getParent();
							if (path.startsWith(prefix)) {
								path = path.length() > prefix.length() ? path.substring(prefix.length() + 1) : "";
							}
							String s = f.getName();
							if (path.length() > 0) {
								s += " in " + path;
							}
							TableItem item = new TableItem(table, SWT.NONE);
							item.setText(s);
							mapPositionToFileInfo.put(i++, fileIndex);
						}

						Image alphaImage = Utils.createAlphaImage(table.getDisplay(), 1, 25, (byte) 255);
						TableItem item = table.getItem(0);
						item.setImage(alphaImage);
						item.setImage((Image) null);
						alphaImage.dispose();
						

						table.setSelection(0);
					}
				});
				mb.open(new UserPrompterResultListener() {
					public void prompterClosed(int result) {
						if (result != 0 || selectedIndex[0] < 0) {
							return;
						}
						Integer file_index = mapPositionToFileInfo.get(selectedIndex[0]);

						if (file_index != null) {
  						int open_result = openInEMP(dm,file_index,complete_only,referal);
  						
  						if ( open_result == 0 ){
  							PlatformTorrentUtils.setHasBeenOpened(dm, true);
  						}
						}
					}
				});
			}
			return;
		}
		
		if (PlayUtils.canUseEMP(torrent, file_index,complete_only)) {
			debug("Can use EMP");

			int open_result = openInEMP(dm,file_index,complete_only,referal);
			
			if ( open_result == 0 ){
				PlatformTorrentUtils.setHasBeenOpened(dm, true);
				return;
			}else if ( open_result == 2 ){
				debug( "Open in EMP abandoned" );
				return;
			} else {
				debug("Open EMP Failed");
			}
			// fallback to normal
		} else {
			debug("Can't use EMP.");
		}
		
		// We used to pop up a dialog saying we didn't know how to play the file
		// But now the play toolbar and play 'default' action aren't even enabled
		// if we can't use EMP.  So there's no point.
	}


	/**
	 * @param string
	 *
	 * @since 3.0.3.3
	 */
	private static void debug(String string) {
		if (org.gudy.azureus2.core3.util.Constants.isCVSVersion()) {
			System.out.println(string);
		}
	}

	
	private static boolean	emp_installing;
	
	/**
	 * 
	 * @param dm
	 * @return 0=good, 1 = fail, 2 = abandon
	 */

	private static int 
	installEMP(
		String				name,
		final Runnable		target )
	{
		synchronized( TorrentListViewsUtils.class ){
			
			if ( emp_installing ){
				
				Debug.out( "EMP is already being installed, secondary launch for " + name + " ignored" );
				
				return( 2 );
			}
			
			emp_installing = true;
		}
		
		boolean	running = false;
		
		try{
			final PlayerInstaller installer = new PlayerInstaller();
			
			final PlayerInstallWindow window = new PlayerInstallWindow(installer);
			
			window.open();
			
			AEThread2 installerThread = new AEThread2("player installer",true) {
				public void 
				run() 
				{
					try{
						if (installer.install()){
							Utils.execSWTThread(new AERunnable() {
								
								public void runSupport() {
									target.run();
									
								}
							});
						}
					}finally{
						
						synchronized( TorrentListViewsUtils.class ){
							
							emp_installing = false;
						}
					}
				}
			};
			
			installerThread.start();
			
			running = true;
			
			return( 0 );
			
		}catch( Throwable e ){
			
			Debug.out( e );
			
			return( 1 );
			
		}finally{
			
			if ( !running ){
				
				synchronized( TorrentListViewsUtils.class ){
					
					emp_installing = false;
				}
			}
		}
		
	}
	/**
	 * New version accepts map with ASX parameters. If the params are null then is uses the
	 * old version to start the player. If the
	 *
	 *
	 * @param dm - DownloadManager
	 * @return - int: 0 = ok, 1 = fail, 2 = abandon, installation in progress
	 * @since 3.0.4.4 -
	 */
	private static int 
	openInEMP(
		final DownloadManager dm, final int _file_index, 
		final boolean complete_only, final String referal ) 
	{
		try {
			int file_index = -1;
			
			if ( _file_index == -1 ){

				EnhancedDownloadManager edm = DownloadManagerEnhancer.getSingleton().getEnhancedDownload( dm );
				
				if (edm != null) {
					file_index = edm.getPrimaryFileIndex();
				}
				
			}else{
				
				file_index = _file_index;
			}
			
			if ( file_index == -1 ){
				
				return( 1 );
			}
			
			final int f_file_index = file_index;
						
			org.gudy.azureus2.plugins.disk.DiskManagerFileInfo file = PluginCoreUtils.wrap( dm ).getDiskManagerFileInfo()[ file_index ];
			
			final URL url;
			
			if ((! complete_only ) && file.getDownloaded() != file.getLength()){
					
				url = PlayUtils.getMediaServerContentURL( file );
				
			}else{
								
				url = null;
			}
				
			if ( url != null ){
					
				if ( PlayUtils.isStreamPermitted()){
					
					final boolean show_debug_window = false;
					
					new AEThread2( "stream:async" )
					{
						public void
						run()
						{
							StreamManager	sm = StreamManager.getSingleton();
	
							synchronized( TorrentListViewsUtils.class ){
								
								if ( current_stream != null && !current_stream.isCancelled()){
									
									if ( current_stream.getURL().equals( url )){
										
										current_stream.setPreviewMode( !current_stream.getPreviewMode());
										
										return;
									}
									
									current_stream.cancel();
									
									current_stream = null;
								}
				
								if ( show_debug_window && ( stream_viewer == null || stream_viewer.isDisposed())){
									
									Utils.execSWTThread(
										new Runnable()
										{
											public void
											run()
											{
												if ( stream_viewer != null ){
													
													stream_viewer.close();
												}
												
												stream_viewer = new 
													TextViewerWindow( "Stream Status", "Debug information for stream process", "", false );
												
												stream_viewer.addListener(
													new TextViewerWindow.TextViewerWindowListener()
													{
														public void 
														closed() 
														{
															synchronized( TorrentListViewsUtils.class ){
																
																if ( current_stream != null ){
																	
																	current_stream.cancel();
																	
																	current_stream = null;
																}
															}
														}
													});
											}
										});
								}
								
								current_stream = 
									sm.stream( 
										dm, f_file_index, url, false,
										new StreamManagerDownloadListener()
										{
											private long	last_log = 0;
											
											public void
											updateActivity(
												String		str )
											{
												append( "Activity: " + str );
											}
											
											public void
											updateStats(
												int			secs_until_playable,
												int			buffer_secs,
												long		buffer_bytes,
												int			target_secs )
											{
												long	now = SystemTime.getMonotonousTime();
												
												if ( now - last_log >= 1000 ){
												
													last_log = now;
													
													append( "stats: play in " + secs_until_playable + " sec, buffer=" + DisplayFormatters.formatByteCountToKiBEtc( buffer_bytes ) + "/" + buffer_secs + " sec - target=" + target_secs + " sec" );
												}
											}
											
											public void
											ready()
											{
												append( "ready" );
											}
											
											public void
											failed(
												Throwable 	error )
											{
												append( "failed: " + Debug.getNestedExceptionMessage(error));
												
												Debug.out( error );
											}
											
											private void
											append(
												final String	str )
											{
												if ( stream_viewer != null ){
													
													Utils.execSWTThread(
														new Runnable()
														{
															public void
															run()
															{
																if ( stream_viewer != null && !stream_viewer.isDisposed()){
																	
																	stream_viewer.append( str + "\r\n" );
																}
															}
														});
												}
											}
										});
								}
							}
						}.start();
		
				}else{
					
					FeatureManagerUI.openStreamPlusWindow(referal);
					
				}
				
				return( 0 );
						
			}
			
			synchronized( TorrentListViewsUtils.class ){
				
				if ( current_stream != null && !current_stream.isCancelled()){
										
					current_stream.cancel();
					
					current_stream = null;
				}
			}
			
			Class epwClass = null;
			
			try {
				// Assumed we have a core, since we are passed a
				// DownloadManager
				PluginInterface pi = AzureusCoreFactory.getSingleton().getPluginManager().getPluginInterfaceByID(
						"azemp", false );

				if (pi == null) {

					return (installEMP(dm.getDisplayName(), new Runnable(){ public void run(){ openInEMP( dm, f_file_index,complete_only,referal ); }}));
					
				}else if ( !pi.getPluginState().isOperational()){
					
					return( 1 );
				}

				epwClass = pi.getPlugin().getClass().getClassLoader().loadClass(
						"com.azureus.plugins.azemp.ui.swt.emp.EmbeddedPlayerWindowSWT");

			} catch (ClassNotFoundException e1) {
				return 1;
			}
	
										
			try{
				Method method = epwClass.getMethod("openWindow", new Class[] {
						File.class, String.class
					});

				File f = file.getFile( true );
				
				method.invoke(null, new Object[] {
						f, f.getName()
					});
					
				return( 0 );
					
			}catch( Throwable e ){
				debug( "file/name open method missing" );
			}

			
				// fall through here if old emp
			
			Method method = epwClass.getMethod("openWindow", new Class[] {
				DownloadManager.class
			});

			method.invoke(null, new Object[] {
				dm
			});

			return 0;
		} catch (Throwable e) {
			e.printStackTrace();
			if (e.getMessage() == null
					|| !e.getMessage().toLowerCase().endsWith("only")) {
				Debug.out(e);
			}
		}

		return 1;
	}//openInEMP

	public static int
	openInEMP(
		final String	name,
		final URL		url )
	{
		Class epwClass = null;
		
		try {

			PluginInterface pi = AzureusCoreFactory.getSingleton().getPluginManager().getPluginInterfaceByID(
					"azemp", false );

			if ( pi == null ){

				return( installEMP( name, new Runnable(){ public void run(){ openInEMP( name, url );}} ));
				
			}else if ( !pi.getPluginState().isOperational()){
				
				return( 1 );
			}

			epwClass = pi.getPlugin().getClass().getClassLoader().loadClass(
					"com.azureus.plugins.azemp.ui.swt.emp.EmbeddedPlayerWindowSWT");

		} catch (ClassNotFoundException e1) {
			return 1;
		}

									
		try{
			Method method = epwClass.getMethod("openWindow", new Class[] {
					URL.class, String.class
				});
			
			method.invoke(null, new Object[] {url, name	});
				
			return( 0 );
				
		}catch( Throwable e ){
			debug( "URL/name open method missing" );
			
			return( 1 );
		}
	}
	
	/**
	* @param dm
	*
	* @since 3.0.0.7
	*/
	private static void handleNoFileExists(final DownloadManager dm) {
		final UIFunctionsSWT functionsSWT = UIFunctionsManagerSWT.getUIFunctionsSWT();
		if (functionsSWT == null) {
			return;
		}
		ManagerUtils.start(dm);

		String sPrefix = "v3.mb.PlayFileNotFound.";
		MessageBoxShell mb = new MessageBoxShell(
				MessageText.getString(sPrefix + "title"), MessageText.getString(sPrefix
						+ "text", new String[] {
					dm.getDisplayName(),
				}), new String[] {
					MessageText.getString(sPrefix + "button.remove"),
					MessageText.getString(sPrefix + "button.redownload"),
					MessageText.getString("Button.cancel"),
				}, 2);
		mb.setRelatedObject(dm);
		mb.open(new UserPrompterResultListener() {
			public void prompterClosed(int i) {
				if (i == 0) {
					ManagerUtils.asyncStopDelete(dm, DownloadManager.STATE_STOPPED,
							true, false, null);
				} else if (i == 1) {
					dm.forceRecheck(new ForceRecheckListener() {
						public void forceRecheckComplete(DownloadManager dm) {
							ManagerUtils.start(dm);
						}
					});
				}
			}
		});

	}

	/**
	 * Plays or Streams a Download
	 * 
	 * @param dm
	 * @param file_index Index of file in torrent to play.  -1 to auto-pick
	 * 				"best" play file.
	 */
	public static void playOrStream(final DownloadManager dm, int file_index ) {
		playOrStream(dm, file_index, PlayUtils.COMPLETE_PLAY_ONLY, false, null );
	}
}
