package net.pms.configuration;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.util.Arrays;

import net.pms.PMS;	//regzamod
import net.pms.Messages;
import net.pms.dlna.MediaInfoParser;
import net.pms.dlna.RootFolder;
import net.pms.dlna.rz_SessionCtl;
import net.pms.dlna.rz_SessionInfo;
import net.pms.formats.Format;
import net.pms.network.HTTPResource;
import net.pms.network.SpeedStats;
import net.pms.util.PmsProperties;
import net.pms.util.PMSUtil;	//regzamod
import net.pms.configuration.PmsConfiguration;


import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.ConversionException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jna.Platform;

//---- regzamod add start 
final class CondSizeAsp {
	public int w_cond;	// width condition 
	public int w_value;	
	public int h_cond;	// height condition 
	public int h_value;
	public int a_cond;	// aspect_ratio condition 
	public float a_value;
	public int f_cond;	// file_type
	public String f_value;
	public int t_cond;	// file_type negative
	public String t_value;
}
//---- regzamod add end 

//public class RendererConfiguration {
public class RendererConfiguration implements Cloneable {  //regzamod
	/*
	 * Static section
	 */
	private static final Logger logger = LoggerFactory.getLogger(RendererConfiguration.class);
	private static ArrayList<RendererConfiguration> renderersConfs;
	private static RendererConfiguration defaultConf;
	private static Map<InetAddress, RendererConfiguration> addressAssociation = new HashMap<InetAddress, RendererConfiguration>();
	private static Map<InetSocketAddress, RendererConfiguration> addressAssociation_s = new HashMap<InetSocketAddress, RendererConfiguration>();
	
	private static PmsProperties projectProperties = new PmsProperties();
	public ArrayList<CondSizeAsp> CondSizeAspList= new ArrayList<CondSizeAsp>();	//regzamod add
	private int trans_vformat;
	private int remux_vformat;
	private int trans_vformat_web;
	private int trans_vformat_aav;
	public rz_SessionInfo sess;
	private PmsConfiguration pconf=PMS.getConfiguration();
	
	public static RendererConfiguration getDefaultConf() {
		return defaultConf;
	}

	public Object clone() throws CloneNotSupportedException {  //add, regzamod
		return super.clone();
	}
	
	public static void loadRendererConfigurations() {
		renderersConfs = new ArrayList<RendererConfiguration>();
		try {
			defaultConf = new RendererConfiguration();
			defaultConf.setRZ_AutoSearchLock(2);  //all lock
		} catch (ConfigurationException e) {
		}

		try {
			// Read project properties resource file.
			projectProperties.loadFromResourceFile("/resources/project.properties");
		} catch (IOException e) {
			logger.error("Error loading renderer configurations: could not load project.properties");
			return;
		}

		File renderersDir = new File(projectProperties.get("project.renderers.dir"));

		if (renderersDir.exists() && renderersDir.isDirectory()) {
			logger.info("Loading renderer configurations from " + renderersDir.getAbsolutePath());

			File[] confs = renderersDir.listFiles();
			int rank = 1;
			for (File f : confs) {
				if (f.getName().endsWith(".conf")) {
					try {
						logger.info("Loading configuration file: " + f.getName());
						RendererConfiguration r = new RendererConfiguration(f);
						r.rank = rank++;
						renderersConfs.add(r);
					} catch (ConfigurationException ce) {
						logger.info("Error in loading configuration of: " + f.getAbsolutePath());
					}

				}
			}
		}
	}

	//private RootFolder rootFolder;
	public RootFolder rootFolder;  //regzamod

	public static void resetAllRenderers() {
		for(RendererConfiguration rc  : renderersConfs) {
			rc.rootFolder = null;
		}
	}

	public RootFolder getRootFolder() {
		if (rootFolder == null) {
			//rootFolder = new RootFolder();
			rootFolder = new RootFolder(this,sess);
			rootFolder.discoverChildren();
		}
		return rootFolder;
	}

	/**
	 * Associate an IP address with this renderer. The association will
	 * persist between requests, allowing the renderer to be recognized
	 * by its address in later requests.
	 * @param sa The IP address to associate.
	 * @see #getRendererConfigurationBySocketAddress(InetAddress)
	 */
	/*
	public void associateIP(InetAddress ia) {//origin
		addressAssociation.put(ia, this);
		SpeedStats.getInstance().getSpeedInMBits(ia, getRendererName());
	}
	*/
	public void associateIP(InetSocketAddress sa) {  //regzam
		addressAssociation.put(sa.getAddress(), this);  //HostAddr
		SpeedStats.getInstance().getSpeedInMBits(sa.getAddress(), getRendererName());
	}

	/*
	public static RendererConfiguration getRendererConfigurationBySocketAddress(InetAddress ia) {  //origin
		return addressAssociation.get(ia);
	}
	*/
	public static RendererConfiguration getRendererConfigurationBySocketAddress(InetAddress ia) { //regzam
		//PMS.dbg("getRendererConfigurationBySocketAddress: rz_rend_judge_type="+PMS.rz_rend_judge_type
		//	+", myAddr="+PMS.mySockAddr+", given ia="+ia);
		
		if(PMS.rz_rend_judge_type==0) {  //judge by [SocketAddress]
			return addressAssociation.get(ia);
		}
		else if(PMS.rz_rend_judge_type==1) {	//judge by [SocketAddress+UAH] only for devs on myHost
			if(ia.equals(PMS.myHostAddr)) {  //dev on myHost
				return null;  //can't judge by Address only: need match uah
			}
			else {
				return addressAssociation.get(ia);
			}
		}
		else {  //PMS.rz_rend_judge_type==2 :judge by [SocketAddress+UAH] for All devs
			return null;	//can't judge by SocketAddress
		}
	}

	public static RendererConfiguration getRendererConfigurationByUA(String userAgentString) {
		return getRendererConfigurationByUA(userAgentString,null);
	}
	
	//regzamod, add ia checking if defined
	public static RendererConfiguration getRendererConfigurationByUA(String userAgentString,InetAddress ia) {
		for (RendererConfiguration r : renderersConfs) {
			if (r.matchUserAgent(userAgentString)) {
				//PMS.dbg("getRendererConfigurationByUA: Found mached UA");
				if(r.isRZ_IPmatch(ia)) {
					//PMS.dbg("getRendererConfigurationByUA: return r="+r);
					return manageRendererMatch(r);
				}
				else {
					//PMS.dbg("getRendererConfigurationByUA: isRZ_IPmatch(ia) return false");
				}
			}
		}
		//PMS.dbg("getRendererConfigurationByUA: return null");
		return null;
	}

	private static RendererConfiguration manageRendererMatch(RendererConfiguration r) {
		if (addressAssociation.values().contains(r)) {
			// FIXME: This cannot ever ever happen because of how renderer matching
			// is implemented in RequestHandler and RequestHandlerV2. The first header
			// match will associate the IP address with the renderer and from then on
			// all other requests from the same IP address will be recognized based on
			// that association. Headers will be ignored and unfortunately they happen
			// to be the only way to get here.
			logger.info("Another renderer like " + r.getRendererName() + " was found!");
		}
		return r;
	}

	public static RendererConfiguration getRendererConfigurationByUAAHH(String header) {
		return getRendererConfigurationByUAAHH(header,null);
	}

	//regzamod, add ia checking if defined
	public static RendererConfiguration getRendererConfigurationByUAAHH(String header,InetAddress ia) {
		for (RendererConfiguration r : renderersConfs) {
			if (StringUtils.isNotBlank(r.getUserAgentAdditionalHttpHeader()) && header.startsWith(r.getUserAgentAdditionalHttpHeader())) {
				String value = header.substring(header.indexOf(":", r.getUserAgentAdditionalHttpHeader().length()) + 1);
				if (r.matchAdditionalUserAgent(value)) {
					if(r.isRZ_IPmatch(ia)) {
						return manageRendererMatch(r);
					}
				}
			}
		}
		return null;
	}
	/*
	 * 
	 */
	private final File configurationFile;
	private final PropertiesConfiguration configuration;
	private FormatConfiguration formatConfiguration;

	public FormatConfiguration getFormatConfiguration() {
		return formatConfiguration;
	}
	private int rank;
	private final Map<String, String> mimes;
	//private final Map<String, String> DLNAPN;
	private final Map<String, String[]> DLNAPN;

	public int getRank() {
		return rank;
	}

	// Those 'is' methods should disappear
	public boolean isXBOX() {
		return getRendererName().toUpperCase().contains("XBOX");
	}
	public boolean isWMP() {  //regzam
		return getRendererName().toUpperCase().contains("WMP");
	}

	public boolean isXBMC() {
		return getRendererName().toUpperCase().contains("XBMC");
	}

	public boolean isPS3() {
		return getRendererName().toUpperCase().contains("PLAYSTATION") || getRendererName().toUpperCase().contains("PS3");
	}

	public boolean isBRAVIA() {
		return getRendererName().toUpperCase().contains("BRAVIA");
	}
	
	public boolean isREGZA() {	//regzamod
		return getRendererName().toUpperCase().contains("REGZA");
	}

	public boolean isFDSSDP() {
		return getRendererName().toUpperCase().contains("FDSSDP");
	}

	private static final String RENDERER_NAME = "RendererName";
	private static final String RENDERER_ICON = "RendererIcon";
	private static final String USER_AGENT = "UserAgentSearch";
	private static final String USER_AGENT_ADDITIONAL_HEADER = "UserAgentAdditionalHeader";
	private static final String USER_AGENT_ADDITIONAL_SEARCH = "UserAgentAdditionalHeaderSearch";
	private static final String VIDEO = "Video";
	private static final String AUDIO = "Audio";
	private static final String IMAGE = "Image";
	private static final String SEEK_BY_TIME = "SeekByTime";

	private static final String MPEGPSAC3 = "MPEGAC3";
	private static final String MPEGTSAC3 = "MPEGTSAC3";
	private static final String WMV = "WMV";
	private static final String LPCM = "LPCM";
	private static final String WAV = "WAV";
	private static final String MP3 = "MP3";

	private static final String TRANSCODE_AUDIO = "TranscodeAudio";
	private static final String TRANSCODE_VIDEO = "TranscodeVideo";
	private static final String DEFAULT_VBV_BUFSIZE = "DefaultVBVBufSize";
	private static final String MUX_H264_WITH_MPEGTS = "MuxH264ToMpegTS";
	private static final String MUX_DTS_TO_MPEG = "MuxDTSToMpeg";
	private static final String WRAP_DTS_INTO_PCM = "WrapDTSIntoPCM";
	private static final String MUX_LPCM_TO_MPEG = "MuxLPCMToMpeg";
	private static final String MAX_VIDEO_BITRATE = "MaxVideoBitrateMbps";
	private static final String MAX_VIDEO_WIDTH = "MaxVideoWidth";
	private static final String MAX_VIDEO_HEIGHT = "MaxVideoHeight";
	private static final String USE_SAME_EXTENSION = "UseSameExtension";
	private static final String MIME_TYPES_CHANGES = "MimeTypesChanges";
	private static final String TRANSCODE_EXT = "TranscodeExtensions";
	private static final String STREAM_EXT = "StreamExtensions";
	private static final String H264_L41_LIMITED = "H264Level41Limited";
	private static final String TRANSCODE_AUDIO_441KHZ = "TranscodeAudioTo441kHz";
	private static final String TRANSCODED_SIZE = "TranscodedVideoFileSize";
	private static final String DLNA_PN_CHANGES = "DLNAProfileChanges";
	private static final String TRANSCODE_FAST_START = "TranscodeFastStart";
	private static final String AUTO_EXIF_ROTATE = "AutoExifRotate";
	private static final String DLNA_ORGPN_USE = "DLNAOrgPN";
	private static final String DLNA_LOCALIZATION_REQUIRED = "DLNALocalizationRequired";
	private static final String MEDIAPARSERV2 = "MediaInfo";
	private static final String MEDIAPARSERV2_THUMB = "MediaParserV2_ThumbnailGeneration";
	private static final String SUPPORTED = "Supported";
	private static final String CUSTOM_MENCODER_QUALITYSETTINGS = "CustomMencoderQualitySettings";
	private static final String SHOW_AUDIO_METADATA = "ShowAudioMetadata";
	private static final String SHOW_SUB_METADATA = "ShowSubMetadata";
	private static final String DLNA_TREE_HACK = "CreateDLNATreeFaster";
	private static final String CHUNKED_TRANSFER = "ChunkedTransfer";

	// Sony devices require JPG thumbnails
	private static final String FORCE_JPG_THUMBNAILS = "ForceJPGThumbnails";

	// Ditlew
	private static final String SHOW_DVD_TITLE_DURATION = "ShowDVDTitleDuration";
	private static final String CBR_VIDEO_BITRATE = "CBRVideoBitrate";
	private static final String BYTE_TO_TIMESEEK_REWIND_SECONDS = "ByteToTimeseekRewindSeconds";

	//---- regzamod add start
	private static final String RZ_DLNA_PN_CHANGES_ADD = "RZ_AddDlnaPC";		// regzamod, cumulaive DLNAProfileChanges
	private static final String RZ_TRICKY_START = "RZ_TrickyStart";				// regzamod, cumulaive DLNAProfileChanges
	private static final String RZ_RESIZE_FOR_REGZA	= "RZ_ResizeForRegza";		// Regza way of resize in transcoding  

	private static final String RZ_NON_TRANSCODE_EX	= "RZ_NonTranscodeEx";				// Muxable conditions of size & aspect_ratio
	private static final String RZ_REMUX_JUDGE_FOR_REGZA	= "RZ_RemuxJudgeForRegza";	//
	private static final String RZ_REMUX_EXTRACT_BY_FFMPEG = "RZ_RemuxExtractByFfmpeg";	//
	private static final String RZ_REMUX_TYPE = "RZ_RemuxType";	//
	private static final String RZ_REMUX_DVD_ANY_AUDIO	= "RZ_RemuxDvdAnyAudio";		//
	private static final String RZ_SIZE_DELTA	= "RZ_SizeDelta";		//gosa for judge remux size
	private static final String RZ_ASPECT_DELTA	= "RZ_AspectDelta";		//gosa for judge remux aspectratio
	private static final String RZ_TRANS_AUDIO_DELAY	= "RZ_TransAudioDelay";	//
	private static final String RZ_TRANS_AUDIO_DELAY_SART	= "RZ_TransAudioDelayStart";	//
	private static final String RZ_TRANS_AUDIO_DELAY_RESTART_TS	= "RZ_TransAudioDelayRestartTs";	//
	private static final String RZ_TRANS_RESTART_REWIND	= "RZ_TransRestartRewind";		//
	private static final String RZ_TRANS_AUDIO_DELAY_BY_TIME_SEEK	= "RZ_TransAudioDelayByTimeSeek";	//

	private static final String RZ_REMUX_RESTART_REWIND	= "RZ_RemuxRestartRewind";		//
	private static final String RZ_REMUX_AUDIO_DELAY	= "RZ_RemuxAudioDelay";	//
	private static final String RZ_REMUX_AUDIO_DELAY_RESTART	= "RZ_RemuxAudioDelayRestart";	//
	private static final String RZ_REMUX_AUDIO_DELAY_RESTART_FM	= "RZ_RemuxAudioDelayRestartFm";	//
	private static final String RZ_REMUX_AUDIO_DELAY_BY_TIME_SEEK	= "RZ_RemuxAudioDelayByTimeSeek";	//
	private static final String RZ_TRANSCODE_TO_M2TS	= "RZ_TranscodeToM2ts";	//
	private static final String RZ_REMUX_TO_M2TS	= "RZ_RemuxToM2ts";	//
	private static final String RZ_TRANS_FORMAT	= "RZ_TransFormat";	//
	private static final String RZ_REMUX_FORMAT	= "RZ_RemuxFormat";	//
	private static final String RZ_TRANS_FORMAT_WEB	= "RZ_TransFormatWeb";	//
	private static final String RZ_TRANS_FORMAT_AAV	= "RZ_TransFormatAAV";	//
	private static final String RZ_FORCE_SINGLE_RANGE_TYPE	= "RZ_ForceSingleRangeType";	//
	private static final String RZ_SD_SIZE_ZOOM	= "RZ_SDsizeZoom";		//
	private static final String RZ_SD_SIZE_ZOOM_DVD	= "RZ_SDsizeZoomDVD";	// for DVD/ISO
	private static final String RZ_TRANS_START_SKIP	= "RZ_TransStartSkip";	//
	private static final String RZ_REMUX_START_SKIP	= "RZ_RemuxStartSkip";	//
	private static final String RZ_MPEGTS_FPS_MNT	= "RZ_MpegtsFpsMnt";	//for ts/m2ts transcode
	private static final String RZ_IPADDRESS	= "RZ_IPAddress";	//
	private static final String RZ_HACK_FOR_ZS1	= "RZ_HackForZS1";	//
	private static final String RZ_USE_GENERIC_DLNAPN	= "RZ_UseGenericDLNAPN";	// generic means private
	private static final String RZ_ACCEPT_H264L41_COMPATIBLE = "RZ_AcceptH264L41Compatible";
		
	private static final String RZ_USE_FFMPEG_MUXER	= "RZ_UseFFmpegMuxer";		// use ffmpeg muxer on trans
	private static final String RZ_FORCE_RESIZE_DVD	= "RZ_ForceResizeDVD";		//
	private static final String RZ_TRANS_FORMAT_DVD	= "RZ_TransFormatDVD";		//
	
	//ServerSide Resume
	private static final String KEY_RZ_RESUME_MODE= "rz_resume_mode";	
	private static final String KEY_RZ_RESUME_PLAY_MIN_TIME1= "rz_resume_play_min_time1";	
	private static final String KEY_RZ_RESUME_PLAY_MIN_TIME2= "rz_resume_play_min_time2";	
	private static final String KEY_RZ_RESUME_REWIND_TIME= "rz_resume_rewind_time";	
	private static final String KEY_RZ_RESUME_DURATION_DELTA= "rz_resume_duration_delta";	
	
	//ServerSide TimeSeek
	private static final String RZ_BITRATE_FOR_TIMESEEK	    = "RZ_BitrateForTimeSeek";	//
	private static final String RZ_BITRATE_FOR_TIMESEEK_MIN	= "RZ_BitrateForTimeSeekMin";	//
	private static final String RZ_BITRATE_FOR_TIMESEEK_MAX	= "RZ_BitrateForTimeSeekMax";	//

	private static final String RZ_BYTESEEK_PAUSE_REWIND    = "RZ_ByteSeekPauseRewind";
	private static final String RZ_BYTESEEK_PAUSE_MIN       = "RZ_ByteSeekPauseMin";
	private static final String RZ_BYTESEEK_HACK            = "RZ_ByteSeekHack";
	private static final String RZ_BYTESEEK_CALIB_SKIP      = "RZ_ByteSeekCalibSkip";
	private static final String RZ_BYTESEEK_CALIB_SKIP_RANGE= "RZ_ByteSeekCalibSkipRange";
	private static final String RZ_BYTESEEK_CALIB_TIME_LAG  = "RZ_ByteSeekCalibTimeLag";
	private static final String RZ_BYTESEEK_CALIB_TIME_RATIO= "RZ_ByteSeekCalibTimeRatio";
	private static final String RZ_BYTESEEK_CALIB_TIME_MIN=   "RZ_ByteSeekCalibTimeMin";
	private static final String RZ_BYTESEEK_CALIB_TIME_MIN2=  "RZ_ByteSeekCalibTimeMin2";

	//trans/remux optional scripts
	private static final String RZ_REND_OPT_SCRIPT= "Rz_RendOptScript";	
	
	// misc.
	private static final String RZ_ASPECT_HACK= "RZ_AspectHack";
	private static final String RZ_QUICK_START_HACK_TIME= "RZ_QuickStartHackTime";
	private static final String RZ_ACTION_BUTTON_TYPE= "RZ_ActionButtonType";
	private static final String RZ_AUTO_SEARCH_LOCK= "RZ_AutoSearchLock";
	private static final String RZ_DLNA_NAME_TYPE= "RZ_DlnaNameType";   //name in dlnaURL 
	private static final String RZ_ITUNES_PARSE_MODE= "RZ_ItunesParseMode";   
	private static final String RZ_AUDIO_AS_VIDEO= "RZ_AudioAsVideo";
	private static final String RZ_IMAGE_AS_VIDEO= "RZ_ImageAsVideo";
	private static final String RZ_MEDIA_PARSE_TYPE= "RZ_MediaParseType";
	private static final String RZ_MEDIA_JUDGE_TYPE= "RZ_MediaJudgeType";
	private static final String RZ_AAV_AUTO_PLAY= "RZ_AAV_AutoPlay";
	private static final String RZ_AAV_AUTO_PLAY_IGAP= "RZ_AAV_AutoPlayIgap";  //inter-audio gap(sec)
	private static final String RZ_AAV_AUTO_PLAY_TRICKS= "RZ_AAV_AutoPlayTricks";
	private static final String RZ_IAV_AUTO_PLAY_TRICKS= "RZ_IAV_AutoPlayTricks";
	
	private static final String RZ_DEF_START_WAIT  = "RZ_DefStartWait";
	private static final String RZ_TRANS_START_WAIT= "RZ_TransStartWait";
	private static final String RZ_REMUX_START_WAIT= "RZ_RemuxStartWait";
	private static final String RZ_AAV_START_WAIT  = "RZ_AavStartWait";
	private static final String RZ_IAV_START_WAIT  = "RZ_IavStartWait";
	private static final String RZ_WEB_START_WAIT  = "RZ_WebStartWait";
	private static final String RZ_PAGE_LIST_MAX  = "RZ_PageListMax";
	
	public static final int COND_GE = 1;   // Greater than or Equal
	public static final int COND_LE = 2;   // Less than or Equal
	public static final int COND_EQ = 3;   // Equal
	public int	size_delta= 1;
	public double aspect_delta= 0.01f;
	//---- regzamod add end
	
	// Ditlew
	public int getByteToTimeseekRewindSeconds() {
		return getInt(BYTE_TO_TIMESEEK_REWIND_SECONDS, 0);
	}

	// Ditlew
	public int getCBRVideoBitrate() {
		return getInt(CBR_VIDEO_BITRATE, 0);
	}

	// Ditlew
	public boolean isShowDVDTitleDuration() {
		return getBoolean(SHOW_DVD_TITLE_DURATION, false);
	}

	private RendererConfiguration() throws ConfigurationException {
		this(null);
	}
	private Pattern userAgentPattern = null;
	private Pattern userAgentAddtionalPattern = null;

	public RendererConfiguration(File f) throws ConfigurationException {
		configuration = new PropertiesConfiguration();
		configuration.setListDelimiter((char) 0);
		configurationFile = f;
		if (f != null) {
			configuration.load(f);
		}
		userAgentPattern = Pattern.compile(getUserAgent(), Pattern.CASE_INSENSITIVE);
		userAgentAddtionalPattern = Pattern.compile(getUserAgentAdditionalHttpHeaderSearch(), Pattern.CASE_INSENSITIVE);
		mimes = new HashMap<String, String>();
		String mimeTypes = configuration.getString(MIME_TYPES_CHANGES, null);
		if (StringUtils.isNotBlank(mimeTypes)) {
			StringTokenizer st = new StringTokenizer(mimeTypes, "|");
			while (st.hasMoreTokens()) {
				String mime_change = st.nextToken().trim();
				int equals = mime_change.indexOf("=");
				if (equals > -1) {
					String old = mime_change.substring(0, equals).trim().toLowerCase();
					String nw = mime_change.substring(equals + 1).trim().toLowerCase();
					mimes.put(old, nw);
				}
			}
		}
		DLNAPN = new HashMap<String, String[]>();
		String DLNAPNchanges = configuration.getString(DLNA_PN_CHANGES, null);
		if (DLNAPNchanges != null) {
			logger.trace("Config DLNAPNchanges: " + DLNAPNchanges);
		}
		if (StringUtils.isNotBlank(DLNAPNchanges)) {
			StringTokenizer st = new StringTokenizer(DLNAPNchanges, "|");
			while (st.hasMoreTokens()) {
				String DLNAPN_change = st.nextToken().trim();
				int equals = DLNAPN_change.indexOf("=");
				if (equals > -1) {
					String old = DLNAPN_change.substring(0, equals).trim().toUpperCase();
					String[] nw= new String[5];
					Arrays.fill(nw, "");
					nw[0]= DLNAPN_change.substring(equals + 1).trim().toUpperCase();
					DLNAPN.put(old, nw);
				}
			}
		}
		if (f == null) {
			// the default renderer supports everything !
			//PMS.dbg("RendererConf: Construction defaultRenderer");
			{
				configuration.addProperty(MEDIAPARSERV2, true);
				configuration.addProperty(MEDIAPARSERV2_THUMB, true);
				configuration.addProperty(SUPPORTED, "f:.+");
			}
			if(PMS.getConfiguration().getRZ_media_parse_type()==1) {
				//configuration.addProperty(MEDIAPARSERV2, false);  // cause freeze!!, why?
				//configuration.addProperty(MEDIAPARSERV2_THUMB, false);// cause freeze!!, why?
				//configuration.addProperty(SUPPORTED, "f:.+");
				setRZ_MediaParseType(1);
				if(PMS.rz_debug>1) PMS.dbg("RendererConf: set RZ_MEDIA_PARSE_TYPE=1");
			}
		}
		if (isMediaParserV2()|| getRZ_MediaJudgeType()==2) { //origin
			//parse & judge
			formatConfiguration = new FormatConfiguration(configuration.getList(SUPPORTED));
		}
		else if(getRZ_MediaParseType()==2) {  //regzam
			//dummy for parse only
			//PMS.dbg("RendererConfiguration: getRZ_MediaParseType()==2: MediaParseV2 without judgeV2 ");
			formatConfiguration = new FormatConfiguration();  
		}
		
		//---- regzamod add start
		if (true || PMS.rz_mod) {	//regzamod
			//---- make DlnaProfileChange list
			rz_AddDlnaPC(configuration.getList(RZ_DLNA_PN_CHANGES_ADD));
			if(PMS.rz_debug>1) {
				PMS.dbg2("RendererConfiguration: DLNAPNchanges="+DLNAPN);
			}
			
			// test load
			/*
			logger.warn("RendererConfiguration: Exec loadRz_TransOptScript, for TEST!!");
			PMS.rz_debug=2;
			loadRz_TransOptScript();  //for test, shuldn't load until really used
			*/
			
			//---- make Muxable condition list as for Size & Aspect ratio
			//rz_MakeMuxSizeAspList(configuration.getList(RZ_NON_TRANSCODE_EX));
			rz_NonTranscodeExList(configuration.getList(RZ_NON_TRANSCODE_EX));
			
			if(PMS.rz_debug>1) {
				PMS.dbg2("RendererConfiguration: CondSizeAspList size="+CondSizeAspList.size());
				for(int i=0; i<CondSizeAspList.size();i++) {
					CondSizeAsp c=CondSizeAspList.get(i);
					PMS.dbg2("entries["+i+"]"+", w_cond="+c.w_cond+", w_val="+c.w_value);
					PMS.dbg2("entries["+i+"]"+", h_cond="+c.h_cond+", h_val="+c.h_value);
					PMS.dbg2("entries["+i+"]"+", a_cond="+c.a_cond+", a_val="+c.a_value);
					PMS.dbg2("entries["+i+"]"+", f_cond="+c.f_cond+", f_val="+c.f_value);
					PMS.dbg2("entries["+i+"]"+", t_cond="+c.t_cond+", t_val="+c.t_value);
				}
			}
			size_delta=getRZ_SizeDelta();
			aspect_delta=getRZ_AspectDelta();
			if(PMS.rz_debug>0) {
				PMS.dbg2("size_delta="+size_delta+", aspect_delta="+aspect_delta);
			}
		}
		//---- regzamod add end
	}
	
	//---- regzamod add start -----------------------------
	//---- cumulaive type DLNAProfileChanges
	private void rz_AddDlnaPC(List<?> dlnapc) {
		if(dlnapc==null) return;
		for (Object line : dlnapc) {
			String DLNAPNchanges=line.toString();
			if(PMS.rz_debug>1) PMS.dbg2("rz_AddDlnaPC: line="+DLNAPNchanges);
			
			// ignore comments begin with #
			int pos = DLNAPNchanges.indexOf("#");
			if(pos>-1) {
				DLNAPNchanges=DLNAPNchanges.substring(0,pos);
			}
			
			if (StringUtils.isNotBlank(DLNAPNchanges)) {
				StringTokenizer st = new StringTokenizer(DLNAPNchanges, "|");
				while (st.hasMoreTokens()) {
					String DLNAPN_change = st.nextToken().trim();
					int equals = DLNAPN_change.indexOf("=");
					if (equals > -1) {
						String old = DLNAPN_change.substring(0, equals).trim().toUpperCase();
						//String nw = DLNAPN_change.substring(equals + 1).trim().toUpperCase();
						String str=DLNAPN_change.substring(equals + 1);
						//PMS.dbg("rz_AddDlnaPC: old="+old+", new="+str);
						String[] nw= new String[5];
						Arrays.fill(nw, "");
						String[] nw1=PMSUtil.splitx1(str,",");
						for(int i=0;i<nw1.length && i<5;i++) {
							nw[i]=nw1[i].trim();
							if(i==0) nw[i]=nw[i].toUpperCase();
							//PMS.dbg("rz_AddDlnaPC: new i="+i+", str='"+nw[i]+"'");
						}
						// = DLNAPN_change.substring(equals + 1).trim().toUpperCase();
						DLNAPN.put(old, nw);
					}
				}
			}
		}
	}
	
	//---- make Muxable condition list as for Size & Aspect ratio
	private void rz_NonTranscodeExList(List<?> cond) {
		if(cond==null) return;
		for (Object line : cond) {
			String muxcond=line.toString();
			if(PMS.rz_debug>1) PMS.dbg2("rz_NonTranscodeExList: line="+muxcond);
			//---------------------------------------------
			// parse single line
			// e.x.  w:ge:1280, h:ge:720, a:eq:1.7778
			//---------------------------------------------
			int pos = muxcond.indexOf("#");	// ignore comments begin with #
			if(pos>-1) {
				muxcond=muxcond.substring(0,pos);
			}
			
			CondSizeAsp c= new CondSizeAsp();  // single line condition
			c.w_cond=0;	//init
			c.h_cond=0;
			c.a_cond=0;
			c.f_cond=0;
			c.t_cond=0;
			if (StringUtils.isNotBlank(muxcond)) {
				boolean valid=true;
				StringTokenizer st1 = new StringTokenizer(muxcond, ",");
				while (st1.hasMoreTokens()) {
					//---------------------------------------------
					// parse single condition
					// e.x. w:ge:1280
					//---------------------------------------------
					String mc1 = st1.nextToken().trim();
					StringTokenizer st2 = new StringTokenizer(mc1, ":");
					int i=0;
					String[] mc2= new String[3];
					while (st2.hasMoreTokens() && i<3) {
						mc2[i++] = st2.nextToken().trim();
					}
					if(mc2[0].equals("w")) {	//width
						if(mc2[1].equals("ge")) c.w_cond=COND_GE;
						else if(mc2[1].equals("le")) c.w_cond=COND_LE;
						else if(mc2[1].equals("eq")) c.w_cond=COND_EQ;
						else valid=false;
						c.w_value=Integer.parseInt(mc2[2]);
					}
					else if(mc2[0].equals("h")) {	//height 
						if(mc2[1].equals("ge")) c.h_cond=COND_GE;
						else if(mc2[1].equals("le")) c.h_cond=COND_LE;
						else if(mc2[1].equals("eq")) c.h_cond=COND_EQ;
						else valid=false;
						c.h_value=Integer.parseInt(mc2[2]);
					}
					else if(mc2[0].equals("a")) {	//aspect_ratio
						if(mc2[1].equals("ge")) c.a_cond=COND_GE;
						else if(mc2[1].equals("le")) c.a_cond=COND_LE;
						else if(mc2[1].equals("eq")) c.a_cond=COND_EQ;
						else valid=false;
						c.a_value=Float.parseFloat(mc2[2]);
					}
					else if(mc2[0].equals("f")) {	//file_type 
						if(mc2[1].equals("eq")) c.f_cond=COND_EQ;
						else valid=false;
						c.f_value=mc2[2];
					}
					else if(mc2[0].equals("t")) {	//file_type for negative judge
						if(mc2[1].equals("eq")) c.t_cond=COND_EQ;
						else valid=false;
						c.t_value=mc2[2];
					}
					else {
						logger.warn("rz_NonTranscodeExList: Illegal element="+mc2[0]);
						valid=false;
					}
				}
				// add 1line condition to condition list 
				if(valid) {
					CondSizeAspList.add(c);
				}
			}
		}
	}
	
	//---- params for REGZA ------------
	public boolean isRZ_TrickyStart() {
		return rz_getBoolean(RZ_TRICKY_START, false);
	}
	public boolean isRZ_ResizeForRegza() {
		return rz_getBoolean(RZ_RESIZE_FOR_REGZA, false);
	}
	public boolean isRZ_RemuxJudgeForRegza() {
		return rz_getBoolean(RZ_REMUX_JUDGE_FOR_REGZA, false);
	}
	public boolean isRZ_RemuxExtractByFfmpeg() {
		return rz_getBoolean(RZ_REMUX_EXTRACT_BY_FFMPEG, false);
	}
	public boolean isRZ_RemuxDvdAnyAudio() {
		return rz_getBoolean(RZ_REMUX_DVD_ANY_AUDIO, false);
	}
	public int getRZ_RemuxType() {
		//return rz_getInt(RZ_REMUX_TYPE, 113);
		return rz_getInt(RZ_REMUX_TYPE, 222);
	}
	public int getRZ_SizeDelta() {
		return rz_getInt(RZ_SIZE_DELTA, 1);
	}
	public float getRZ_AspectDelta() {
		return rz_getFloat(RZ_ASPECT_DELTA, 0.01);
	}
	public float getRZ_RemuxRestartRewind() {
		return rz_getFloat(RZ_REMUX_RESTART_REWIND, 0.0);
	}
	public float getRZ_TransRestartRewind() {
		return rz_getFloat(RZ_TRANS_RESTART_REWIND, 0.0);
	}
	public float getRZ_RemuxAudioDelay() {
		return rz_getFloat(RZ_REMUX_AUDIO_DELAY, 0.0);
	}
	public float getRZ_RemuxAudioDelayRestart() {
		return rz_getFloat(RZ_REMUX_AUDIO_DELAY_RESTART, 0.0);
	}
	public float getRZ_TransAudioDelayRestartTs() {
		return rz_getFloat(RZ_TRANS_AUDIO_DELAY_RESTART_TS, 0.0);
	}
	public float getRZ_RemuxAudioDelayRestartFm() {
		return rz_getFloat(RZ_REMUX_AUDIO_DELAY_RESTART_FM, 0.0);
	}
	public boolean isRZ_RemuxAudioDelayByTimeSeek() {
		return rz_getBoolean(RZ_REMUX_AUDIO_DELAY_BY_TIME_SEEK, true);
	}
	public boolean isRZ_TransAudioDelayByTimeSeek() {
		return rz_getBoolean(RZ_TRANS_AUDIO_DELAY_BY_TIME_SEEK, true);
	}

	/*
	public boolean isRZ_TranscodeToM2ts() {  //depricated -> use getRZ_TransFormat
		return rz_getBoolean(RZ_TRANSCODE_TO_M2TS, false);
	}
	public boolean isRZ_RemuxToM2ts() { 	 //depricated -> use getRZ_RemuxFormat
		return rz_getBoolean(RZ_REMUX_TO_M2TS, false);
	}
	*/
	public int getRZ_RemuxVFormat() {
		if(remux_vformat>0) {
			return remux_vformat;
		}
		remux_vformat=PMS.TRS_MPEGTS;
		String str=getRZ_RemuxFormat();
		if(str!=null) {
			if(str.equals("m2ts")) {	
				remux_vformat = PMS.TRS_M2TS;
			} else if(str.equals("mpegts")) {
				remux_vformat = PMS.TRS_MPEGTS;
			}
		}
		return remux_vformat;
	}
	public int getRZ_TransVFormat() {
		if(trans_vformat>0) {
			return trans_vformat;
		}
		//---- Override by RZ_TransFormat
		String str=getRZ_TransFormat();
		trans_vformat=PMS.TRS_MPEGPS;
		if(str!=null) {
			if(str.equals("m2ts")) {	
				trans_vformat = PMS.TRS_M2TS;
			} else if(str.equals("mpegts")) {
				trans_vformat = PMS.TRS_MPEGTS;
			} else if(str.equals("mpegts2")) {
				trans_vformat = PMS.TRS_MPEGTS;
			} else if(str.equals("mpegps")) {
				trans_vformat = PMS.TRS_MPEGPS;
			}
		}
		else {
			if(isTranscodeToMPEGTSAC3()) {
				trans_vformat = PMS.TRS_MPEGTS;
			} else {
				trans_vformat = PMS.TRS_MPEGPS;
			}
		}
		return trans_vformat;
	}
	
	public int getRZ_TransVFormatWeb() {
		if(trans_vformat_web>0) {
			return trans_vformat_web;
		}
		//---- Override by RZ_TransFormat
		String str=getRZ_TransFormatWeb();
		trans_vformat_web=PMS.TRS_MPEGPS;
		
		if(str!=null) {
			if(str.equals("m2ts")) {	
				trans_vformat_web = PMS.TRS_M2TS;
			} else if(str.equals("mpegts")) {
				trans_vformat_web = PMS.TRS_MPEGTS;
			} else if(str.equals("mpegts2")) {
				trans_vformat_web = PMS.TRS_MPEGTS;
			} else if(str.equals("mpegps")) {
				trans_vformat_web = PMS.TRS_MPEGPS;
			}
		}
		else {
			trans_vformat_web=getRZ_TransVFormat();
			/*
			if(isTranscodeToMPEGTSAC3()) {
				trans_vformat = PMS.TRS_MPEGTS;
			} else {
				trans_vformat = PMS.TRS_MPEGPS;
			}
			*/
		}
		return trans_vformat_web;
	}
	
	public int getRZ_TransVFormatAAV() {
		if(trans_vformat_aav>0) {
			return trans_vformat_aav;
		}
		//---- Override by RZ_TransFormat
		String str=getRZ_TransFormatAAV();
		trans_vformat_aav=PMS.TRS_MPEGPS;
		
		if(str!=null) {
			if(str.equals("m2ts")) {	
				trans_vformat_aav = PMS.TRS_M2TS;
			} else if(str.equals("mpegts")) {
				trans_vformat_aav = PMS.TRS_MPEGTS;
			} else if(str.equals("mpegts2")) {
				trans_vformat_aav = PMS.TRS_MPEGTS;
			} else if(str.equals("mpegps")) {
				trans_vformat_aav = PMS.TRS_MPEGPS;
			}
		}
		else {
			trans_vformat_aav=getRZ_TransVFormat();
		}
		return trans_vformat_aav;
	}
	
	public String getRZ_TransFormat() {
		return rz_getString(RZ_TRANS_FORMAT,null);
	}
	public String getRZ_RemuxFormat() {
		return rz_getString(RZ_REMUX_FORMAT,null);
	}
	public String getRZ_TransFormatWeb() {
		return rz_getString(RZ_TRANS_FORMAT_WEB,null);
	}
	public String getRZ_TransFormatAAV() {
		return rz_getString(RZ_TRANS_FORMAT_AAV,null);
	}
	public boolean isRZ_ForceSingleRangeType() {
		return rz_getBoolean(RZ_FORCE_SINGLE_RANGE_TYPE, false);
	}
	public float getRZ_SDsizeZoom() {
		return rz_getFloat(RZ_SD_SIZE_ZOOM, 1.0);
	}
	public float getRZ_SDsizeZoomDVD() {
		return rz_getFloat(RZ_SD_SIZE_ZOOM_DVD, 1.0);
	}
	public float getRZ_TransAudioDelayStart() {
		return rz_getFloat(RZ_TRANS_AUDIO_DELAY_SART, 0);
	}
	public float getRZ_TransAudioDelay() {
		return rz_getFloat(RZ_TRANS_AUDIO_DELAY, 0);
	}
	public float getRZ_TransStartSkip() {
		return rz_getFloat(RZ_TRANS_START_SKIP, 0);
	}
	public float getRZ_RemuxStartSkip() {
		return rz_getFloat(RZ_REMUX_START_SKIP, 0);
	}
	public float getRZ_WebStartSkip() {
		return rz_getFloat(RZ_REMUX_START_SKIP, 0);
	}
	public int getRZ_HackForZS1() {
		return rz_getInt(RZ_HACK_FOR_ZS1, 0);
	}
	public float getRZ_MpegtsFpsMnt() {
		return rz_getFloat(RZ_MPEGTS_FPS_MNT, -1);
	}
	public String getRZ_getIPAddress() {
		return rz_getString(RZ_IPADDRESS, null);
	}
	public boolean isRZ_UseGenericDLNAPN() {
		return rz_getBoolean(RZ_USE_GENERIC_DLNAPN, false);
	}
	public boolean isRZ_AcceptH264L41Compatible() {
		return rz_getBoolean(RZ_ACCEPT_H264L41_COMPATIBLE, false);
	}
	public int getRZ_UseFFmpegMuxer() {
		return rz_getInt(RZ_USE_FFMPEG_MUXER, 0);
	}
	public int getRZ_ForceResizeDVD() {
		//=0:NoResize,=1:Resize only when without subtitle,=2:Reasize always
		return rz_getInt(RZ_FORCE_RESIZE_DVD, 1);
	}
	public String getRZ_TransFormatDVD() {
		return rz_getString(RZ_TRANS_FORMAT_DVD, "default");
	}
	
	//---- ServerSide Resume
	public int getRZ_resume_mode() {
		return rz_getInt(KEY_RZ_RESUME_MODE, 1);
	}
	public float getRZ_resume_play_min_time1() {
		return rz_getFloat(KEY_RZ_RESUME_PLAY_MIN_TIME1, 5.0);
	}
	public float getRZ_resume_play_min_time2() {
		return rz_getFloat(KEY_RZ_RESUME_PLAY_MIN_TIME2, 5.0);
	}
	public float getRZ_resume_rewind_time() {
		return rz_getFloat(KEY_RZ_RESUME_REWIND_TIME, 10.0);
	}
	public float getRZ_resume_duration_delta() {
		return rz_getFloat(KEY_RZ_RESUME_DURATION_DELTA, 60.0);
	}
	
	//---- ServerSide ByteSeek
	public float getRZ_BitrateForTimeSeek() {
		return rz_getFloat(RZ_BITRATE_FOR_TIMESEEK, 24.0);	//Mbps
	}
	public float getRZ_BitrateForTimeSeekMin() {
		return rz_getFloat(RZ_BITRATE_FOR_TIMESEEK_MIN, 2.0);	//Mbps
	}
	public float getRZ_BitrateForTimeSeekMax() {
		return rz_getFloat(RZ_BITRATE_FOR_TIMESEEK_MAX, 40.0);	//Mbps
	}
	public float getRZ_ByteSeekPauseRewind() {
		return rz_getFloat(RZ_BYTESEEK_PAUSE_REWIND, 4.0);
	}
	public float getRZ_ByteSeekPauseMin() {
		return rz_getFloat(RZ_BYTESEEK_PAUSE_MIN, 0.5);
	}
	public int getRZ_ByteSeekHack() {
		return rz_getInt(RZ_BYTESEEK_HACK, 0);
	}
	public float getRZ_ByteSeekCalibSkip() {
		return rz_getFloat(RZ_BYTESEEK_CALIB_SKIP, 30.0);
	}
	public float getRZ_ByteSeekCalibSkipRange() {
		return rz_getFloat(RZ_BYTESEEK_CALIB_SKIP_RANGE, 60.0);
	}
	public float getRZ_ByteSeekCalibTimeLag() {
		return rz_getFloat(RZ_BYTESEEK_CALIB_TIME_LAG, 6.0);
	}
	public float getRZ_ByteSeekCalibTimeRatio() {
		return rz_getFloat(RZ_BYTESEEK_CALIB_TIME_RATIO, 1.0);
	}
	public float getRZ_ByteSeekCalibTimeMin() {
		return rz_getFloat(RZ_BYTESEEK_CALIB_TIME_MIN, 10.0);
	}
	public float getRZ_ByteSeekCalibTimeMin2() {
		return rz_getFloat(RZ_BYTESEEK_CALIB_TIME_MIN2, 120.0);
	}
	
	//---- misc hack
	public int getRZ_AspectHack() {
		return rz_getInt(RZ_ASPECT_HACK,0);
	}
	public int getRZ_QuickStartHackTime() {
		return rz_getInt(RZ_QUICK_START_HACK_TIME,0);
	}
	public int getRZ_ActionButtonType() {
		return rz_getInt(RZ_ACTION_BUTTON_TYPE,-1);  //=1:file_type,=2:folder_type,=-1:use that in pms.conf
	}
	public int getRZ_AutoSearchLock() {
		// =0:unlock,=1:lock some folders(web/resume/settings),=2:lock all,=-1:use that in pms.conf
		return rz_getInt(RZ_AUTO_SEARCH_LOCK,-1);
	}
	public void setRZ_AutoSearchLock(int val) {
		// =0:unlock,=1:lock some folders(web/resume/settings),=2:lock all,=-1:use that in pms.conf
		configuration.setProperty(RZ_AUTO_SEARCH_LOCK, ""+val);
	}
	public int getRZ_MediaParseType() {
		//return rz_getInt(RZ_MEDIA_PARSE_TYPE,0);  	//=0:default,=1:force MediaPaeseV1(ffmpeg),=2:force MediaParseV2(Mediainfo)
		int type=rz_getInt(RZ_MEDIA_PARSE_TYPE,0);
		if(type==0) {//use system default
			return PMS.getConfiguration().getRZ_media_parse_type();
		}
		else return type;
		//return type;
	}
	public void setRZ_MediaParseType(int val) {
		configuration.setProperty(RZ_MEDIA_PARSE_TYPE, ""+val);
	}
	public int getRZ_MediaJudgeType() {
		int type=rz_getInt(RZ_MEDIA_JUDGE_TYPE,0);  	//=0:default,=1:judge by suffix,=2: judge by mediaInfo
		if(type==0) {
			if(isMediaParserV2()) return 2;
			else return 1;
		}
		else {
			return type;
		}
	}
	public int getRZ_ItunesParseMode() {
		return rz_getInt(RZ_ITUNES_PARSE_MODE,-1);  //=0:later(when opened),=1:now(pararel),=2:now(serial),=-1:refer sysytem default(PMS.conf)
	}
	public int getRZ_AudioAsVideo() {
		return rz_getInt(RZ_AUDIO_AS_VIDEO,-1);  //=0:false,=1:true,=-1:refer sysytem default(PMS.conf)
	}
	public int getRZ_ImageAsVideo() {
		return rz_getInt(RZ_IMAGE_AS_VIDEO,-1);  //=0:false,=1:true,=-1:refer sysytem default(PMS.conf)
	}
	public int getRZ_AAV_AutoPlay() {
		return rz_getInt(RZ_AAV_AUTO_PLAY,0);  //ServeSide AutoPlay for AudioAsVideo (=0:Off, =1:Serial-repeat, =2:Random-repeat)
	}
	public float getRZ_AAV_AutoPlayIgap() {  // inter-audio gap(sec)
		return rz_getFloat(RZ_AAV_AUTO_PLAY_IGAP,3.0);
	}
	public int getRZ_AAV_AutoPlayTrics() {  // >0: support trickPlay on AutoPlay
		return rz_getInt(RZ_AAV_AUTO_PLAY_TRICKS,0);
	}
	public int getRZ_IAV_AutoPlayTrics() {  // >0: support trickPlay on AutoPlay
		return rz_getInt(RZ_IAV_AUTO_PLAY_TRICKS,0);
	}
	public int getRZ_PageListMax() {
		return rz_getInt(RZ_PAGE_LIST_MAX,-1);  //max list count on display (on terminal)
	}
	
	//------------------------------------------------------------------------------
	// get following values for start playing tuning params, see OutputParams.java
	//	params.waitbeforestart   =mval[0];         //wait time before kick player (msec)
	//	params.minBufferSize     =mval[1];         //wait min Buffered before 1st send (MBytes, on Tras/Remux BufferMem)
	//	params.secondread_minsize=mval[2]*1000000; //wait min Buffered before 2nd send (Bytes,  on Tras/Remux BufferMem)
	//------------------------------------------------------------------------------
	private static double mval_def[]=null;
	private static double mval_trans[]=null;
	private static double mval_remux[]=null;
	private static double mval_web[]=null;
	private static double mval_aav[]=null;
	private static double mval_iav[]=null;
	
	public double[] getRZ_DefStartWait() {
		if(mval_def!=null) return mval_def;
		String str=rz_getString(RZ_DEF_START_WAIT,"-");
		if(str.equals("-")) mval_def=pconf.getRZ_def_startwait();
		else mval_def=getMvalStartWait(str,pconf.getRZ_def_startwait(),pconf.getRZ_def_startwait());
		return mval_def;
	}
	public double[] getRZ_TransStartWait() {
		if(mval_trans!=null) return mval_trans;
		String str=rz_getString(RZ_TRANS_START_WAIT,"-");
		if(str.equals("-")) mval_trans=pconf.getRZ_trans_startwait();
		else mval_trans=getMvalStartWait(str,getRZ_DefStartWait(),pconf.getRZ_trans_startwait());
		return mval_trans;
	}
	public double[] getRZ_RemuxStartWait() {
		if(mval_remux!=null) return mval_remux;
		String str=rz_getString(RZ_REMUX_START_WAIT,"-");
		if(str.equals("-")) mval_remux=pconf.getRZ_remux_startwait();
		else mval_remux=getMvalStartWait(str,getRZ_DefStartWait(),pconf.getRZ_remux_startwait());
		return mval_remux;
	}
	public double[] getRZ_WebStartWait() {
		if(mval_web!=null) return mval_web;
		String str=rz_getString(RZ_WEB_START_WAIT,"-");
		if(str.equals("-")) mval_web= pconf.getRZ_web_startwait();
		else mval_web= getMvalStartWait(str,getRZ_DefStartWait(),pconf.getRZ_web_startwait());
		return mval_web;
	}
	public double[] getRZ_AavStartWait() {
		if(mval_aav!=null) return mval_aav;
		String str=rz_getString(RZ_AAV_START_WAIT,"-");
		if(str.equals("-")) mval_aav= pconf.getRZ_aav_startwait();
		else mval_aav= getMvalStartWait(str,getRZ_DefStartWait(),pconf.getRZ_aav_startwait());
		return mval_aav;
	}
	public double[] getRZ_IavStartWait() {
		if(mval_iav!=null) return mval_iav;
		String str=rz_getString(RZ_IAV_START_WAIT,"-");
		if(str.equals("-")) mval_iav= pconf.getRZ_iav_startwait();
		else mval_iav= getMvalStartWait(str,getRZ_DefStartWait(),pconf.getRZ_iav_startwait());
		return mval_iav;
	}
	private double[] getMvalStartWait(String str,double def1[],double def2[]) {
		//make return value array from string
		double mval[]={0,0,0};
		String mstr[]=str.split(",");
		int cnt=0;
		try {
			for (String s: mstr) {
				double val=Double.parseDouble(s.trim());
				if(val>=0) mval[cnt]=val;
				else if(val>= -1) mval[cnt]=def1[cnt];
				else if(def2!=null) mval[cnt]=def2[cnt];
				cnt++;
				if(cnt>2) break;
			}
		} catch (NumberFormatException e) {
			logger.warn("RendererConfiguration.getMvalStartWait: getRZ_XXXStartWait() value error="+e.getMessage());
		} 
		return mval;
	}
	
	public int[] getRZ_DlnaNameType() {
		int ret[]= {2,0,12}; //default: {FixedName}{without suffix}{namelen<=12}
		/*
		ret[0]=0: {DispName(title)}
		ret[0]=1: {FileName}
		ret[0]=2: {FixedName}
		ret[1]=0: without suffix
		ret[1]=1: with suffix
		ret[2]={max name len}: -1: means full length
		*/
		
		String str=rz_getString(RZ_DLNA_NAME_TYPE, null);
		if(str==null) return ret;
		String[] sm=str.split(",");
		if(sm.length>=3) {
			ret[2]=Integer.parseInt(sm[2]);
		}
		if(sm.length>=2) {
			ret[1]=Integer.parseInt(sm[1]);
		}
		if(sm.length>=1) {
			ret[0]=Integer.parseInt(sm[0]);
		}
		
		//PMS.dbg("RendererConfiguration.getRZ_DlnaNameType: type="+ret[0]+", sfx="+ret[1]+", maxlen="+ret[2]);
		return ret;
		
	}
	
	public List<?> getScriptList() {
		return configuration.getList(RZ_REND_OPT_SCRIPT);
	}
	
	public boolean isRZ_IPmatch(InetAddress ia) {
		String ips=getRZ_getIPAddress();
		//PMS.dbg("isRZ_IPmatch: given ip="+ia.toString()+", defined in renderer="+ ips);
		if(ia==null || ips==null || ips.length()==0) {	// IP not defined (i.e. not restricted) --> means match any addr
			return true;
		}
		else {
			String[] ipm=ips.split(",");
			String	ipt=ia.toString();
			if(ipt.startsWith("/")) {
				ipt=ipt.substring(1);
			}
			for(int i=0;i<ipm.length;i++) {
				//PMS.dbg("defined IPs in renderer["+i+"]="+ ipm[i]);
				if(ipt.equals(ipm[i].trim())) {
					return true;
				}
			}
		}
		return false;
	}
	
	//public boolean isRZ_MuxableSizeAsp(int w,int h, float a, String f) {
	public boolean isRZ_NonTranscodeEx(int w,int h, double a, String f) {
		boolean ans;
		boolean negative;
		
		//PMS.dbg("---- isRZ_NonTranscodeEx : file_type="+f);
		CondSizeAsp c;
		if(CondSizeAspList.size()<=0) {
			return true;
		}
		for (int i=0;i<CondSizeAspList.size();i++) {
			c=CondSizeAspList.get(i);
			ans=false;
			negative=false;
			for(int j=0;j<1;j++) {	//only once
				ans=false;
				negative=false;
				if(c.w_cond==COND_GE) if(w<c.w_value-size_delta) break;
				if(c.w_cond==COND_LE) if(w>c.w_value+size_delta) break;
				if(c.w_cond==COND_EQ) if(w<c.w_value-size_delta||w>c.w_value+size_delta) break;
				if(c.h_cond==COND_GE) if(h<c.h_value-size_delta) break;
				if(c.h_cond==COND_LE) if(h>c.h_value+size_delta) break;
				if(c.h_cond==COND_EQ) if(h<c.h_value-size_delta||h>c.h_value+size_delta) break;
				if(c.a_cond==COND_GE) if(a<c.a_value-aspect_delta) break;
				if(c.a_cond==COND_LE) if(a>c.a_value+aspect_delta) break;
				if(c.a_cond==COND_EQ) if(a<c.a_value-aspect_delta||a>c.a_value+aspect_delta) break;
				if(c.f_cond==COND_EQ) if(f==null || !f.equals(c.f_value)) break;
				if(c.t_cond==COND_EQ) {	// t is a negative condition as for file_suffix
					if(f==null || !f.equals(c.t_value)) {
						//PMS.dbg("c.t_cond found but not match ");
						break;
					}
					else {
						//PMS.dbg("c.t_cond found & match ");
						negative=true;
					}
				}
				if(j==0) {// came to here: means all condition above passed
					ans=true;
				}
			}
			if (ans) {
				if(PMS.rz_debug>1) {
					PMS.dbg2("---- match Muxable cond ----------------");
					PMS.dbg2("w_cond="+c.w_cond+", w_val="+c.w_value);
					PMS.dbg2("h_cond="+c.h_cond+", h_val="+c.h_value);
					PMS.dbg2("a_cond="+c.a_cond+", a_val="+c.a_value);
					PMS.dbg2("f_cond="+c.f_cond+", f_val="+c.f_value);
					PMS.dbg2("t_cond="+c.t_cond+", t_val="+c.t_value);
				}
				return negative?false:true;
			}
		}
		if(PMS.rz_debug>1) {
			PMS.dbg2("---- Unmatch Muxable cond ----------------");
		}
		return false;
	}
	//---- regzamod add end --------------------------------

	public String getDLNAPN(String old) {
		if (DLNAPN.containsKey(old)) {
			//return DLNAPN.get(old);
			String[] n=DLNAPN.get(old);
			return n[0];
		}
		return old;
	}
		
	public String[] getDLNAPN_M(String old) {
		if (DLNAPN.containsKey(old)) {
			return DLNAPN.get(old);
		}
		//return old;
		String[] nw= new String[5];
		Arrays.fill(nw, "");
		nw[0]=old;
		return nw;
	}

	public boolean supportsFormat(Format f) {
		switch (f.getType()) {
			case Format.VIDEO:
				return isVideoSupported();
			case Format.AUDIO:
				return isAudioSupported();
			case Format.IMAGE:
				return isImageSupported();
			default:
				break;
		}
		return false;
	}

	public boolean isVideoSupported() {
		return getBoolean(VIDEO, true);
	}

	public boolean isAudioSupported() {
		return getBoolean(AUDIO, true);
	}

	public boolean isImageSupported() {
		return getBoolean(IMAGE, true);
	}

	public boolean isTranscodeToWMV() {
		return getVideoTranscode().startsWith(WMV);
	}

	public boolean isTranscodeToAC3() {
		return isTranscodeToMPEGPSAC3() || isTranscodeToMPEGTSAC3();
	}

	public boolean isTranscodeToMPEGPSAC3() {
		return getVideoTranscode().startsWith(MPEGPSAC3);
	}

	public boolean isTranscodeToMPEGTSAC3() {
		return getVideoTranscode().startsWith(MPEGTSAC3);
	}

	public boolean isAutoRotateBasedOnExif() {
		return getBoolean(AUTO_EXIF_ROTATE, false);
	}

	public boolean isTranscodeToMP3() {
		return getAudioTranscode().startsWith(MP3);
	}

	public boolean isTranscodeToLPCM() {
		return getAudioTranscode().startsWith(LPCM);
	}

	public boolean isTranscodeToWAV() {
		return getAudioTranscode().startsWith(WAV);
	}

	public boolean isTranscodeAudioTo441() {
		return getBoolean(TRANSCODE_AUDIO_441KHZ, false);
	}

	public boolean isH264Level41Limited() {
		return getBoolean(H264_L41_LIMITED, false);
	}

	public boolean isTranscodeFastStart() {
		return getBoolean(TRANSCODE_FAST_START, false);
	}

	public boolean isDLNALocalizationRequired() {
		return getBoolean(DLNA_LOCALIZATION_REQUIRED, false);
	}

	public String getMimeType(String mimetype) {
		if (isMediaParserV2()) {
			//Following FormatConfiguration.XXX and according mimetye must be defined at SupporteFormat=.. in Renderer.conf
			//otherwize, following getFormatConfiguration().match will fail and return null: will cause fail on transcode play
			if (mimetype != null && mimetype.equals(HTTPResource.VIDEO_TRANSCODE)) {
				mimetype = getFormatConfiguration().match(FormatConfiguration.MPEGPS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
				if (isTranscodeToMPEGTSAC3()) {
					mimetype = getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.MPEG2, FormatConfiguration.AC3);
				} else if (isTranscodeToWMV()) {
					mimetype = getFormatConfiguration().match(FormatConfiguration.WMV, FormatConfiguration.WMV, FormatConfiguration.WMA);
				}
			} else if (mimetype != null && mimetype.equals(HTTPResource.AUDIO_TRANSCODE)) {
				mimetype = getFormatConfiguration().match(FormatConfiguration.LPCM, null, null);  
				//PMS.dbg("RendererConf.getMimeType: AUDIO_TRANSCODE mimetype="+mimetype);
				if (mimetype != null) {
					if (isTranscodeAudioTo441()) {
						mimetype += ";rate=44100;channels=2";
					} else {
						mimetype += ";rate=48000;channels=2";
					}
				}
				if (isTranscodeToWAV()) {
					mimetype = getFormatConfiguration().match(FormatConfiguration.WAV, null, null);
				} 
				else if (isTranscodeToMP3()) {
					mimetype = getFormatConfiguration().match(FormatConfiguration.MP3, null, null);
				}
			}
			return mimetype;
		}
		if (mimetype != null && mimetype.equals(HTTPResource.VIDEO_TRANSCODE)) {
			mimetype = HTTPResource.MPEG_TYPEMIME;
			if (isTranscodeToWMV()) {
				mimetype = HTTPResource.WMV_TYPEMIME;
			}
		} else if (mimetype != null && mimetype.equals(HTTPResource.AUDIO_TRANSCODE)) {
			mimetype = HTTPResource.AUDIO_LPCM_TYPEMIME;
			if (mimetype != null) {
				if (isTranscodeAudioTo441()) {
					mimetype += ";rate=44100;channels=2";
				} else {
					mimetype += ";rate=48000;channels=2";
				}
			}
			if (isTranscodeToMP3()) {
				mimetype = HTTPResource.AUDIO_MP3_TYPEMIME;
			}
			if (isTranscodeToWAV()) {
				mimetype = HTTPResource.AUDIO_WAV_TYPEMIME;
			}
		}
		if (mimes.containsKey(mimetype)) {
			return mimes.get(mimetype);
		}
		return mimetype;
	}

	public boolean matchUserAgent(String userAgent) {
		return userAgentPattern.matcher(userAgent).find();
	}

	public boolean matchAdditionalUserAgent(String userAgent) {
		return userAgentAddtionalPattern.matcher(userAgent).find();
	}

	public String getUserAgent() {
		return getString(USER_AGENT, "^DefaultUserAgent");
	}

	public String getRendererName() {
		return getString(RENDERER_NAME, Messages.getString("PMS.17"));
	}

	public String getRendererIcon() {
		return getString(RENDERER_ICON, "unknown.png");
	}

	public String getUserAgentAdditionalHttpHeader() {
		return getString(USER_AGENT_ADDITIONAL_HEADER, null);
	}

	public String getUserAgentAdditionalHttpHeaderSearch() {
		return getString(USER_AGENT_ADDITIONAL_SEARCH, "DefaultUserAgent");
	}

	public String getUseSameExtension(String file) {
		String s = getString(USE_SAME_EXTENSION, null);
		if (s != null) {
			s = file + "." + s;
		} else {
			s = file;
		}
		return s;
	}

	public boolean isSeekByTime() {
		return getBoolean(SEEK_BY_TIME, true);
	}

	public boolean isMuxH264MpegTS() {
		boolean muxCompatible = getBoolean(MUX_H264_WITH_MPEGTS, true);
		if (isMediaParserV2()) {
			muxCompatible = getFormatConfiguration().match(FormatConfiguration.MPEGTS, FormatConfiguration.H264, null) != null;
		}
		if (Platform.isMac() && System.getProperty("os.version") != null && System.getProperty("os.version").contains("10.4.")) {
			muxCompatible = false; // no tsMuxeR for 10.4 (yet?)
		}
		return muxCompatible;
	}

	public boolean isDTSPlayable() {
		return isMuxDTSToMpeg() || (isWrapDTSIntoPCM() && isMuxLPCMToMpeg());
	}

	public boolean isMuxDTSToMpeg() {
		if (isMediaParserV2()) {
			return getFormatConfiguration().isDTSSupported();
		}
		return getBoolean(MUX_DTS_TO_MPEG, false);
	}

	public boolean isWrapDTSIntoPCM() {
		return getBoolean(WRAP_DTS_INTO_PCM, true);
	}

	public boolean isMuxLPCMToMpeg() {
		if (isMediaParserV2()) {
			return getFormatConfiguration().isLPCMSupported();
		}
		return getBoolean(MUX_LPCM_TO_MPEG, true);
	}

	public boolean isMpeg2Supported() {
		if (isMediaParserV2()) {
			return getFormatConfiguration().isMpeg2Supported();
		}
		return isPS3();
	}

	public String getVideoTranscode() {
		return getString(TRANSCODE_VIDEO, MPEGPSAC3);
	}

	public String getAudioTranscode() {
		return getString(TRANSCODE_AUDIO, LPCM);
	}

	public boolean isDefaultVBVSize() {
		return getBoolean(DEFAULT_VBV_BUFSIZE, false);
	}

	public String getMaxVideoBitrate() {
		return getString(MAX_VIDEO_BITRATE, null);
	}

	public String getCustomMencoderQualitySettings() {
		return getString(CUSTOM_MENCODER_QUALITYSETTINGS, null);
	}

	public int getMaxVideoWidth() {
		return getInt(MAX_VIDEO_WIDTH, 0);
	}

	public int getMaxVideoHeight() {
		return getInt(MAX_VIDEO_HEIGHT, 0);
	}

	public boolean isVideoRescale() {
		return getMaxVideoWidth() > 0 && getMaxVideoHeight() > 0;
	}

	public boolean isDLNAOrgPNUsed() {
		return getBoolean(DLNA_ORGPN_USE, true);
	}

	public String getTranscodedExtensions() {
		return getString(TRANSCODE_EXT, "");
	}

	public String getStreamedExtensions() {
		return getString(STREAM_EXT, "");
	}

	public long getTranscodedSize() {
		return getLong(TRANSCODED_SIZE, 0);
	}

	private int getInt(String key, int def) {
		try {
			return configuration.getInt(key, def);
		} catch (ConversionException e) {
			return def;
		}
	}

	private long getLong(String key, int def) {
		try {
			return configuration.getLong(key, def);
		} catch (ConversionException e) {
			return def;
		}
	}

	private boolean getBoolean(String key, boolean def) {
		try {
			return configuration.getBoolean(key, def);
		} catch (ConversionException e) {
			return def;
		}
	}
	
	private int rz_getInt(String key, int def) {
		String s=rz_getString(key, ""+def);
		return Integer.parseInt(s);
	}
	
	private float rz_getFloat(String key, double def) {
		String s=rz_getString(key, ""+def);
		return Float.parseFloat(s);
	}

	private boolean rz_getBoolean(String key, boolean def) {
		String val=rz_getString(key,"unknown");
		if(val.equals("true")) return true;
		else if(val.equals("false")) return false;
		else return def;
	}

	private String rz_getString(String key, String def) {//regzamod,  omit tail's #data
		String value = configuration.getString(key, def);
		if (value != null) {
			//---- regzamod, ignore comment parts begin with #
			int pos = value.indexOf("#");	
			if(pos>-1) {
				value=value.substring(0,pos);
			}
			//----
			value = value.trim();
		}
		if(value==null) {
			return def;
		}
		return value;
	}
	
	private String getString(String key, String def) {	//Original, don't omit tails's #data
		String value = configuration.getString(key, def);
		if (value != null) {
			value = value.trim();
		}
		return value;
	}

	public String toString() {
		return getRendererName();
	}

	public boolean isMediaParserV2() {
		return getBoolean(MEDIAPARSERV2, false) && MediaInfoParser.isValid();
	}
	
	//public void setMediaParserV2(boolean b) {
	//	configurarion.setProperty(MEDIAPARSERV2, b) ;
	//}
	
	public boolean isMediaParserV2ThumbnailGeneration() {
		return getBoolean(MEDIAPARSERV2_THUMB, false) && MediaInfoParser.isValid();
	}

	public boolean isForceJPGThumbnails() {
		return (getBoolean(FORCE_JPG_THUMBNAILS, false) && MediaInfoParser.isValid()) || isBRAVIA();
	}

	public boolean isShowAudioMetadata() {
		return getBoolean(SHOW_AUDIO_METADATA, true);
	}

	public boolean isShowSubMetadata() {
		return getBoolean(SHOW_SUB_METADATA, true);
	}

	public boolean isDLNATreeHack() {
		return getBoolean(DLNA_TREE_HACK, false) && MediaInfoParser.isValid();
	}

	public boolean isChunkedTransfer() {
		return getBoolean(CHUNKED_TRANSFER, false);
	}
}
