/*
 * File:     MediaDescriptorUtil.java
 * Project:  MPI Linguistic Application
 * Date:     03 April 2006
 *
 * Copyright (C) 2001-2006  Max Planck Institute for Psycholinguistics
 *
 * 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 mpi.eudico.client.annotator.linkedmedia;

import mpi.eudico.client.annotator.ElanLayoutManager;
import mpi.eudico.client.annotator.ElanLocale;
import mpi.eudico.client.annotator.ViewerManager2;

import mpi.eudico.client.annotator.commands.ELANCommandFactory;

import mpi.eudico.client.annotator.player.ElanMediaPlayer;
import mpi.eudico.client.annotator.player.EmptyMediaPlayer;
import mpi.eudico.client.annotator.player.JMFGraphicMediaPlayer;
import mpi.eudico.client.annotator.player.NoPlayerException;
import mpi.eudico.client.annotator.player.PlayerFactory;
import mpi.eudico.client.annotator.player.QTMediaPlayer;

import mpi.eudico.client.annotator.util.FileExtension;
import mpi.eudico.client.annotator.util.FileUtility;

import mpi.eudico.client.annotator.viewer.SignalViewer;

import mpi.eudico.server.corpora.clomimpl.abstr.MediaDescriptor;
import mpi.eudico.server.corpora.clomimpl.abstr.TranscriptionImpl;

import java.io.File;

import java.util.ArrayList;
import java.util.Vector;

import javax.swing.JOptionPane;


/**
 * A utility class for creating, checking and updating mediadescriptors.
 *
 * @author Han Sloetjes
 */
public class MediaDescriptorUtil {
    /**
     * Checks the existance of the file denoted by the mediadescriptor.
     *
     * @param md the media descriptor
     *
     * @return true when the file exists, false otherwise
     */
    public static boolean checkLinkStatus(MediaDescriptor md) {
        if ((md == null) || (md.mediaURL == null) ||
                (md.mediaURL.length() < 5)) {
            return false;
        }

        // remove the file: part of the URL, leading slashes are no problem
        int colonPos = md.mediaURL.indexOf(':');
        String fileName = md.mediaURL.substring(colonPos + 1);

        // replace all back slashes by forward slashes
        fileName = fileName.replace('\\', '/');

        File file = new File(fileName);

        if (!file.exists()) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Creates a mediadescriptor for the specified file.
     *
     * @param filePath the full path of the file
     *
     * @return a MediaDescriptor
     */
    public static MediaDescriptor createMediaDescriptor(String filePath) {
        if ((filePath == null) || (filePath.length() == 0)) {
            return null;
        }

        String mediaURL = FileUtility.pathToURLString(filePath);

        if (mediaURL == null) {
            return null;
        }

        String mimeType = null;
        String mediaExtension;

        if (mediaURL.indexOf('.') > -1) {
            mediaExtension = mediaURL.substring(mediaURL.lastIndexOf('.') + 1);
        } else {
            mediaExtension = mediaURL.substring(mediaURL.length() - 3); // of no use, at least with JMF
        }

        mimeType = MediaDescriptorUtil.mimeTypeForExtension(mediaExtension);

        MediaDescriptor md = new MediaDescriptor(mediaURL, mimeType);

        return md;
    }

    /**
     * Creates a Vector of mediadescriptors for the specified files.
     *
     * @param fileNames a collection of files
     *
     * @return a Vector of MediaDescriptors
     */
    public static Vector createMediaDescriptors(Vector fileNames) {
        Vector mediaDescriptors = new Vector();

        if (fileNames == null) {
            return mediaDescriptors;
        }

        MediaDescriptor nextMD = null;
mdloop: 
        for (int i = 0; i < fileNames.size(); i++) {
            String path = (String) fileNames.get(i);
            nextMD = MediaDescriptorUtil.createMediaDescriptor(path);

            if (nextMD == null) {
                continue;
            }

            for (int j = 0; j < mediaDescriptors.size(); j++) {
                MediaDescriptor otherMD = (MediaDescriptor) mediaDescriptors.get(j);

                if (otherMD.mediaURL.equals(nextMD.mediaURL)) {
                    // don't add the same file twice?
                    continue mdloop;
                }

                // should this automatic detection of extracted_from remain??
                if (nextMD.mimeType.equals(MediaDescriptor.WAV_MIME_TYPE) &&
                        MediaDescriptorUtil.isVideoType(otherMD)) {
                    if (FileUtility.sameNameIgnoreExtension(nextMD.mediaURL,
                                otherMD.mediaURL)) {
                        nextMD.extractedFrom = otherMD.mediaURL;
                    }
                }

                if (otherMD.mimeType.equals(MediaDescriptor.WAV_MIME_TYPE) &&
                        MediaDescriptorUtil.isVideoType(nextMD)) {
                    if (FileUtility.sameNameIgnoreExtension(nextMD.mediaURL,
                                otherMD.mediaURL)) {
                        otherMD.extractedFrom = nextMD.mediaURL;
                    }
                }
            }

            mediaDescriptors.add(nextMD);
        }

        return mediaDescriptors;
    }

    /**
     * Returns a mime-type for a given file extension.  Works only for a very
     * limited set of known file types.
     *
     * @param fileExtension the file extension
     *
     * @return a Mime-Type String from the <code>MediaDescriptor</code> class
     */
    public static String mimeTypeForExtension(String fileExtension) {
        if ((fileExtension == null) || (fileExtension.length() < 2)) {
            return MediaDescriptor.UNKNOWN_MIME_TYPE;
        }

        String lowExt = fileExtension.toLowerCase();

        for (int i = 0; i < FileExtension.MPEG_EXT.length; i++) {
            if (lowExt.equals(FileExtension.MPEG_EXT[i])) {
                return MediaDescriptor.MPG_MIME_TYPE;
            }
        }

        for (int i = 0; i < FileExtension.WAV_EXT.length; i++) {
            if (lowExt.equals(FileExtension.WAV_EXT[i])) {
                return MediaDescriptor.WAV_MIME_TYPE;
            }
        }

        for (int i = 0; i < FileExtension.QT_EXT.length; i++) {
            if (lowExt.equals(FileExtension.QT_EXT[i])) {
                return MediaDescriptor.QUICKTIME_MIME_TYPE;
            }
        }

        for (int i = 0; i < FileExtension.MISC_AUDIO_EXT.length; i++) {
            if (lowExt.equals(FileExtension.MISC_AUDIO_EXT[i])) {
                return MediaDescriptor.GENERIC_AUDIO_TYPE;
            }
        }

        for (int i = 0; i < FileExtension.MISC_VIDEO_EXT.length; i++) {
            if (lowExt.equals(FileExtension.MISC_VIDEO_EXT[i])) {
                return MediaDescriptor.GENERIC_VIDEO_TYPE;
            }
        }

        return MediaDescriptor.UNKNOWN_MIME_TYPE;
    }

    /**
     * Returns whether the specified mime type is a known video type.
     *
     * @param mimeType the mime type string
     *
     * @return true if the specified mimetype is known to be a video type
     *
     * @see #isVideoType(MediaDescriptor)
     */
    public static boolean isVideoType(String mimeType) {
        if (mimeType == null) {
            return false;
        }

        return (mimeType.equals(MediaDescriptor.GENERIC_VIDEO_TYPE) ||
        mimeType.equals(MediaDescriptor.MPG_MIME_TYPE) ||
        mimeType.equals(MediaDescriptor.QUICKTIME_MIME_TYPE));
    }

    /**
     * Returns whether the specified MediaDescriptor is of a known video type.
     *
     * @param descriptor the mediadescriptor
     *
     * @return true if the specified mediadescriptor is of a known video type
     *
     * @see #isVideoType(String)
     */
    public static boolean isVideoType(MediaDescriptor descriptor) {
        if (descriptor == null) {
            return false;
        }

        return MediaDescriptorUtil.isVideoType(descriptor.mimeType);
    }

    /**
     * Tries to update the mediaplayers in the viewermanager as well as the
     * layoutmanager and finally sets the mediadescriptors in the
     * transcription.
     *
     * @param transcription the Transcription with the old descriptors
     * @param descriptors the new media descriptors
     */
    public static void updateMediaPlayers(TranscriptionImpl transcription,
        Vector descriptors) {
        if ((transcription == null) || (descriptors == null)) {
            return;
        }

        long mediaTime = 0L;

        //Vector currentDesc = transcription.getMediaDescriptors();
        ViewerManager2 viewerManager = ELANCommandFactory.getViewerManager(transcription);
        ElanLayoutManager layoutManager = ELANCommandFactory.getLayoutManager(transcription);
        SignalViewer signalViewer = layoutManager.getSignalViewer();
        mediaTime = viewerManager.getMasterMediaPlayer().getMediaTime();

        // make sure all players are connected
        if (layoutManager.getMode() == ElanLayoutManager.SYNC_MODE) {
            layoutManager.connectAllPlayers();
        }

        // the master media player cannot be removed directly
        // replace the master; the master is added to the slaves
        viewerManager.setMasterMediaPlayer(new EmptyMediaPlayer(
                Integer.MAX_VALUE));

        // remove the slaves
        Vector slavePlayers = viewerManager.getSlaveMediaPlayers();
        Vector remPlayers = new Vector(slavePlayers.size());

        for (int i = 0; i < slavePlayers.size(); i++) {
            remPlayers.add(slavePlayers.get(i));
        }

        for (int i = 0; i < remPlayers.size(); i++) {
            ElanMediaPlayer slave = (ElanMediaPlayer) remPlayers.get(i);
            viewerManager.destroyMediaPlayer(slave);
            layoutManager.remove(slave);
        }

        if (signalViewer != null) {
            viewerManager.destroyViewer(signalViewer);
            layoutManager.remove(signalViewer);
        }

        // create new players from the descriptors
        MediaDescriptorUtil.createMediaPlayers(transcription, descriptors);

        viewerManager.getMasterMediaPlayer().setMediaTime(mediaTime);
        transcription.setMediaDescriptors(descriptors);
        transcription.setChanged();
    }

    /**
     * Tries to create the mediaplayers in the viewermanager as well as the
     * layoutmanager and finally sets the mediadescriptors in the
     * transcription.
     *
     * @param transcription the Transcription with the old descriptors
     * @param descriptors the new media descriptors
     */
    public static void createMediaPlayers(TranscriptionImpl transcription,
        Vector descriptors) {
        if ((transcription == null) || (descriptors == null)) {
            return;
        }

        int numDesc = descriptors.size();

        try {
            ViewerManager2 viewerManager = ELANCommandFactory.getViewerManager(transcription);
            ElanLayoutManager layoutManager = ELANCommandFactory.getLayoutManager(transcription);

            int maxPlayers = 6; //hardwired for now
            int nrOfPlayers = 0;
            String masterMediaURL = null;
            String signalSource = null;
            long signalOffset = 0;
            long masterMediaOffset = 0;
            boolean createSvgPlayer = false;

            if (transcription.getSVGFile() != null /*||SVGPrefs.getUseSVG()*/    ) {
                // this makes all players QT players for now
                createSvgPlayer = true;
            }

            MediaDescriptor curMD;
            ArrayList failedPlayers = null;

            for (int i = 0; (i < numDesc) && (nrOfPlayers < maxPlayers); i++) {
                curMD = (MediaDescriptor) descriptors.get(i);

                if (!MediaDescriptorUtil.checkLinkStatus(curMD)) {
                    continue;
                }

                if ((masterMediaURL != null) &&
                        curMD.mimeType.equals(MediaDescriptor.WAV_MIME_TYPE)) {
                    // if it is extracted wav set signal source and do not create a player
                    if (masterMediaURL.equals(curMD.extractedFrom)) {
                        signalSource = curMD.mediaURL;
                        signalOffset = masterMediaOffset;
                        viewerManager.setSignalSourcePlayer(viewerManager.getMasterMediaPlayer());

                        continue; // create no player
                    }
                }

                try {
                    ElanMediaPlayer player;

                    if (createSvgPlayer) {
                        player = viewerManager.createMediaPlayer(curMD,
                                PlayerFactory.QT_MEDIA_FRAMEWORK);
                    } else if (curMD.mimeType == MediaDescriptor.QUICKTIME_MIME_TYPE) {
                        player = viewerManager.createMediaPlayer(curMD,
                                PlayerFactory.QT_MEDIA_FRAMEWORK);
                    } else {
                        player = viewerManager.createMediaPlayer(curMD);
                    }

                    if (nrOfPlayers == 0) {
                        // here comes the mastermedia player
                        viewerManager.setMasterMediaPlayer(player);
                        masterMediaURL = curMD.mediaURL;
                        masterMediaOffset = curMD.timeOrigin;

                        // if audio than also signal source until proven later 
                        // otherwise by mpg media source
                        if (curMD.mimeType.equals(MediaDescriptor.WAV_MIME_TYPE)) {
                            signalSource = curMD.mediaURL;
                            signalOffset = curMD.timeOrigin;
                            viewerManager.setSignalSourcePlayer(player);
                        }
                    } else {
                        // if WAV && signalsource == null use it is the source for the signalviewer
                        if (curMD.mimeType.equals(MediaDescriptor.WAV_MIME_TYPE) &&
                                (signalSource == null)) {
                            signalSource = curMD.mediaURL;
                            signalOffset = curMD.timeOrigin;
                        }

                        // if this WAV is extracted from the mastermedia, inherit time-origin
                        if (curMD.mimeType.equals(MediaDescriptor.WAV_MIME_TYPE) &&
                                masterMediaURL.equals(curMD.extractedFrom)) {
                            signalSource = curMD.mediaURL;
                            signalOffset = viewerManager.getMasterMediaPlayer()
                                                        .getMediaDescriptor().timeOrigin;
                            viewerManager.setSignalSourcePlayer(viewerManager.getMasterMediaPlayer());
                        }

                        // wave player is master media but wav is extracted from this mpg

                        /* Mar 06: block removed: the .wav form as master can have its own offset
                        if (viewerManager.getMasterMediaPlayer()
                                             .getMediaDescriptor().mimeType.equals(
                                    MediaDescriptor.WAV_MIME_TYPE) &&
                                curMD.mediaURL.equals(
                                    viewerManager.getMasterMediaPlayer()
                                                     .getMediaDescriptor().extractedFrom)) {
                            masterMediaOffset = curMD.timeOrigin;
                            // march 06: bug: the video is set as the source for the signal viewer
                            //signalSource = curMD.mediaURL;// bug...
                            //signalOffset = curMD.timeOrigin;// bug...
                            //viewerManager.setSignalSourcePlayer(player);

                            // should the offset of the master media be adjusted to that of the video?
                            // 01-05 no: the user will be warned when he or she gives the files a different offset
                            //viewerManager.getMasterMediaPlayer().setOffset(masterMediaOffset);
                        }
                        */
                    }

                    nrOfPlayers++;

                    // only add layoutable players to the layout
                    //if (curMD.mimeType.equals(MediaDescriptor.MPG_MIME_TYPE)) {
                    layoutManager.add(player);

                    //}
                } catch (NoPlayerException npe) {
                    if (failedPlayers == null) {
                        failedPlayers = new ArrayList();
                    }

                    failedPlayers.add(curMD);
                }
            }

            if (nrOfPlayers == 0) {
                viewerManager.setMasterMediaPlayer(new EmptyMediaPlayer(
                        transcription.getLatestTime()));
                layoutManager.add(viewerManager.getMasterMediaPlayer());
            }

            // Create a signal viewer
            if (signalSource != null) {
                SignalViewer newSignalViewer = viewerManager.createSignalViewer(signalSource);
                newSignalViewer.setOffset(signalOffset);

                layoutManager.add(newSignalViewer);
            }

            if (createSvgPlayer) {
                if (viewerManager.getMasterMediaPlayer() instanceof QTMediaPlayer) {
                    viewerManager.createQTSVGViewer();
                } else if (viewerManager.getMasterMediaPlayer() instanceof JMFGraphicMediaPlayer) {
                    // effect or renderer
                    // JMFSVGViewer jmfSVGViewer =
                    viewerManager.createJMFSVGViewer();
                }
            }

            layoutManager.doLayout();

            // inform the user of failures...
            if ((failedPlayers != null) && (failedPlayers.size() > 0)) {
                StringBuffer sb = new StringBuffer(
                        "No player could be created for:\n");

                for (int i = 0; i < failedPlayers.size(); i++) {
                    sb.append("- " +
                        ((MediaDescriptor) failedPlayers.get(i)).mediaURL +
                        "\n");
                }

                JOptionPane.showMessageDialog(null, sb.toString(),
                    ElanLocale.getString("Message.Warning"),
                    JOptionPane.WARNING_MESSAGE);
            }
        } catch (Exception rex) {
        }
    }
}
