/*
 * @(#)BasicTrackControl.java	1.4 02/08/21
 *
 * Copyright (c) 1996-2002 Sun Microsystems, Inc.  All rights reserved.
 */

package com.sun.media;

import java.util.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.format.*;
import com.sun.media.controls.*;
import com.sun.media.util.*;


/**
 * Basic track control for use with a Processor or Player.
 */
public class BasicTrackControl implements javax.media.control.TrackControl {

    final static String realizeErr = 
		"Cannot get CodecControl before reaching the realized state.";
    final static String connectErr = 
		"Cannot set a PlugIn before reaching the configured state."; 

    PlaybackEngine engine;
    Track track;
    OutputConnector firstOC, lastOC;

    // The modules used for this track.
    protected Vector modules = new Vector(7);

    // The selected renderer for this track.
    protected BasicRendererModule rendererModule;

    protected BasicMuxModule muxModule = null;

    protected boolean prefetchFailed = false;
    protected boolean rendererFailed = false;


    public BasicTrackControl(PlaybackEngine engine, Track track, 
				OutputConnector oc) {
	this.engine = engine;
  	this.track = track;
	this.firstOC = oc;
	this.lastOC = oc;
	setEnabled(track.isEnabled());
    }

    public Format getOriginalFormat() {
	return track.getFormat();
    }

    public Format getFormat() {
	return track.getFormat();
    }

    public Format [] getSupportedFormats() {
	return new Format[] {track.getFormat()};
    }

    /**
     * Top level routine to build a single track.
     */
    public boolean buildTrack(int trackID, int numTracks) {
	return false;
    }

    public Format setFormat(Format format) {
	if (format != null && format.matches(getFormat()))
		return getFormat();
	return null;
    }

    public void setCodecChain(Codec codec[]) 
		throws NotConfiguredError, UnsupportedPlugInException {
	if (engine.getState() > engine.Configured)
	    throw new NotConfiguredError(connectErr);
	if (codec.length < 1)
	    throw new UnsupportedPlugInException("No codec specified in the array.");
    }

    public void setRenderer(Renderer renderer) 
		throws NotConfiguredError {
	if (engine.getState() > engine.Configured)
	    throw new NotConfiguredError(connectErr);
    }

    /**
     * Prefetch the modules for this track.
     */
    public boolean prefetchTrack() {
	BasicModule bm;
	for (int j = 0; j < modules.size(); j++) {
	    bm = (BasicModule)modules.elementAt(j);
	    if (!bm.doPrefetch()) {
		setEnabled(false);
		prefetchFailed = true;
		if (bm instanceof BasicRendererModule)
		    rendererFailed = true;
		return false;
	    }
	}
	// If it failed to prefetch before but now it's alright again,
	// we'll re-enable the track.
	if (prefetchFailed) {
	    setEnabled(true);
	    prefetchFailed = false;
	    rendererFailed = false;
	}
	return true;
    }


    /**
      * Start the modules for this track.
     */
    public void startTrack() {
	for (int j = 0; j < modules.size(); j++) {
	    ((BasicModule)modules.elementAt(j)).doStart();
	}
    }


    /**
      * Start the modules for this track.
     */
    public void stopTrack() {
	for (int j = 0; j < modules.size(); j++) {
	    ((BasicModule)modules.elementAt(j)).doStop();
	}
    }


    public boolean isCustomized() {
	return false;
    }


    /**
     * Returns true if this track holds the master time base.
     */
    public boolean isTimeBase() {
	return false;
    }


    public boolean isEnabled() {
	return track.isEnabled();
    }

    public void setEnabled(boolean enabled) {
	track.setEnabled(enabled);
    }

    protected ProgressControl progressControl() {
	return null;
    };

    protected FrameRateControl frameRateControl() {
	return null;
    };

    public void prError() {
	Log.error("  Unable to handle format: " + getOriginalFormat());
	Log.write("\n");
    }

    public Object[] getControls() throws NotRealizedError {
	if (engine.getState() < Controller.Realized)
		throw (new NotRealizedError(realizeErr));

	InputConnector ic;
	OutputConnector oc = firstOC;
	Module m;
	PlugIn p = null;

	Control controls[];
	Vector cv = new Vector();
	Control c;
	Object cs[];
	int i, size;

	while (oc != null && (ic = oc.getInputConnector()) != null) {
	    m = ic.getModule();
	    cs = m.getControls();
	    if (cs != null) {
	        for (i = 0; i < cs.length; i++) {
		    cv.addElement(cs[i]);
	        }
	    }
	    oc = m.getOutputConnector(null);
	}

	size = cv.size();
	controls = new Control[size];
	for (i = 0; i < size; i++)
	    controls[i] = (Control)cv.elementAt(i);

	return controls;
    }

    public Object getControl(String type) {
	Class cls;
	try {
	    // cls = Class.forName(type);
	    cls = BasicPlugIn.getClassForName(type);
	} catch (ClassNotFoundException e) {
	    return null;
	}
	Object cs[] = getControls();
	for (int i = 0; i < cs.length; i++) {
	    if (cls.isInstance(cs[i]))
		return cs[i];
	}
	return null;
    }

    public java.awt.Component getControlComponent() {
	return null;
    }
	
	
    /**
     * Update the format per track on the progress control.
     */
    public void updateFormat() {
	if (!track.isEnabled())
	    return;

	ProgressControl pc;

	if ((pc = progressControl()) == null)
	    return;

	StringControl sc;
	if (track.getFormat() instanceof AudioFormat) {
	    String channel = "";
	    AudioFormat afmt = (AudioFormat)track.getFormat();
	    sc = pc.getAudioCodec();
	    sc.setValue(afmt.getEncoding());
	    sc = pc.getAudioProperties();
	    if (afmt.getChannels() == 1)
		channel = JMFI18N.getResource("mediaengine.mono");
	    else
		channel = JMFI18N.getResource("mediaengine.stereo");
	    sc.setValue(afmt.getSampleRate()/1000.0 +
		JMFI18N.getResource("mediaengine.khz") + ", " +
		afmt.getSampleSizeInBits() +
		JMFI18N.getResource("mediaengine.-bit") + ", " +
		channel);
	}
	if (track.getFormat() instanceof VideoFormat) {
	    VideoFormat vfmt = (VideoFormat)track.getFormat();
	    sc = pc.getVideoCodec();
	    sc.setValue(vfmt.getEncoding());
	    sc = pc.getVideoProperties();
	    if (vfmt.getSize() != null)
		sc.setValue(vfmt.getSize().width + " X " + vfmt.getSize().height);
	}
    }

    float lastFrameRate = 0.0f;
    long lastStatsTime = 0;  // so now - lastStatsTime will not be 0.

    /**
     * Update the frame rate per track on the progress control.
     */
    public void updateRates(long now) {
	FrameRateControl prc;

	if ((prc = frameRateControl()) == null)
	    return;
	
	if (!track.isEnabled() || 
		!(track.getFormat() instanceof VideoFormat) || 
		(rendererModule == null && muxModule == null))
	    return;

	float rate, avg;

	if (now == lastStatsTime)
	    rate = lastFrameRate;
	else {
	    int framesPlayed;
	    if (rendererModule != null)
		framesPlayed = rendererModule.getFramesPlayed();
	    else
		framesPlayed = muxModule.getFramesPlayed();
	    rate = (float)((float)framesPlayed /
			   (now - lastStatsTime) * 1000.0f);
	}
	avg = (float)((int)(((lastFrameRate + rate)/2) * 10))/10.f;
	prc.setFrameRate(avg);
	lastFrameRate = rate;
	lastStatsTime = now;
	if (rendererModule != null)
	    rendererModule.resetFramesPlayed();
	else
	    muxModule.resetFramesPlayed();
    }
}


