package net.sourceforge.jffmpeg;

import java.util.ResourceBundle;

import java.util.StringTokenizer;

import javax.media.format.VideoFormat;
import javax.media.format.AudioFormat;
import javax.media.Format;

import net.sourceforge.jffmpeg.ffmpegnative.NativeDecoder;

/**
 * Manage the available Codecs.
 *
 * These are loaded from a .properties file.
 */
public class CodecManager {
    /**
     * Resource bundle with configuration information
     */
    private ResourceBundle resources;

    /**
     * Location of Jffmpeg resource file
     */
    private static final String JFFMPEG_RESOURCE = "net.sourceforge.jffmpeg.Jffmpeg";

    /**
     * List of supported video formats
     */
    private static final String SUPPORTED_VIDEO_FORMATS = "SupportedVideoFormats";

    /**
     * List of supported video formats
     */
    private static final String SUPPORTED_AUDIO_FORMATS = "SupportedAudioFormats";

    /**
     * Override native library name
     */
    private static final String LIBRARY_NAME = "FFMpegNativeLibrary";
    private static final String SYS_LIBRARY_NAME = "net.sourceforge.jffmpeg.FFMpegNativeLibrary";


    /**
     * Load the CodecManager class
     */
    public CodecManager() {
        resources = ResourceBundle.getBundle( JFFMPEG_RESOURCE );
    }

    /**
     * Read the native library name.
     *  This can be overriden using a system property.
     */
    public String getNativeLibraryName() {
        String libraryName = null;
        try {
            libraryName = System.getProperty( SYS_LIBRARY_NAME );
        } catch ( Exception e ) {
            /* SecurityException */
        }
 
        try {
            if ( libraryName == null || libraryName.length() == 0 ) {
                libraryName = resources.getString( LIBRARY_NAME );
            }
        } catch ( Exception e ) {
            /* ResourceNotFoundException */
        }
 
        return libraryName;
    }
     
    /**
     * Return list of supported VideoFormats
     */
    public VideoFormat[] getSupportedVideoFormats() {
        String formats = resources.getString( SUPPORTED_VIDEO_FORMATS );
        StringTokenizer tokenizer = new StringTokenizer( formats, "," );

        VideoFormat[] videoFormats = new VideoFormat[ tokenizer.countTokens() ];
        int i = 0;
        while ( tokenizer.hasMoreTokens() ) {
            videoFormats[ i++ ] = new VideoFormat( tokenizer.nextToken() );
        }
        return videoFormats;
    }

    /**
     * Return list of supported VideoFormats
     */
    public AudioFormat[] getSupportedAudioFormats() {
        String formats = resources.getString( SUPPORTED_AUDIO_FORMATS );
        StringTokenizer tokenizer = new StringTokenizer( formats, "," );

        AudioFormat[] audioFormats = new AudioFormat[ tokenizer.countTokens() ];
        int i = 0;
        while ( tokenizer.hasMoreTokens() ) {
            audioFormats[ i++ ] = new AudioFormat( tokenizer.nextToken() );
        }
        return audioFormats;
    }

    /**
     * Return the Codec information for this encoding
     */
    public JffmpegVideoFormat getVideoCodec( String encoding ) {
        return new JffmpegVideoFormat( encoding.toLowerCase(), resources );
    }

    /**
     * Return the Codec information for this encoding
     */
    public JffmpegAudioFormat getAudioCodec( String encoding ) {
        return new JffmpegAudioFormat( encoding.toLowerCase(), resources );
    }

    /**
     * Store if the native codec is available
     */
    private static volatile int isNativeLoaded = -1;
    public synchronized static boolean isNativeAvailable() {
        if ( isNativeLoaded == -1 ) {
            try {
                NativeDecoder d = new NativeDecoder();
                isNativeLoaded = d.isCodecAvailable() ? 1 : 0;
            } catch ( Throwable e ) {
                isNativeLoaded = 0;
            }
        }
        return (isNativeLoaded > 0);
    }        
}

/**
 * This class is used to describe an individual 
 * Video codec.  All parameters are loaded from
 * a resource bundle.
 */
class JffmpegVideoFormat extends VideoFormat {
    private static final String RTP_STRING = ".RTP";
    private static final String NATIVE_STRING = ".Native";
    private static final String CLASS_STRING = ".Class";
    private static final String JAVA_CLASS_STRING = ".JavaClass";
    private static final String FFMPEG_NAME = ".FFMpegName";
    private static final String TRUNCATE_STRING = ".IsTruncated";

    private String name;
    ResourceBundle configuration;

    /**
     * Construct a Video codec description
     */
    public JffmpegVideoFormat( String name, ResourceBundle configuration ) {
        super( name );
        this.name = name;
        this.configuration = configuration;
    }

    /**
     * Returns true if this is an RTP format
     */
    public boolean isRtp() {
        return "true".equalsIgnoreCase( configuration.getString( name + RTP_STRING) );
    }

    /**
     * Returns true if this Codec is managed by the JNI layer
     */ 
    public boolean isNative() {
        return CodecManager.isNativeAvailable() && "true".equalsIgnoreCase( configuration.getString( name + NATIVE_STRING) );
    }

    /**
     * Return the class set to handle this codec
     */
    public String getCodecClass() {
        return configuration.getString( name + (isNative() ? CLASS_STRING : JAVA_CLASS_STRING) );
    }

    /**
     * Returns the identifier used by FFMpeg for this Codec
     */
    public String getFFMpegCodecName() {
        return configuration.getString( name + FFMPEG_NAME );
    }

    public boolean isTruncated() {
        return "true".equalsIgnoreCase( configuration.getString( name + TRUNCATE_STRING) );
    }
}

/**
 * This class is used to describe an individual 
 * Audio codec.  All parameters are loaded from
 * a resource bundle.
 */
class JffmpegAudioFormat extends AudioFormat {
    private static final String NATIVE_STRING = ".Native";
    private static final String CLASS_STRING = ".Class";
    private static final String JAVA_CLASS_STRING = ".JavaClass";
    private static final String FFMPEG_NAME = ".FFMpegName";

    private String name;
    ResourceBundle configuration;

    /**
     * Construct a Video codec description
     */
    public JffmpegAudioFormat( String name, ResourceBundle configuration ) {
        super( name );
        this.name = name;
        this.configuration = configuration;
    }

    /**
     * Returns true if this Codec is managed by the JNI layer
     */ 
    public boolean isNative() {
        return CodecManager.isNativeAvailable() && "true".equalsIgnoreCase( configuration.getString( name + NATIVE_STRING) );
    }

    /**
     * Return the class set to handle this codec
     */
    public String getCodecClass() {
        return configuration.getString( name + (isNative() ? CLASS_STRING : JAVA_CLASS_STRING) );
    }

    /**
     * Returns the identifier used by FFMpeg for this Codec
     */
    public String getFFMpegCodecName() {
        return configuration.getString( name + FFMPEG_NAME );
    }
}
