package org.limewire.ui.swing.player;

import java.awt.Container;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;

import javax.media.IncompatibleSourceException;
import javax.media.MediaLocator;
import javax.media.Player;
import javax.media.protocol.DataSource;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import org.limewire.inject.LazySingleton;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
import org.limewire.player.api.AudioPlayer;
import org.limewire.util.ExceptionUtils;
import org.limewire.util.OSUtils;

import com.google.inject.Inject;
import com.google.inject.Provider;

@LazySingleton
class MediaPlayerFactoryImpl implements MediaPlayerFactory {

    private static final Log LOG = LogFactory.getLog(MediaPlayerFactoryImpl.class);
    
    private final Provider<AudioPlayer> player;
    
    private final List<String> handlers = new ArrayList<String>(2);
    
    private final JPanel p = new JPanel();
        
    @Inject
    public MediaPlayerFactoryImpl(Provider<AudioPlayer> player) {
        this.player = player;
        
        registerHandlers();
    }
    
    private void registerHandlers() {
        if(OSUtils.isWindows()) {
            handlers.add("net.sf.fmj.ds.media.content.unknown.Handler");
        } else if(OSUtils.isMacOSX()) {
            handlers.add("net.sf.fmj.qt.media.content.unknown.JavaToCocoaHandler");
            handlers.add("net.sf.fmj.qt.media.content.unknown.QuickTimeForJavaHandler");
        }
    }
    
    @Override
	public Player createMediaPlayer(File file, final Container parentComponent) throws IncompatibleSourceException {
        if (!OSUtils.isWindows() && !OSUtils.isMacOSX()) {
            throw new IllegalStateException("Video is only supported on Windows and Mac");
        }

        Player handler = null;
        
        //attempt to cycle through the list of player handles. If one fails
        // attempt loading file with the next one
        for(String handle : handlers) {
            try {
                if(LOG.isDebugEnabled())
                    LOG.debug("loading " + handle);
                Class clazz = Class.forName(handle);
                Player player = (Player)clazz.newInstance();
                setupPlayer(player, file);
                return player;
            } catch (IncompatibleSourceException e) {
            } catch (ClassNotFoundException e) {
            } catch (InstantiationException e) {
            } catch (IllegalAccessException e) {
            }
        }
        
        if(LOG.isDebugEnabled())
            LOG.debug("Loading default java player");
        
        // default to the java sound player if all else fails
        handler = new JavaSoundPlayer(player);
        setupPlayer(handler, file);
        parentComponent.add(p);
        
        return handler;
    }
    
    /**
     * Attempts to set the source on the player and realize it. After a player is realized 
     * the player is ready to use. If the realization fails an IncompatibleSourceException
     * is thrown.
     */
    private void setupPlayer(Player player, File file) throws IncompatibleSourceException {
        try {
            player.setSource(createDataSource(file));
        } catch (IOException e) {
            throw new IncompatibleSourceException(e.toString() + " \n" + ExceptionUtils.getStackTrace(e));
        } catch (UnsatisfiedLinkError e) {
            throw new IncompatibleSourceException(e.toString() + " \n" + ExceptionUtils.getStackTrace(e));
        }
        player.realize();
    }
    
    /**
     * Creates a DataSource from the file for use with the Player.
     */
    private DataSource createDataSource(File file) throws MalformedURLException{
        //FMJ's handling of files is incredibly fragile.  This works with both quicktime and directshow.
        DataSource source = new net.sf.fmj.media.protocol.file.DataSource();
        if (OSUtils.isMacOSX()) {
            // On OS-X escaping characters such as spaces, parenthesis, etc are causing the 
            // files not to be loaded by FMJ. So let's not escape any characters. 
            // NOTE: only three backslashes since absolute path returns one. Need 4 to escape
            // the string!!
            source.setLocator(new MediaLocator("file:///" + file.getAbsolutePath()));
        } else {
            // This produces a URL with the form file:/path rather than file:///path
            // This URL form is accepted on Windows but not OS X.
            String urlString = file.toURI().toURL().toExternalForm();
            source.setLocator(new MediaLocator(urlString));
        }
        return source;
    }
}
