/*
 * 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; version 2
 * of the License only.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
//-----------------------------------------------------------------------------
// Renderer Option Script Executor
//-----------------------------------------------------------------------------
package net.pms.dlna;

//import java.io.*;
import java.io.File;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import net.pms.PMS;
import net.pms.configuration.PmsConfiguration;
import net.pms.configuration.RendererConfiguration;
import net.pms.configuration.MapFileConfiguration;
import net.pms.util.PMSUtil;
import net.pms.io.OutputParams;
import net.pms.encoders.Player;
import net.pms.dlna.rz_SessionInfo;


import bsh.EvalError;
import bsh.Interpreter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class rz_ScriptExecutor {
	private static final Logger logger = LoggerFactory.getLogger(rz_ScriptExecutor.class);

	RendererConfiguration renderer;  //need?
	private ArrayList<ScriptEnt> TransOptScriptList=new ArrayList<ScriptEnt>();
	private boolean TransOptScriptInited;
	private long ScriptLastModified;
	private long ScriptLastExecuted;
	private Interpreter bsh_interp;
	private String rendname;
	private rz_SessionInfo sess;

	//Script List Entry 
	public static class ScriptEnt implements Cloneable {
		public Object clone() throws CloneNotSupportedException {  //add, regzamod
			return super.clone();
		}
		public int type;  			//=1:load,=2:exec
		public int stat;			//=0:off,=1:on (current)
		public int stat_sv;			//=0:off,=1:on (original:defalut)
		public int when;			//=1:list,=2:play,  =3:list|play,  =4:oneshot
		public int target;			//=1:file,=2:folder,=3:file|folder,=4:other
		public int mgrp;			//=1:begin_menu_group,=2:inherit_menu_group,=0:no-menu_group
		public String mpath;
		public String mname;
		public String script;
	}
	ScriptEnt cur_scp= new ScriptEnt();
	int last_mgrp=0; //for count up
	
	public rz_ScriptExecutor(RendererConfiguration renderer, rz_SessionInfo sess) {
		this.renderer=renderer;
		this.rendname=(renderer==null?"":renderer.getRendererName());
		this.sess=sess;
	}
	
	public void init() {
		bsh_interp = new Interpreter();
		execScripts(1,0,0,null,"dummy_dlna",null,null);
	}
	
	public boolean isRz_TransOptScriptExist() {
		if(!TransOptScriptInited) return false;
		return (TransOptScriptList.size()>0);
	}
	public ArrayList<ScriptEnt> getRz_TransOptScriptList() {
		return TransOptScriptList;
	}
	public long getRz_ScriptLastModified() {
		return ScriptLastModified;
	}
	public void setRz_ScriptLastModified(long time) {
		ScriptLastModified=time;
	}
	
	public String getRz_TransOptScript(int type, int when, int target) {
		// int type;	//=1:load,=2:exec
		// int when;	//=1:list,=2:play,  =3:list|play,  =4:oneshot
		// int target;	//=1:file,=2:folder,=3:file|folder,=4:other

		if(!TransOptScriptInited) return null;
		StringBuilder slist=new StringBuilder();
		for(ScriptEnt scp: TransOptScriptList) {
			if(scp.script!=null && ( (scp.stat&0x01)==1 || (scp.stat&0x04)!=0 ) ) {
				if(type==1) { //init load
					if((scp.type&type)!=0) {
						slist.append(scp.script);
					}
				}
				//else if(when==4) {	//oneshot
				else if((when&0x4)!=0) {	//oneshot
					if((scp.type&type)!=0 && (scp.when&when)!=0) {
						slist.append(scp.script);
					}
				}
				else {	//exec 
					if((scp.type&type)!=0 && (scp.when&when)!=0 && (scp.target&target)!=0) {
						slist.append(scp.script);
					}
				}
			}
		}
		if(PMS.rz_debug>2) PMS.dbg("getRz_TransOptScript: type="+type
			+", when="+when
			+", target="+target
			+", script="+slist.toString());
		return slist.toString();
	}
	
	public void loadRz_TransOptScript(List<?> cond) {
		cur_scp.type= -1;
		cur_scp.stat=1; 
		cur_scp.stat_sv=1; 
		cur_scp.when=0; 
		cur_scp.target=0; 
		cur_scp.mgrp=0; 
		cur_scp.mname=null; 
		cur_scp.mpath=null; 
		String str;
		if(!TransOptScriptInited) { //1st read
			for (Object line : cond) {
				str=line.toString();
				if(str.trim().toLowerCase().startsWith("[file]")) {
					String fname=str.substring(6).trim();
					tosFile(fname);
				}
				else {
					cur_scp.type=2;  	//exec
					cur_scp.stat=1;  	//on
					cur_scp.stat_sv=1;  //on
					cur_scp.when=3; 	//list|play
					cur_scp.target=3; 	//file|folder
					cur_scp.mgrp=0; 
					cur_scp.mname=null;
					cur_scp.mpath=null;
					cur_scp.script=str+"\n";
					tos_put(cur_scp);
				}
			}
			TransOptScriptInited=true;
			setRz_ScriptLastModified(System.currentTimeMillis());
		}
		if(PMS.rz_debug>2) {
			PMS.dbg("---- loadRz_TransOptScript: entries count="+TransOptScriptList.size()+" ----");
			int	i=0;
			for(ScriptEnt scp: TransOptScriptList) {
				i++;
				PMS.dbg("Entry No."+i+", type="+scp.type+", stat="+scp.stat
					+", when="+scp.when+", target="+scp.target
					+", mname="+scp.mname+", mpath="+scp.mpath);
				PMS.dbg("Script=\n"+scp.script);
			}
		}
	}
	
	private void tosFile(String fname) {
		String str;
		String s;
		StringBuilder slist=new StringBuilder();
		cur_scp.type= -1;  //init by invalid type: means valid part isn't begun yet
		try{
			//BufferedReader br = new BufferedReader(new FileReader(new File(fname)));
			//Script file must be always UTF-8, regardless of Java's Default Encoding 
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fname),"UTF-8"));
			int pos,pos2,pos_next;
			String[] sep={",","=","\\s"};
			
			while((str=br.readLine())!= null){
				//str=str.trim();
				if(str.startsWith("#")) continue;
				if(str.contains("<@load>")) {	//<@load>
					if(cur_scp.type>0 && slist.length()>0) {
						cur_scp.script=slist.toString();
						tos_put(cur_scp);
						slist.delete(0, slist.length());
					}
					cur_scp.type=1;  //load
					cur_scp.stat=1;  //on
					cur_scp.stat_sv=1;  //on
					cur_scp.when=0; 
					cur_scp.target=0; 
					cur_scp.mgrp=0; 
					cur_scp.mname=null;
					cur_scp.mpath=null;
				}
				else if((pos=str.indexOf("<@exec"))>-1) {  //<@exec>
					if(cur_scp.type>0 && slist.length()>0) {  //flush previous load
						cur_scp.script=slist.toString();
						tos_put(cur_scp);
						slist.delete(0, slist.length());
					}
					cur_scp.type=2;  	//exec
					cur_scp.stat=1;  	//on
					cur_scp.stat_sv=1;  	//on
					cur_scp.when=3; 	//list|play
					cur_scp.target=3; 	//file|folder
					cur_scp.mgrp=0; 
					cur_scp.mname=null;
					cur_scp.mpath=null;
					
					pos2=0;
					if((pos2=str.lastIndexOf(">",str.length()-1))>-1) {
						str=str.substring(pos+6,pos2).trim();
					}
					else {
						str=str.substring(pos+6).trim();
					}
				
					pos=str.indexOf("stat=");
					if(pos>-1) {
						//DODO: this way can't handle when without comma(,) after the param
						pos2=str.indexOf(",",pos+5);
						if(pos2>-1) s=str.substring(pos+5,pos2).trim();
						else s=str.substring(pos+5).trim();
						if(s!=null) {
							cur_scp.stat=Integer.parseInt(s);
							cur_scp.stat_sv=cur_scp.stat;  //memory original value
						}
					}
					pos=str.indexOf("when=");
					if(pos>-1) {
						pos2=str.indexOf(",",pos+5);
						if(pos2>-1) s=str.substring(pos+5,pos2).trim();
						else s=str.substring(pos+5).trim();
						if(s!=null) cur_scp.when=Integer.parseInt(s);
					}
					pos=str.indexOf("target=");
					if(pos>-1) {
						pos2=str.indexOf(",",pos+7);
						if(pos2>-1) s=str.substring(pos+7,pos2).trim();
						else s=str.substring(pos+7).trim();
						if(s!=null) cur_scp.target=Integer.parseInt(s);
					}
					pos=str.indexOf("mgrp=");
					int rgp=0;
					if(pos>-1) {
						pos2=str.indexOf(",",pos+5);
						if(pos2>-1) s=str.substring(pos+5,pos2).trim();
						else s=str.substring(pos+5).trim();
						if(s!=null) rgp=Integer.parseInt(s);
						if(rgp==1) {  //new group
						 	last_mgrp++;
						 	cur_scp.mgrp=last_mgrp;
						}
						else if(rgp==2) {  //inherit current group
							cur_scp.mgrp=last_mgrp;
						}
						else {
							cur_scp.mgrp=0;
						}
					}
					if((pos=str.indexOf("mname="))>-1) { 
						s=str.substring(pos+6).trim();
						if(s.startsWith("\"")) {  // quoted by " "
							pos2=s.indexOf("\"",1);
							if(pos2>1) {
								cur_scp.mname=s.substring(1,pos2).trim();
							}
						}
						else if((pos2=str.indexOf(",",pos+6))>-1) {
							cur_scp.mname=str.substring(pos+6,pos2).trim();
						}
						else {
							cur_scp.mname=str.substring(pos+6).trim();
						}
					}
				}
				else {
					slist.append(str+"\n");
				}
			}
			if(cur_scp.type>0 && slist.length()>0) {
				cur_scp.script=slist.toString();
				tos_put(cur_scp);
				slist.delete(0, slist.length());
			}
			br.close();
		}catch(FileNotFoundException e){
			logger.error("getRz_TransOptScript:FileNotFoundException",e);
	    }catch(IOException e){
			logger.error("getRz_TransOptScript:IOException",e);
		}
	}

	private void tos_put(ScriptEnt scp_i) {
		ScriptEnt scp=null;
		String[] menu_path=null;
		try {
			scp=(ScriptEnt)scp_i.clone();
		} catch (CloneNotSupportedException e1) {
			logger.error("tos_put: clone creation error: " + e1.getMessage());
		}
		
		if(true) {
			if(scp.mname!=null) {
				//menu_path=menu_name.split("(?!\\)/");
				menu_path=scp.mname.split("/");
				//PMS.dbg("menu_name="+menu_name);
				scp.mname=menu_path[menu_path.length-1];
				scp.mpath="";
				for(int i=0;i<menu_path.length-1;i++) {
					scp.mpath+=menu_path[i]+"/";
				}
			}
		} else {
			if(scp.mname!=null) {
				//TODO: this way can't handle "/" in name of menu & menu_folder 
				File f=new File(scp.mname);
				scp.mname=f.getName();
				scp.mpath=f.getParent();
			}
		}
		if(scp.mpath!=null && scp.mpath.length()<=0) scp.mpath=null;

		if(PMS.rz_debug>1) {
			PMS.dbg("tos_put:---- TransOptScriptList entry ----");
			PMS.dbg("tos_put:type="+scp.type
				+", stat="+scp.stat
				+", when="+scp.when
				+", target="+scp.target
				+", mgrp="+scp.mgrp
				+", memu_name="+scp.mname
				+", memu_path="+scp.mpath
			);
			PMS.dbg("tos_put:script="+scp.script);
		}
		TransOptScriptList.add(scp);
	}

	public static class medAddOpts { // for getSpecificCodecOptions()
		public Float adelay_add=null;
		public Float adelay_rep=null;
		public String player  = "unknown";
		public String trans_type  = "unknown";
		public String trans_format= "unknown";
		public String remux_format= "unknown";
	}
	
	public static class ScParams_i { // input params of interpreter
		public int when;
		public int stat;
		public String filename;
		public String dpname;
		public String container;
		public String vcodec;
		public String acodec;
		public double bitrate;
		public double framerate;
		public double duration;
		public double samplerate;
		int channels;
		public int width;
		public int height;
		public String player;
		public String trans_type;
		public String trans_format;
		//public String sys_trans_format_web;
		public String remux_format;
		public String trans_proc;
		public String remux_proc;
		public String trans_prog;
		public String remux_prog;
		boolean audio;
		boolean subtitles;
		
	}
	
	private void ScParamsInit_i(ScParams_i inp) { // input params of interpreterf
		inp.when= -1;
		inp.stat= 0;
		inp.filename="";
		inp.dpname="";
		inp.container="";
		inp.vcodec="";
		inp.acodec="";
		inp.bitrate=0;
		inp.framerate=0;
		inp.duration=0;
		inp.samplerate=0;
		inp.channels=0;
		inp.width=0;
		inp.height=0;
		inp.player="";
		inp.trans_type="";
		inp.trans_format="";
		//inp.sys_trans_format_web="";
		inp.remux_format="";
		inp.trans_proc="";
		inp.trans_prog="";
		inp.remux_proc="";
		inp.remux_prog="";
		inp.audio=false;
		inp.subtitles=false;
	}

	public static class ScParams_o { // output params of interpreterf
		public String player;
		public String trans_type;
		public String trans_format;
		//public String sys_trans_format_web;
		public String sys_web_opt;
		public String sys_aav_opt;
		public String sys_iav_opt;
		public String remux_format;
		public String trans_proc;
		public String remux_proc;
		public String ffmpeg_opt;
		public String tsmuxer_opta;
		public String tsmuxer_optv;
		public String mencoder_opt;
		public String mencoder_opta;
		public float  sound_delay;
		public float  zoom;
		public String dlna_prof;
		public String dlna_op;
		public String mime_type;
		public String dpname;
	}
	
	private void ScParamsInit_o(ScParams_o out) { //
		out.player="";
		out.trans_type="";
		out.trans_format="";
		//out.sys_trans_format_web="";
		out.sys_web_opt="";
		out.sys_aav_opt="";
		out.sys_iav_opt="";
		out.remux_format="";
		out.trans_proc="";
		out.remux_proc="";
		out.trans_proc="";
		out.ffmpeg_opt="";
		out.tsmuxer_opta="";
		out.tsmuxer_optv="";
		out.mencoder_opt="";
		out.mencoder_opta="";
		out.sound_delay=0;
		out.zoom= -1;
		out.dlna_prof="";
		out.dlna_op="";
		out.mime_type="";
		out.dpname="";
	}

	public void execScripts(
		int type,int when,int target, 
		DLNAResource dlna,
		String filename, 
		OutputParams params, 
		medAddOpts opts) {

		if(PMS.rz_debug>1) {
			PMS.dbg("execScripts: called: type="+type+", when="+when+", target="+target
				+", dlna="+dlna
				+", filename="+filename);
		}
		
		String script=null;
		script=getRz_TransOptScript(type,when,target);
		if(script==null||script.length()==0) {
			//PMS.dbg("execScripts: no according script, return");
			return;
		}
		//PMS.dbg("execScripts: found according script, exec");
		exec_scripts(when,script,dlna,filename,params,opts,null);
	}
	
	public void execScripts_s (  //target single scp
		int type,int when,int target, 
		DLNAResource dlna,
		String filename, 
		OutputParams params, 
		medAddOpts opts,
		ScriptEnt scp
		) {

		if(PMS.rz_debug>1) {
			PMS.dbg("execScripts: called: type="+type+", when="+when+", target="+target
				+", dlna="+dlna
				+", filename="+filename);
		}
		
		String script=null;
		//script=getRz_TransOptScript(type,when,target);
		script=scp.script;
		if(script==null||script.length()==0) {
			//PMS.dbg("execScripts: no according script, return");
			return;
		}
		//PMS.dbg("execScripts: found according script, exec");
		exec_scripts(when,script,dlna,filename,params,opts,scp);
	}

	private void exec_scripts(int when, String script, DLNAResource dlna,
		String filename, OutputParams params, medAddOpts opts,ScriptEnt scp) {
		
		StringBuilder sb = new StringBuilder();
		DLNAMediaInfo media=null;
		
		if(dlna!=null) media=dlna.getMedia();
		int mode=0;
		Interpreter interpreter=bsh_interp;

		String str=null;
		String scripts = ""+ script;
		//StringTokenizer stLines = new StringTokenizer(codecs, "\n");
		try {
			//-----------------------------------------------------------------
			// set input params
			//-----------------------------------------------------------------
			interpreter.setStrictJava(false);
			if(false) {  //test for object input/output,--> seems Bsh not support get()/set() user_class obj
				ScParams_i inp=new ScParams_i();
				ScParamsInit_i(inp);
				inp.when=when;
				inp.filename=filename;
				if(dlna!=null) {
					if(dlna.getForcedTitle()!=null) {
						inp.dpname=dlna.getForcedTitle();
					}
					else {
						inp.dpname=dlna.getDisplayName();
					}
					//inp.dpname=dlna.getDpName();
					//---- video
					if(opts!=null) {
						inp.player=opts.player;
						inp.trans_type=opts.trans_type;
						inp.trans_format=opts.trans_format;
						inp.remux_format=opts.remux_format;
					}
					//---- video
					if(media!=null) {
						inp.container=media.getContainer()!=null?media.getContainer():"";
						inp.vcodec=media.getCodecV()!=null?media.getCodecV():"";
						String framerate=media!=null?media.getFrameRate():null;	//regzamod
						if(framerate != null) inp.framerate=Double.parseDouble(framerate);
						inp.duration=media.getDurationInSeconds();
						inp.width=media.getWidth();
						inp.height=media.getHeight();
						
					}
					//---- audio
					DLNAMediaAudio aid=null;
					if(params!=null) aid=params.aid;
					else {
						//aid=dlna.getMediaAudio();
						if(media!=null && media.getAudioCodes().size()>0) {
							aid=media.getAudioCodes().get(0);
						}
					}
					if(aid!=null) {
						inp.audio=true;
						//inp.acodec=aid.getAudioCodec();
						inp.acodec=aid.getCodecA();
						inp.samplerate=aid.getSampleRate();
						inp.channels=aid.getNrAudioChannels();
					}
					//---- subtitles
					DLNAMediaSubtitle sid=null;
					if(params!=null) sid=params.sid;
					else if(dlna!=null) sid=dlna.getMediaSubtitle();
					if(sid!=null) {
						inp.subtitles=(sid != null);
					}
				}
				interpreter.set("inp",inp);
				ScParams_o out=new ScParams_o();
				ScParamsInit_o(out);
				interpreter.set("out",out);
			}
			else {
				if(scp!=null) {  //single script
					interpreter.set("inp.stat", scp.stat);
				}
				else {
					interpreter.set("inp.stat", 0);
				}
				interpreter.set("inp.when", when); //=1:list, =2:play
				interpreter.set("inp.rendname", this.rendname);
				interpreter.set("inp.filename", filename==null?"":filename);
				if(dlna!=null) {
					interpreter.set("inp.dpname", dlna.getDisplayName());
					if(dlna.isFolder()) {
						interpreter.set("inp.is_folder", true);
					}
					else {
						interpreter.set("inp.is_folder", false);
					}
					if(dlna.getExt()!=null) {
						if(dlna.getExt().isVideo()) {
							interpreter.set("inp.is_video", true);
						}
						else {
							interpreter.set("inp.is_video", false);
							if(dlna.getExt().isAudio()) {
								interpreter.set("inp.is_audio", true);
							}
							else {
								interpreter.set("inp.is_audio", false);
								if(dlna.getExt().isImage()) {
									interpreter.set("inp.is_image", true);
								}
								else {
									interpreter.set("inp.is_image", false);
								}
							}
						}
					}
					else {
						interpreter.set("inp.is_video", false);
						interpreter.set("inp.is_audio", false);
						interpreter.set("inp.is_image", false);
					}
				}
				else {
					interpreter.set("inp.is_folder", false);
					interpreter.set("inp.dpname", "unknown");
					interpreter.set("inp.is_video", false);
					interpreter.set("inp.is_audio", false);
					interpreter.set("inp.is_image", false);
				}
				if(media!=null) {
					interpreter.set("inp.container",media.getContainer()!=null?media.getContainer():"");
					interpreter.set("inp.vcodec",media.getCodecV()!=null?media.getCodecV():"");
					interpreter.set("inp.bitrate",media.getBitrate());
				}
				else {
					interpreter.set("inp.container","unknown");
					interpreter.set("inp.vcodec","unknown");
					interpreter.set("inp.bitrate",0);
				}
				if(params!=null) {
					interpreter.set("inp.timeseek",params.timeseek);
				}
				else {
					interpreter.set("inp.timeseek",0.0);
				}
				
				DLNAMediaAudio aid=null;
				if(params!=null) {
					aid=params.aid;
				}
				else {
					//PMS.dbg("exec_scripts: when="+when+", media="+media);
					//aid=dlna.getMediaAudio();
					if(media!=null && media.getAudioCodes().size()>0) {
						aid=media.getAudioCodes().get(0);
						//PMS.dbg("exec_scripts: when="+when+", getAudioCodes()="+media.getAudioCodes()+", aid="+aid);
					}
				}
				//DLNAMediaAudio aid=params!=null?params.aid:dlna.getMediaAudio();
				if(aid!=null) {
					interpreter.set("inp.audio", true);
					//interpreter.set("inp.acodec",aid.getAudioCodec());
					interpreter.set("inp.acodec",aid.getCodecA());
					interpreter.set("inp.samplerate",aid.getSampleRate());
					interpreter.set("inp.channels",aid.getNrAudioChannels());
				}
				else {
					interpreter.set("inp.audio", false);
					interpreter.set("inp.acodec","");
					interpreter.set("inp.samplerate", 0);
					interpreter.set("inp.channels", 0);
				}
				
				DLNAMediaSubtitle sid=null;
				if(params!=null) sid=params.sid;
				else if(dlna!=null) sid=dlna.getMediaSubtitle();
				//DLNAMediaSubtitle sid=params!=null?params.sid:dlna.getMediaSubtitle();
				interpreter.set("inp.subtitles", sid != null);
				//interpreter.set("srtfile", srtFileName);
			}
			
			try {
				//String framerate = media.getValidFps(false);	//regzamod
				// media.getValidFps doesn't return correct value
				String framerate = media!=null?media.getFrameRate():null;	//regzamod
				if (framerate != null) {
					interpreter.set("inp.framerate", Double.parseDouble(framerate));
				}
				else {
					interpreter.set("inp.framerate", 0.0);
				}
			} catch (NumberFormatException e) {
			}

			if(media!=null) {
				interpreter.set("inp.duration", media.getDurationInSeconds());
				interpreter.set("inp.height", media.getHeight());
				interpreter.set("inp.width", media.getWidth());
			}
			else {
				interpreter.set("inp.duration", 0.0);
				interpreter.set("inp.height", 0);
				interpreter.set("inp.width", 0);
			}
			
			//medAddOpts
			if(opts!=null) {
				interpreter.set("inp.player", opts.player);
				interpreter.set("inp.trans_type",   opts.trans_type);
				interpreter.set("inp.trans_format", opts.trans_format);
				interpreter.set("inp.remux_format", opts.remux_format);
			}
			else {
				interpreter.set("inp.player", "unknown");
				interpreter.set("inp.trans_type",   "unknown");
				interpreter.set("inp.trans_format", "unknown");
				interpreter.set("inp.remux_format", "unknown");
			}
			
			//-----------------------------------------------------------------
			// init output params 
			//-----------------------------------------------------------------
			//system common
			//interpreter.set("out.sys_trans_format_web", "");
			//PMS.dbg("ScriptExecutor: set rz_SysMetaWebOpt="+PMS.rz_SysMetaWebOpt);
			/*
			interpreter.set("out.sys_web_opt", PMS.rz_SysMetaWebOpt);
			interpreter.set("out.sys_aav_opt", PMS.rz_SysMetaAAVOpt);
			interpreter.set("out.sys_iav_opt", PMS.rz_SysMetaIAVOpt);
			interpreter.set("out.sys_direct_seek", "");
			*/
			interpreter.set("out.sys_web_opt", sess.rz_SysMetaWebOpt);
			interpreter.set("out.sys_aav_opt", sess.rz_SysMetaAAVOpt);
			interpreter.set("out.sys_iav_opt", sess.rz_SysMetaIAVOpt);
			interpreter.set("out.sys_direct_seek", "");
			
			//per dlna
			interpreter.set("out.player", "");
			interpreter.set("out.trans_type", "");
			interpreter.set("out.trans_format", "");
			interpreter.set("out.trans_format_web", "");
			interpreter.set("out.remux_format", "");
			interpreter.set("out.trans_proc", "");
			interpreter.set("out.trans_prog", "");
			interpreter.set("out.remux_proc", "");
			interpreter.set("out.remux_prog", "");
			interpreter.set("out.ffmpeg_opt", "");
			interpreter.set("out.tsmuxer_opta", "");
			interpreter.set("out.tsmuxer_optv", "");
			interpreter.set("out.mencoder_opt", "");
			interpreter.set("out.mencoder_opta", "");
			interpreter.set("out.dlna_prof", "");
			interpreter.set("out.dlna_op", "");
			interpreter.set("out.mime_type", "");
			interpreter.set("out.dpname", "");
			interpreter.set("out.sound_delay", 0.0);
			interpreter.set("out.zoom", -1.0);
			interpreter.set("out.meta_flags", -1);
			//interpreter.set("out.direct_seek", -1);
			
			//-----------------------------------------------------------------
			// exec scripts
			//-----------------------------------------------------------------
			long mod_time=getRz_ScriptLastModified();
			long exe_time_prev=ScriptLastExecuted;  		//memry prev executed time
			ScriptLastExecuted=System.currentTimeMillis(); 	//update executed time
			
			try {
				Object result = interpreter.eval(scripts);
			} catch (Throwable e) {
				logger.warn("======================================================" );
				logger.warn("BeanShell Error while executing: " + scripts + " : ");
				logger.warn(e.getMessage());
				logger.warn("======================================================" );
			}

			//-----------------------------------------------------------------
			// get output params 
			//-----------------------------------------------------------------
			//if(dlna==null || mod_time>exe_time_prev) { 
				// judging mod_time>exe_time_prev -->don't work well!!
			//if(dlna==null) { 
				// sys common value will be not got when==2(play target), 
				// so script that change common value should immediately exec when==4 (button changed)
			if(dlna==null || true) {  // most versatile, but more cpu load
				//----------------------------------------------------------------------
				// must exec even if dlna!=null 
				// for some script that need update system common value when play target (i.e. dlna!=null)
				// DoDO: as for cpu load, you should consider better way
				//----------------------------------------------------------------------
				//dlna==null: means for system commnon, not for per DLNA object
				//sys_xxx means system common value, not per dlna value
				//ToDO: these params should be per_session instead of system_common
				//-----------------------------------------------------------------
				//In case of system commnon : value reset will be NOT done by DLNAResources
				//, so reset should be done by me: str==null indicate value resetted
				//In case of per DLNA object: value reset will be done by DLNAResources when any script menu activated
				//, so reset needn't be done by me
				//-----------------------------------------------------------------
				
				//---- system common
				str=(String)interpreter.get("out.sys_direct_seek");
				if(str!=null && str.length()>0) PMS.rz_direct_seek=str.equals("true")?true:false;
				
				/* moved from system_commn to per session_common
				str=(String)interpreter.get("out.sys_web_opt");
				PMS.rz_SysMetaWebOpt=str;
				str=(String)interpreter.get("out.sys_aav_opt");
				PMS.rz_SysMetaAAVOpt=str;
				str=(String)interpreter.get("out.sys_iav_opt");
				PMS.rz_SysMetaIAVOpt=str;
				//PMS.dbg("ScriptExecutor: got rz_SysMetaWebOpt="+PMS.rz_SysMetaWebOpt);
				str=(String)interpreter.get("out.sys_direct_seek");
				*/
				
				//---- session common
				str=(String)interpreter.get("out.sys_web_opt");
				sess.rz_SysMetaWebOpt=str;
				str=(String)interpreter.get("out.sys_aav_opt");
				sess.rz_SysMetaAAVOpt=str;
				str=(String)interpreter.get("out.sys_iav_opt");
				sess.rz_SysMetaIAVOpt=str;
				
			}
			if(dlna!=null) { // for per object input/output
				str=(String)interpreter.get("out.player");
				if(str!=null && str.length()>0) {
					Player pl=PMS.get().getPlayer(str);
					if(pl!=null) { 
						if(dlna!=null) dlna.player3=pl;
					}
				}
				
				str=(String)interpreter.get("out.trans_type");
				//str=out.trans_type;
				if(PMS.rz_debug>2) PMS.dbg("execScripts: get trans_type="+str+", current rz_MetaTransType="+dlna.rz_MetaTransType[1]);
				if(str!=null && str.length()>0) {	// {direct|remux|trans}
					if(str.equals("direct")) 		dlna.rz_MetaTransType[1]=PMS.ENC_DIRECT;
					else if(str.equals("remux"))	dlna.rz_MetaTransType[1]=PMS.ENC_REMUX;
					else if(str.equals("trans"))	dlna.rz_MetaTransType[1]=PMS.ENC_TRANSCODE;
					else {
						logger.warn("illegal trans_type="+str);
					}
				}
				else {
					//shouldn't reset here! b/c scripts may be not always same when=1(list) and 2(play) 
					//otherwise value setted when=1 will be resetted if same script not enabled when=2 
					//reset should be done when=1(list) in DLNAResource.getDLNAResources()
					//dlna.rz_MetaTransType[1]=-1;
				}
				
				str=(String)interpreter.get("out.trans_format");
				if(str!=null && str.length()>0) {
					if(str.equals("mpegps")) 		dlna.rz_MetaTransFormat[1]=PMS.TRS_MPEGPS;
					else if(str.equals("mpegts"))	dlna.rz_MetaTransFormat[1]=PMS.TRS_MPEGTS;
					else if(str.equals("mpegts2"))	dlna.rz_MetaTransFormat[1]=PMS.TRS_MPEGTS2;
					else if(str.equals("m2ts"))		dlna.rz_MetaTransFormat[1]=PMS.TRS_M2TS;
					else {
						logger.warn("illegal trans_format="+str);
					}
				}
				str=(String)interpreter.get("out.trans_format_web");
				if(str!=null && str.length()>0) {
					if(str.equals("mpegps")) 		dlna.rz_MetaTransFormatWeb[1]=PMS.TRS_MPEGPS;
					else if(str.equals("mpegts"))	dlna.rz_MetaTransFormatWeb[1]=PMS.TRS_MPEGTS;
					else if(str.equals("mpegts2"))	dlna.rz_MetaTransFormatWeb[1]=PMS.TRS_MPEGTS2;
					else if(str.equals("m2ts"))		dlna.rz_MetaTransFormatWeb[1]=PMS.TRS_M2TS;
					else {
						logger.warn("illegal trans_format_web="+str);
					}
				}
				
				str=(String)interpreter.get("out.remux_format");
				if(str!=null && str.length()>0) {
					//PMS.dbg("execScripts: remux_format="+str);
					if(str.equals("mpegts")) 		dlna.rz_MetaRemuxFormat[1]=PMS.TRS_MPEGPS;
					else if(str.equals("m2ts"))		dlna.rz_MetaRemuxFormat[1]=PMS.TRS_M2TS;
					else {
						logger.warn("illegal remux_format="+str);
					}
				}
				
				str=(String)interpreter.get("out.remux_proc");
				if(str!=null && str.length()>0) dlna.rz_MetaRemuxProc[1]=Integer.parseInt(str);
				str=(String)interpreter.get("out.trans_proc");
				if(str!=null && str.length()>0) dlna.rz_MetaTransProc[1]=Integer.parseInt(str);

				str=(String)interpreter.get("out.remux_prog");
				if(str!=null && str.length()>0) dlna.rz_MetaRemuxProg[1]=str;
				str=(String)interpreter.get("out.trans_prog");
				if(str!=null && str.length()>0) dlna.rz_MetaTransProg[1]=str;

				//str=(String)interpreter.get("out.ffmpeg_path");
				//if(str!=null && str.length()>0) dlna.rz_MetaFFmpegPath[1]=str;
				
				str=(String)interpreter.get("out.ffmpeg_opt");
				if(str!=null && str.length()>0) dlna.rz_MetaFFmpegOpt[1]=str+" ";  // " " for later add to tail
				
				str=(String)interpreter.get("out.tsmuxer_opta");
				if(str!=null && str.length()>0) dlna.rz_MetaTsmuxerOptA[1]=str+" ";  // " " for later add to tail
				
				str=(String)interpreter.get("out.tsmuxer_optv");
				if(str!=null && str.length()>0) dlna.rz_MetaTsmuxerOptV[1]=str+" ";  // " " for later add to tail


				str=(String)interpreter.get("out.mencoder_opt");
				if(str!=null && str.length()>0) dlna.rz_MetaMencoderOpt[1]=str+" ";
				
				str=(String)interpreter.get("out.mencoder_opta");
				if(str!=null && str.length()>0) dlna.rz_MetaMencoderOptA[1]=str+" ";
				
				//str=(String)interpreter.get("out.sys_web_opt");
				//if(str==null || str.length()>=0) PMS.rz_SysMetaWebOpt=str+" ";
				//PMS.dbg("ScriptExecutor: PMS.rz_SysMetaWebOpt="+PMS.rz_SysMetaWebOpt);
				
				str=(String)interpreter.get("out.dlna_prof");
				if(str!=null && str.length()>0) dlna.rz_MetaDlnaProf[1]=str;
				
				str=(String)interpreter.get("out.dlna_op");
				if(str!=null && str.length()>0) dlna.rz_MetaDlnaOp[1]=str;
				
				str=(String)interpreter.get("out.dpname");
				if(str!=null && str.length()>0) dlna.rz_MetaTitle[1]=str;
				
				double sound_delay=(Double)interpreter.get("out.sound_delay");
				dlna.rz_SoundDelay[1]=sound_delay;
				
				double zoom=(Double)interpreter.get("out.zoom");
				dlna.rz_MetaZoom[1]=zoom;
				
				//int meta_flags=(int)interpreter.get("out.meta_flags");
				//if(meta_flags>=0) dlna.rz_MetaFlags[1]=meta_flags;
				
				//int direct_seek=(int)interpreter.get("out.direct_seek");
				//if(direct_seek==1) dlna.rz_MetaFlags[1]|=dlna.META_DIRECT_SEEK;
				//else if(direct_seek==0) dlna.rz_MetaFlags[1]&=~(dlna.META_DIRECT_SEEK);
				//PMS.dbg("dlna.rz_MetaFlags[1]="+dlna.rz_MetaFlags[1]);
			}

			//if(dlna!=null) PMS.dbg("execScripts: rz_MetaRemuxProg="+dlna.rz_MetaRemuxProg[1]);
			
			if(PMS.rz_debug>1 && dlna!=null) {
				PMS.dbg("execScripts: filename="+filename);
				PMS.dbg("execScripts: container="+(String)interpreter.get("container")
					+", vcovec="+(String)interpreter.get("vcodec")
					+", acovec="+(String)interpreter.get("acodec"));
				PMS.dbg("execScripts: rz_MetaTransType="+dlna.rz_MetaTransType[1]);
				PMS.dbg("execScripts: rz_MetaTransFormat="+dlna.rz_MetaTransFormat[1]);
				PMS.dbg("execScripts: rz_MetaRemuxFormat="+dlna.rz_MetaRemuxFormat[1]);
				PMS.dbg("execScripts: rz_MetaTransProc="+dlna.rz_MetaTransProc[1]);
				PMS.dbg("execScripts: rz_MetaRemuxProc="+dlna.rz_MetaRemuxProc[1]);
				PMS.dbg("execScripts: rz_MetaTransProg="+dlna.rz_MetaTransProg[1]);
				PMS.dbg("execScripts: rz_MetaRemuxProg="+dlna.rz_MetaRemuxProg[1]);
				//PMS.dbg("execScripts: rz_MetaFFmpegPath="+dlna.rz_MetaFFmpegPath[1]);
				
				PMS.dbg("execScripts: rz_MetaFFmpegOpt="+dlna.rz_MetaFFmpegOpt[1]);
				PMS.dbg("execScripts: rz_MetaMencoderOpt="+dlna.rz_MetaMencoderOpt[1]);
				PMS.dbg("execScripts: rz_MetaMencoderOptA="+dlna.rz_MetaMencoderOptA[1]);
				PMS.dbg("execScripts: rz_SysMetaWebOpt="+sess.rz_SysMetaWebOpt);
				PMS.dbg("execScripts: rz_SoundDelay="+dlna.rz_SoundDelay[1]);
				PMS.dbg("execScripts: rz_MetaTitle="+dlna.rz_MetaTitle[1]);
				PMS.dbg("execScripts: sys.rz_SysMetaTransFormatWeb="+sess.rz_SysMetaTransFormatWeb);
			}
			
		} catch (EvalError e) {
			logger.warn("\n==== BeanShell error: ==========================" );
			logger.warn(e.getMessage());
			logger.warn("\n================================================" );
		}
		return;
	}
	
	public static String trans_type_int2string(int trans_type) {
		if(trans_type==PMS.ENC_DIRECT) return "direct";
		if(trans_type==PMS.ENC_REMUX) return "remux";
		if(trans_type==PMS.ENC_TRANSCODE) return "trans";
		return "unknown";
	}
	public static String trans_format_int2string(int trans_format) {
		if(trans_format==PMS.TRS_MPEGPS) return "mpegps";
		if(trans_format==PMS.TRS_MPEGTS) return "mpegts";
		if(trans_format==PMS.TRS_MPEGTS2) return "mpegts2";
		if(trans_format==PMS.TRS_M2TS) return "m2ts";
		return "unknown";
	}

}