package railo.runtime.tag;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Random;

import railo.commons.io.res.Resource;
import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.ClassException;
import railo.commons.lang.StringUtil;
import railo.loader.util.Util;
import railo.runtime.PageContext;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.PageException;
import railo.runtime.ext.tag.TagSupport;
import railo.runtime.op.Caster;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.util.KeyConstants;
import railo.runtime.video.ProfileCollection;
import railo.runtime.video.VideoExecuter;
import railo.runtime.video.VideoInfo;
import railo.runtime.video.VideoInput;
import railo.runtime.video.VideoInputImpl;
import railo.runtime.video.VideoOutput;
import railo.runtime.video.VideoOutputImpl;
import railo.runtime.video.VideoProfile;
import railo.runtime.video.VideoProfileImpl;
import railo.runtime.video.VideoUtilImpl;

/**
 * implementation of the tag Compiler
 */
public class Video extends TagSupport {

	private static final int ACTION_CONVERT=0;
	private static final int ACTION_INFO=1;
	private static final int ACTION_CONCAT=2;
	private static final int ACTION_CUT_IMAGE=4;
	private static final int ACTION_INSTALL=8;
	private static final int ACTION_UNINSTALL=16;
	
	public static final int NAMECONFLICT_UNDEFINED=0;
	public static final int NAMECONFLICT_ERROR=1;
	public static final int NAMECONFLICT_SKIP=2;
	public static final int NAMECONFLICT_OVERWRITE=3;
	public static final int NAMECONFLICT_MAKEUNIQUE=4;

	public static final int EXECUTION_QUALITY=0;
	public static final int EXECUTION_PERFORMANCE=1;
	private static final Key SOURCE = KeyConstants._source;
	private static final Key SOURCE1 = KeyImpl.intern("source1");
	private static final Key SOURCE2 = KeyImpl.intern("source2");
	private static final Key AUDIO = KeyImpl.intern("audio");
	private static final Key VIDEO = KeyImpl.intern("video");
	
	private static VideoUtilImpl util=VideoUtilImpl.getInstance();
	
	
	private String result="cfvideo";
	private int action;
	private String strAction;
	private VideoInput source;
	private VideoInput source1;
	private VideoInput source2;
	private VideoOutput destination;
	private int nameconflict=NAMECONFLICT_UNDEFINED;
	private String name;
	private int width=-1;
	private int height=-1;
	private String strWidth;
	private String strHeight;
	private Struct data;
	

	private long audiosamplerate=0;
	private long audioBitrate=0;
	private double startTime=0;
	private long startFrame=0;
	private double maxTime=0;
	private long maxFrame=0;
	
	private VideoProfile profile=null;
	
	private int aspectRatio;
	private int framerate;
	private long videoBitrate;
	private long videoBitrateMin;
	private long videoBitrateMax;
	private long videoBitrateTolerance;
	private String debug=null;
	
	private String author;
	private String title;
	private String comment;
	private String copyright;
	private String videoCodec;
	private String audioCodec;
	private long buffersize;
	private int execution=EXECUTION_PERFORMANCE;
	private static ProfileCollection _profileCollection;
	
	public Video(){
		
	}
	
	@Override
	public void release() {
		super.release();
		result="cfvideo";
		execution=EXECUTION_PERFORMANCE;
		source=null;
		source1=null;
		source2=null;
		destination=null;
		nameconflict=NAMECONFLICT_UNDEFINED;
		name=null;
		width=-1;
		height=-1;
		profile=null;
		audioBitrate=0;
		videoCodec=null;
		audioCodec=null;
		
		audiosamplerate=0;
		aspectRatio=0;
		framerate=0;
		videoBitrate=0;
		videoBitrateMin=0;
		videoBitrateMax=0;
		videoBitrateTolerance=0;
		buffersize=0;
		author=null;
		title=null;
		comment=null;
		copyright=null;
		debug=null;

		maxTime=0;
		maxFrame=0;
		startTime=0;
		startFrame=0;
		strWidth=null;
		strHeight=null;
		data=null;
	}
	


	/**
	 * @param action the action to set
	 * @throws PageException 
	 */
	public void setAction(String action) throws PageException {
		strAction=action;
		action=action.toLowerCase().trim();
        if(action.equals("concat"))			this.action=ACTION_CONCAT; 
        else if(action.equals("merge"))			this.action=ACTION_CONCAT; 
        else if(action.equals("convert"))	this.action=ACTION_CONVERT;
        //else if(action.equals("cut"))		this.action=ACTION_CUT;
        else if(action.equals("cut image"))	this.action=ACTION_CUT_IMAGE;
        else if(action.equals("cutimage"))	this.action=ACTION_CUT_IMAGE;
        else if(action.equals("cut_image"))	this.action=ACTION_CUT_IMAGE;
        else if(action.equals("cut-image"))	this.action=ACTION_CUT_IMAGE;
        else if(action.equals("info"))		this.action=ACTION_INFO;
        
        else if(action.equals("install"))		this.action=ACTION_INSTALL;
        else if(action.equals("uninstall"))		this.action=ACTION_UNINSTALL;
        
        else throw doThrow("invalid value for attribute action for tag video ["+action+"], " +
        		"valid actions are [concat, convert, cutImage, info,install,uninstall]");
		
	}

	/**
	 * @param source the source to set
	 * @throws PageException 
	 */
	public void setSource(String source) throws PageException {
		this.source = new VideoInputImpl(ResourceUtil.toResourceExisting(pageContext, source));
	}
	
	public void setData(Struct data){
		this.data=data;
	}

	/**
	 * @param destination the destination to set
	 * @throws PageException 
	 */
	public void setDestination(String destination) {
		this.destination = new VideoOutputImpl(ResourceUtil.toResourceNotExisting(pageContext, destination));
	}

	/** set the value nameconflict
	*  Action to take if filename is the same as that of a file in the directory.
	* @param nameconflict value to set
	 * @throws ApplicationException 
	**/
	public void setNameconflict(String nameconflict) throws PageException	{
		nameconflict=nameconflict.toLowerCase().trim();
		if("error".equals(nameconflict)) 			this.nameconflict=NAMECONFLICT_ERROR;
		else if("skip".equals(nameconflict)) 		this.nameconflict=NAMECONFLICT_SKIP;
		else if("overwrite".equals(nameconflict)) 	this.nameconflict=NAMECONFLICT_OVERWRITE;
		else if("makeunique".equals(nameconflict)) 	this.nameconflict=NAMECONFLICT_MAKEUNIQUE;
		else throw doThrow("invalid value for attribute nameconflict ["+nameconflict+"]",
				"valid values are [error,skip,overwrite,makeunique]");
	}

	public void setProfile(String strProfile) throws PageException {
		VideoProfile p = getProfile(strProfile);
		if(p!=null)profile=p.duplicate();
		else throw doThrow("invalid profile defintion ["+strProfile+"], valid profiles are ["+getProfileKeyList()+"]");
	}

	public void setExecution(String execution) throws PageException {

		execution=execution.toLowerCase().trim();
		if("quality".equals(execution)) 			this.execution=EXECUTION_QUALITY;
		else if("q".equals(execution)) 				this.execution=EXECUTION_QUALITY;
		else if("performance".equals(execution)) 	this.execution=EXECUTION_PERFORMANCE;
		else if("p".equals(execution)) 				this.execution=EXECUTION_PERFORMANCE;
		else throw doThrow("invalid value for attribute execution ["+execution+"]",
				"valid values are [quality,performance]");
	}

	public void setQuality(String strQuality) throws PageException {
		setProfile(strQuality);
	}
		
	@Override
	public int doStartTag() throws PageException	{
		
		try {
			if(action==ACTION_CONVERT) doActionConvert();
			else if(action==ACTION_CONCAT) doActionConcat();
			else if(action==ACTION_CUT_IMAGE) doActionCutImage();
			else if(action==ACTION_INFO) doActionInfo();
			else if(action==ACTION_INSTALL) doActionInstall();
			else if(action==ACTION_UNINSTALL) doActionUninstall();
		}
		catch(Throwable t) {
			throw Caster.toPageException(t);
		}
		return SKIP_BODY;
	}



	private void doActionInstall() throws ClassException, IOException {
		if(data==null)data=new StructImpl();
		getVideoExecuter().install(pageContext.getConfig(),data);  
	}
	private void doActionUninstall() throws ClassException, IOException {
		getVideoExecuter().uninstall(pageContext.getConfig());  
	}

	public Struct doActionInfo() throws PageException, IOException {
		return doActionInfo(source);
		
	}
	
	private Struct doActionInfo(VideoInput source) throws PageException, IOException {

		// precheck settings
		checkFile(source,"source", true, true, false);
		
		//VideoConfig vc = getConfig();
		debug(source);
		Struct info = toStruct(getVideoExecuter().info(pageContext.getConfig(), source));
		pageContext.setVariable(result,info);
		return info;
		
	}

	private VideoExecuter getVideoExecuter() throws ClassException {
		return VideoUtilImpl.createVideoExecuter(pageContext.getConfig());
	}



	private VideoInfo getInfo(VideoInput source) throws PageException, IOException {

		// precheck settings
		checkFile(source,"source", true, true, false);
		
		// execute
		return getVideoExecuter().info(pageContext.getConfig(),source);
		//print.out(raw);

		// write cfvideo		
		//return new Info(raw,true,startTime,maxTime);	
	}


	private void doActionCutImage() throws PageException, IOException {

		// precheck settings
		checkFile(source,"source", true, true, false);
		if(!checkDestination(source,destination,name,nameconflict)) return;
		
		// input
		if(profile==null)profile=new VideoProfileImpl();
		
		// settings
		settings(destination,profile);
		
		destination.setMaxFrames(1);
		destination.setFormat("image2");
		// execute
		Struct info=toStruct(getVideoExecuter().convert(pageContext.getConfig(),new VideoInput[]{source},destination,profile));
		
		// write cfvideo
		debug(source);
		pageContext.setVariable(result,info);
		
		//setResult(raw,false);
		
	}



	private void doActionConcat() throws PageException, IOException {
		// precheck settings
		checkFile(source1,"source1", true, true, false);
		checkFile(source2,"source2", true, true, false);
		if(!checkDestination(source1,destination,name,nameconflict)) return;

		Pair s1 =toMpeg(source1);
		Pair s2 =toMpeg(source2);
		source = new VideoInputImpl(pageContext.getConfig().getTempDirectory().getRealResource("tmp-"+new Random().nextInt()+".mpg"));
		try {
			merge(s1.res,s2.res,source.getResource());
			Struct sct = doActionConvert();
			sct.setEL(SOURCE1, s1.sct);
			sct.setEL(SOURCE2, s2.sct);
			sct.removeEL(SOURCE);
			
		}
		finally {
			source.getResource().delete();
			if(!s1.res.equals(source1.getResource()))s1.res.delete();
			if(!s2.res.equals(source2.getResource()))s2.res.delete();
		}
		
	}



	private void merge(Resource in1, Resource in2, Resource out) throws IOException {
		InputStream is1=null;
		InputStream is2=null;
		OutputStream os=null;
		try {
			is1=in1.getInputStream();
			is2=in2.getInputStream();
			os=out.getOutputStream();
		}
		catch(IOException ioe) {
			Util.closeEL(is1);
			Util.closeEL(is2);
			Util.closeEL(os);
			throw ioe;
		}

		try {
			copy(is1, os);
			copy(is2, os);
		} 
		finally {
			Util.closeEL(is1);
			Util.closeEL(is2);
			Util.closeEL(os);
		}
	}


	public final static void copy(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[0xffff];
        int len;
        while((len = in.read(buffer)) !=-1)
          out.write(buffer, 0, len);
    }
    



	private Pair toMpeg(VideoInput vi) throws PageException, IOException {
		VideoInfo info = getInfo(vi);
		//Struct sct = _doActionInfo(vi);
		
		
		if("mpeg1video".equals(info.getVideoCodec()))
			return new Pair(vi.getResource(),toStruct(info));
		
		VideoOutput tmp = new VideoOutputImpl(pageContext.getConfig().getTempDirectory().getRealResource("tmp-"+new Random().nextInt()+".mpg"));
		try {
			doActionConvert(vi,tmp,null,null,NAMECONFLICT_ERROR);
			return new Pair(tmp.getResource(),toStruct(info));
		}
		catch(PageException pe) {
			tmp.getResource().delete();
			throw pe;
		}
		catch(IOException ioe) {
			tmp.getResource().delete();
			throw ioe;
		}
		
	}



	private Struct doActionConvert() throws PageException, IOException {
		return doActionConvert(source,destination,profile,name,nameconflict);
	}
	private Struct doActionConvert(VideoInput source,VideoOutput destination, VideoProfile quality,String name, int nameconflict) throws PageException, IOException {
				
		// precheck settings
		checkFile(source,"source", true, true, false);
		if(!checkDestination(source,destination,name,nameconflict)) return new StructImpl();
		
		// input
		if(quality==null)quality=new VideoProfileImpl();
		
		// settings
		settings(destination,quality);
		
		// execute
		Struct info = toStruct(getVideoExecuter().convert(pageContext.getConfig(),new VideoInput[]{source},destination,quality));
		
		// write cfvideo
		debug(source);
		
		pageContext.setVariable(result,info);
		return info;
		//return setResult(raw,false);
	}    
	

	private void debug(VideoInput vi) throws PageException {
		if(!StringUtil.isEmpty(debug))pageContext.setVariable(debug, vi.getCommandAsString());
	}

	private void settings(VideoOutput vo,VideoProfile vq) throws PageException, IOException {
		defineSize(vq,new VideoInput[]{source});
		if(audioBitrate>0)vq.setAudioBitrate(audioBitrate);
		if(aspectRatio>0)vq.setAspectRatio(aspectRatio);
		if(framerate>0)vq.setFramerate(framerate);
		if(videoBitrate>0)vq.setVideoBitrate(videoBitrate);
		if(videoBitrateMin>0)vq.setVideoBitrateMin(videoBitrateMin);
		if(videoBitrateMax>0)vq.setVideoBitrateMax(videoBitrateMax);
		if(videoBitrateTolerance>0)vq.setVideoBitrateTolerance(videoBitrateTolerance);
		if(audiosamplerate>0)vq.setAudioSamplerate(audiosamplerate);
		if(buffersize>0)vq.setBufferSize(buffersize);
		if(execution==EXECUTION_QUALITY)vq.setPass(2);

		if(!StringUtil.isEmpty(title))vo.setTitle(title);
		if(!StringUtil.isEmpty(author))vo.setAuthor(author);
		if(!StringUtil.isEmpty(comment))vo.setComment(comment);
		if(!StringUtil.isEmpty(copyright))vo.setCopyright(copyright);
		if(!StringUtil.isEmpty(videoCodec))vq.setVideoCodec(videoCodec);
		if(!StringUtil.isEmpty(audioCodec))vq.setAudioCodec(audioCodec);
		
		
		
		if(framerate==0 && (startFrame>0 || maxTime>0)) {
			framerate=(int)getInfo(source).getFramerate();
		}
		
		// start
		if(startFrame>0) {
			if(framerate>0) startTime=(startFrame/framerate);
		}
		if(startTime>0)destination.setOffset(startTime);
		
		// max
		if(maxTime>0) {
			if(framerate>0) maxFrame=(long)(maxTime*framerate);
		}
		if(maxTime>0)vo.setMaxFrames(maxFrame);
		
		// maxtime (only for cfvideo struct)
		if(maxFrame>0 && maxTime==0) {
			if(framerate!=-1) maxTime=maxFrame/framerate;
		}
		destination.setFrameRate(framerate);
		
	}

	private ProfileCollection getProfileCollection() throws PageException {
		return getProfileCollection(pageContext);
	}
	
	public static ProfileCollection getProfileCollection(PageContext pc) throws PageException {
		if(_profileCollection==null) {
			try {
				_profileCollection=new ProfileCollection(pc.getConfig());
			} 
			catch (Exception e) {
				throw Caster.toPageException(e);
			}
		}
		return _profileCollection;
	}

	private void defineSize(VideoProfile quality, VideoInput[] sources) throws PageException {
		if(strWidth!=null || strHeight!=null) {
			int[] dim = VideoUtilImpl.getInstance().calculateDimension(pageContext, sources, width, strWidth, height, strHeight);
			quality.setDimension(dim[0], dim[1]);
		}
	}

	private void checkFile(VideoInput vi, String label, boolean exist, boolean canRead, boolean canWrite) throws PageException {
		if(vi==null) {
			throw doThrow("attribute "+label+" is required for tag video action "+strAction);
		}
		checkFile(vi.getResource(), label, exist, canRead, canWrite);
	}

	private void checkFile(VideoOutput vo, String label, boolean exist, boolean canRead, boolean canWrite) throws PageException {
		if(vo==null) {
			throw doThrow("attribute "+label+" is required for tag video action "+strAction);
		}
		checkFile(vo.getResource(), label, exist, canRead, canWrite);
	}
	
	
	private void checkFile(Resource res, String label, boolean exist, boolean canRead, boolean canWrite) throws PageException {
		
		if(res==null) {
			throw doThrow("attribute "+label+" is required for tag video action "+strAction);
		}
		
		
		if(!res.exists()) {
			if(exist)throw doThrow(""+label+" file does not exist");
		}
		else {
			 if(!res.isFile())
				throw doThrow(label+" ["+res.toString()+"] is not a file");
	        else if(canRead &&!res.canRead())
	            throw doThrow("no read access to "+label+" ["+res.toString()+"]");
	        else if(canWrite && !res.canWrite())
	            throw doThrow("no write access to "+label+" ["+res.toString()+"]");
		}
	}
	
	private boolean checkDestination(VideoInput vi, VideoOutput destination, String name, int nameconflict) throws PageException {
		checkFile(destination,"destination", false, false, false);
		
		// destination
		if(!Util.isEmpty(name))destination.setResource(destination.getResource().getRealResource(name));
		else if(destination.getResource().isDirectory()) {
			destination.setResource(destination.getResource().getRealResource(vi.getResource().getName()));
		}
		
		// escape %d
		String _name=destination.getResource().getName();
		if(_name.indexOf("%d")!=-1) {
			destination.setResource(destination.getResource().getParentResource().getRealResource(_name.replaceAll("%d", "%%d")));
		}
		
		
		if(destination.getResource().exists() && nameconflict!=NAMECONFLICT_OVERWRITE) {
			// SKIP
			if(nameconflict==NAMECONFLICT_SKIP) return false;
			// MAKEUNIQUE
			else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination.setResource(makeUnique(destination.getResource()));
			// ERROR
			else doThrow("destiniation file ["+destination.toString()+"] already exist");
		}
        return true;
	}
	
	private Resource makeUnique(Resource res) {

		String ext=getFileExtension(res);
		String name=getFileName(res);
		ext=(ext==null)?"":"."+ext;
		int count=0;
		while(res.exists()) {
			res=res.getParentResource().getRealResource(name+(++count)+ext);
		}
		return res;
	}
	
	/**
	 * get file extension of a file object
	 * @param file file object
	 * @return extnesion
	 */
	private static String getFileExtension(Resource file) {
		String name=file.getName();
		int index = name.lastIndexOf('.');
		if(index==-1) return null;
		return name.substring(index+1).trim();
	}
	
	/**
	 * get file name of a file object without extension
	 * @param file file object
	 * @return name of the file 
	 */
	private static String getFileName(Resource file) {
		String name=file.getName();
		int pos=name.lastIndexOf(".");
		
		if(pos==-1)return name;
		return name.substring(0,pos);
	}

	private VideoProfile getProfile(String strProfile) throws PageException {
		strProfile=strProfile.trim().toLowerCase();
		return getProfileCollection().getProfiles().get(strProfile);
	}
	
	private String getProfileKeyList() throws PageException {
		Iterator<String> it = getProfileCollection().getProfiles().keySet().iterator();
		StringBuffer sb=new StringBuffer();
		boolean doDel=false;
		while(it.hasNext()) {
			if(doDel)sb.append(", ");
			sb.append(it.next());
			doDel=true;
		}
		
		return sb.toString();
	}
	
	private PageException doThrow(String message) {
		return doThrow(message,null);
	}
	
	private PageException doThrow(String message, String detail) {
		if(detail==null)return new ApplicationException(message);
		return new ApplicationException(message,detail);
	}
	




	/**
	 * @param width the width to set
	 */
	public void setWidth(String strWidth) {
		this.width = Caster.toIntValue(strWidth,-1);
		this.strWidth = strWidth;
	}

	/**
	 * @param height the height to set
	 */
	public void setHeight(String strHeight) {
		this.height = Caster.toIntValue(strHeight,-1);
		this.strHeight = strHeight;
	}

	/**
	 * @param audioBitrate the audioBitrate to set
	 * @throws PageException 
	 */
	public void setAudiobitrate(String audioBitrate) throws PageException {
		this.audioBitrate = util.toBytes(audioBitrate);
	}

	/**
	 * @param aspectRatio the aspectRatio to set
	 * @throws PageException 
	 */
	public void setAspectratio(String strAspectRatio) throws PageException {
		strAspectRatio=strAspectRatio.trim().toLowerCase();
		if("16:9".equals(strAspectRatio)) aspectRatio=VideoProfile.ASPECT_RATIO_16_9;
		else if("4:3".equals(strAspectRatio)) aspectRatio=VideoProfile.ASPECT_RATIO_4_3;
		else throw doThrow("invalid aspect ratio definition ["+strAspectRatio+"], valid values are [16:9, 4:3]");
	}

	/**
	 * @param framerate the framerate to set
	 */
	public void setFramerate(double framerate) {
		setFps(framerate);
	}
	public void setFps(double framerate) {
		this.framerate = (int) framerate;
	}

	/**
	 * @param videoBitrate the videoBitrate to set
	 * @throws PageException 
	 */
	public void setVideobitrate(String videoBitrate) throws PageException {
		this.videoBitrate = util.toBytes(videoBitrate);
	}

	/**
	 * @param videoBitrateMin the videoBitrateMin to set
	 * @throws PageException 
	 */
	public void setVideobitratemin(String videoBitrateMin) throws PageException {
		this.videoBitrateMin = util.toBytes(videoBitrateMin);
	}

	/**
	 * @param videoBitrateMax the videoBitrateMax to set
	 * @throws PageException 
	 */
	public void setVideobitratemax(String videoBitrateMax) throws PageException {
		this.videoBitrateMax = util.toBytes(videoBitrateMax);
	}

	/**
	 * @param videoBitrateTolerance the videoBitrateTolerance to set
	 * @throws PageException 
	 */
	public void setVideobitratetolerance(String videoBitrateTolerance) throws PageException {
		this.videoBitrateTolerance = util.toBytes(videoBitrateTolerance);
	}

	/**
	 * @param author the author to set
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/**
	 * @param title the title to set
	 */
	public void setTitle(String title) {
		this.title = title;
	}

	/**
	 * @param comment the comment to set
	 */
	public void setComment(String comment) {
		this.comment = comment;
	}

	/**
	 * @param copyright the copyright to set
	 */
	public void setCopyright(String copyright) {
		this.copyright = copyright;
	}

	/**
	 * @param max the maxFrames to set
	 * @throws PageException 
	 */
	public void setMax(String strMax) throws PageException {
		strMax=strMax.trim().toLowerCase();
		if(strMax.endsWith("f")) 			this.maxFrame=Caster.toLongValue(strMax.substring(0,strMax.length()-1));
		else if(strMax.endsWith("ms"))	this.maxTime=Caster.toDoubleValue(strMax.substring(0,strMax.length()-2))/1000F;
		else if(strMax.endsWith("s"))		this.maxTime=Caster.toDoubleValue(strMax.substring(0,strMax.length()-1));
		else setStart(strMax+" ms");
	}

	/**
	 * @param result the result to set
	 */
	public void setResult(String result) {
		this.result = result;
	}



	/**
	 * @param source1 the source1 to set
	 * @throws PageException 
	 */
	public void setSource1(String source1) throws PageException {
		this.source1 = new VideoInputImpl(ResourceUtil.toResourceExisting(pageContext, source1));
	}



	/**
	 * @param source2 the source2 to set
	 * @throws PageException 
	 */
	public void setSource2(String source2) throws PageException {
		this.source2 = new VideoInputImpl(ResourceUtil.toResourceExisting(pageContext, source2));
	}



	/**
	 * @param strStart the position to set
	 * @throws PageException 
	 */
	public void setStart(String strStart) throws PageException {
		strStart=strStart.trim().toLowerCase();
		if(strStart.endsWith("f")) 			this.startFrame=Caster.toLongValue(strStart.substring(0,strStart.length()-1));
		else if(strStart.endsWith("ms"))	this.startTime=Caster.toDoubleValue(strStart.substring(0,strStart.length()-2))/1000F;
		else if(strStart.endsWith("s"))		this.startTime=Caster.toDoubleValue(strStart.substring(0,strStart.length()-1));
		else setStart(strStart+" ms");
	}

	/**
	 * @param videoCodec the videoCodec to set
	 */
	public void setVideocodec(String videoCodec) {
		this.videoCodec = videoCodec;
	}
	public void setAudiocodec(String audioCodec) {
		this.audioCodec = audioCodec;
	}
	
	class Pair {
		Resource res;
		Struct sct;
		public Pair(Resource res, Struct sct) {
			this.res = res;
			this.sct = sct;
		}
	}

	/**
	 * @param audiosamplerate the audiosamplerate to set
	 * @throws PageException 
	 */
	public void setAudiosamplerate(String sAudiosamplerate) throws PageException {
		this.audiosamplerate = util.toHerz(sAudiosamplerate);
	}

	public static void checkRestriction() {
		
	}
	
	/**
	 * @param debug the debug to set
	 */
	public void setDebug(String debug) {
		this.debug = debug;
	}

	/**
	 * @param buffersize the buffersize to set
	 */
	public void setBuffersize(double buffersize) {
		this.buffersize = (long) buffersize;
	}
	

	public Struct toStruct(VideoInfo[] infos) {
		Struct sct=new StructImpl();
		sct.setEL(KeyConstants._source, toStruct(infos[0]));
		sct.setEL(KeyConstants._destination, toStruct(infos[1]));
		return sct;
	}
	
	private Struct toStruct(VideoInfo info) {
		
		Struct sct=info.toStruct();
		
		// audio
		Struct audio=Caster.toStruct(sct.get(AUDIO,null),null);
		if(audio==null) {
			audio=new StructImpl();
			sct.setEL(AUDIO, audio);
		}
		
		// video
		Struct video=Caster.toStruct(sct.get(VIDEO,null),null);
		if(video==null) {
			video=new StructImpl();
			sct.setEL(VIDEO, video);
		}
		
		// Audio
		audio.setEL("channels", info.getAudioChannels());
		audio.setEL(KeyConstants._codec, info.getAudioCodec());
		if(info.getAudioBitrate()!=-1)audio.setEL("bitrate", new Double(info.getAudioBitrate()));
		if(info.getAudioSamplerate()!=-1)audio.setEL("samplerate", new Double(info.getAudioSamplerate()));
		
		// Video
		video.setEL(KeyConstants._codec, info.getVideoCodec());
		video.setEL(KeyConstants._format, info.getVideoFormat());
		if(info.getVideoBitrate()!=-1)video.setEL("bitrate", new Double(info.getVideoBitrate()));
		if(info.getFramerate()!=-1)video.setEL("framerate", new Double(info.getFramerate()));
		
		// Allgemein
		if(info.getDuration()!=-1)sct.setEL("duration", new Double(info.getDuration()));
		if(info.getHeight()!=-1)sct.setEL(KeyConstants._height, new Double(info.getHeight()));
		if(info.getWidth()!=-1)sct.setEL(KeyConstants._width, new Double(info.getWidth()));
		
		
		
		return sct;
	}
}