//----------------------------------------------------------------------
// RzAudioAsVideoTC , by regzamod
// play(transcode) audio as video
// patially, source codes bollowed from PMS for VIERA
//----------------------------------------------------------------------
package net.pms.encoders;

import java.awt.Font;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.IOException;
//import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JTextField;

import net.pms.Messages;
import net.pms.PMS;
import net.pms.dlna.DLNAMediaInfo;
import net.pms.dlna.DLNAMediaAudio;
import net.pms.dlna.DLNAMediaSubtitle;
import net.pms.dlna.DLNAResource;
import net.pms.formats.Format;
import net.pms.io.OutputParams;
//import net.pms.io.PipeIPCProcess;
import net.pms.io.PipeProcess;
import net.pms.io.ProcessWrapper;
import net.pms.io.ProcessWrapperImpl;
import net.pms.network.HTTPResource;
import net.pms.util.ProcessUtil;
import net.pms.configuration.PmsConfiguration;
import net.pms.configuration.RendererConfiguration;

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

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

public class RzAudioAsVideoTC extends Player {
	private static final Logger logger = LoggerFactory.getLogger(RzAudioAsVideoTC.class);
	public static final String ID = "rz_audio_as_video_tc";
	private final PmsConfiguration configuration;
	private static double dur_unk=10*3600; //duration(sec) for unknown duration audio (ex. net-radio)

	//String dummy = System.getProperty("user.dir")+"\\renderers\\images\\RzAudioAsVideo.jpg";
	String dummy = ".\\conf\\AudioAsVideo\\Images\\sample-%d.jpg";
	
	@Override
	public int purpose() {
		return VIDEO_SIMPLEFILE_PLAYER;
		//return AUDIO_SIMPLEFILE_PLAYER;
	}
	
	@Override
	public String id() {
		return ID;
	}
	
	@Override
	public boolean isTimeSeekable() {
		return true;
	}

	@Override
	public boolean avisynth() {
		return false;
	}

	private String overriddenArgs[];
	private String aav_params[];
	
	public RzAudioAsVideoTC(PmsConfiguration configuration) {
		this.configuration = configuration;
	}
	
	@Override
	public String name() {
		return "RzAudioAsVideoTC";
	}

	@Override
	public int type() {
		return Format.VIDEO;
	}
	
	protected String[] getDefaultArgs() {
		return new String[]{
			"dummy",
		};
	}
	
	@Override
	public String[] args() {
		return getDefaultArgs();
	}

	public boolean mplayer() {
		return false;
	}

	@Override
	public String mimeType() {
		return HTTPResource.VIDEO_TRANSCODE;
	}

	@Override
	public String executable() {
		return PMS.getConfiguration().getFfmpegPath();
	}

	@Override
	public ProcessWrapper launchTranscode(
		String audio_in,
		DLNAResource dlna,
		DLNAMediaInfo media,
		OutputParams params) throws IOException {
		
		RendererConfiguration renderer=params.mediaRenderer;
		String s;
			
		//---- tuning params for start playing 
		//params.manageFastStart();
		params.manageStart(params.mediaRenderer.getRZ_AavStartWait());  //set default
		
		if(PMS.rz_debug>1) {
			PMS.dbg("RzAudioAsVideoTC: waitbeforestart for normal (key_videotranscode_start_delay)="+configuration.getVideoTranscodeStartDelay());
			PMS.dbg("RzAudioAsVideoTC: waitbeforestart for aav (rz_aav_start_delay)="+params.waitbeforestart);
			PMS.dbg("RzAudioAsVideoTC: minFileSize (minwebbuffer)="+params.minFileSize);
			PMS.dbg("RzAudioAsVideoTC: minBufferSize (minvideobuffer)="+params.minBufferSize);
			PMS.dbg("RzAudioAsVideoTC: secondread_minsize="+params.secondread_minsize);
		}

		if(media!=null) {	
			setAudioAndSubs(audio_in, dlna, media, params, PMS.getConfiguration());
		}
		//---- create output pipe
		String sfx=".mpg";
		//if(renderer.getRZ_TransVFormat()==PMS.TRS_M2TS) {
		if(renderer.getRZ_TransVFormatAAV()==PMS.TRS_M2TS) {
			sfx=".m2ts";
		}
		PipeProcess pipe = new PipeProcess("aav_trans_out" + System.currentTimeMillis() + sfx);
		params.input_pipes[0] = pipe;
		
		//---------------------------------------------------------------------------
		// Setup params for player
		//---------------------------------------------------------------------------
		//---- setup params for the transcoder
		// RzAAVplayer.py {audio_in} {video_out} --clippath {path}
		//		-dbmode={db|simple} [media tags] [display info]
		
		String path=configuration.getRZ_aav_player_path();
		String[] paths=path.split(",");
		List<String> plist = new ArrayList<String>();
		
		//---- set main args ----------
		if(paths.length>1) {
			for (int i=0;i<paths.length;i++) {
				if(i==0 && paths[i].trim().equals("$python_path$")) {
					paths[i]=configuration.getRZ_python_path();
					//PMS.dbg("RzAudioAsVideoTC: got python_path="+paths[i]);
				}
				if(paths[i].length()>0) plist.add(paths[i].trim());
			}
		}
		else {
			plist.add(path);
		}

		//---- audio in ---------
		plist.add(audio_in);
		plist.add(pipe.getInputPipe());
		
		//s=params.sess.getRoot().visual_clip_path;
		s=params.sess.getRoot().getCurrentClipPath_v();
		s=ProcessUtil.getShortFileNameIfWideChars(s);  //to avoid unicode in path_name
		if(s!=null) {
			plist.add("--clippath");
			plist.add(s);
		}
		
		String temp_path=configuration.getTempFolder().getAbsolutePath();
		//---- set other options ----------
		plist.add("--temp");
		plist.add(temp_path);
		plist.add("--sess_id");
		plist.add(""+params.sess.id);

		plist.add("--selintvl");
		plist.add(""+configuration.getRZ_aav_clip_interval());
			
		plist.add("--selfit");
		plist.add(""+configuration.getRZ_aav_imgfit());
			
		plist.add("--seltype");
		plist.add(PMS.get().getFrame().getRza().getClipSelectBy());
			
		plist.add("--dpsize_w");
		plist.add(""+configuration.getRZ_aav_resize_w());
		plist.add("--dpsize_h");
		plist.add(""+configuration.getRZ_aav_resize_h());
		plist.add("--caption");
		plist.add((configuration.isRZ_aav_caption()?"1":"0"));
		plist.add("--h264");
		plist.add((renderer.isMuxH264MpegTS()?"1":"0"));
		plist.add("--m2ts");
		//plist.add((renderer.isRZ_TranscodeToM2ts()?"1":"0"));
		//plist.add((renderer.getRZ_TransFormat().equals("m2ts")?"1":"0"));
		//plist.add((renderer.getRZ_TransVFormat()==PMS.TRS_M2TS?"1":"0"));
		plist.add((renderer.getRZ_TransVFormatAAV()==PMS.TRS_M2TS?"1":"0"));
		plist.add("--asp_hack");
		plist.add(""+renderer.getRZ_AspectHack());
		plist.add("--filename");
		plist.add(dlna.getName());	//audio_in may be Windows ShortNamed
		plist.add((renderer.isMuxH264MpegTS()?"1":"0"));
		
		/* moved later
		if(params.timeseek>0) {
			plist.add("--timeseek");
			plist.add(""+params.timeseek);
		}
		*/
		String str=null;
		String title=null;
		if(media!=null) {
			if(media.getDurationInSeconds()>0) {
				//pw.println("duration="+media.getDurationInSeconds());
				plist.add("--duration");
				plist.add(""+media.getDurationInSeconds());
			}
			if(media.getAudioCodes().size() > 0) {
				DLNAMediaAudio audio=media.getAudioCodes().get(0);
				if(audio!=null) {
					//---- media basic info
					//pw.println("acodec="+audio.getAudioCodec());
					plist.add("--acodec");
					plist.add(audio.getAudioCodec());
					
					//pw.println("channel="+audio.getNrAudioChannels());
					plist.add("--channel");
					plist.add(""+audio.getNrAudioChannels());
					
					//pw.println(String.format("kbps=%.1f",media.getBitrate()/1000.0));
					plist.add("--kbps");
					plist.add(String.format("%.1f",media.getBitrate()/1000.0));

					//---- media tags
					str=audio.getGenre();
					if(str!=null && !str.isEmpty()) {
						plist.add("--genre");
						plist.add(str);
					}
					str=audio.getArtist();
					if(str!=null && !str.isEmpty()) {
						plist.add("--artist");
						plist.add(str);
					}
					str=audio.getAlbum();
					if(str!=null && !str.isEmpty()) {
						plist.add("--album");
						plist.add(str);
					}
					str=audio.getSongname();
					if(str!=null && !str.isEmpty()) {
						title=str;
					}
				}
			}			
		}
		if(title==null) {  
			title=dlna.getName();
		}
		plist.add("--title");
		plist.add(title);
		
		//---- opt args in url (web.conf)
		String [] uopts=dlna.getUrlParams();
		String opt_str="";
		if(uopts!=null) {
			for(int i=3;i<uopts.length;i++) {
				if(uopts[i]!=null) {
					opt_str = uopts[i].trim();
					plist.add(opt_str);
				}
			}
		}
		
		//---- opt args from scripts
		int auto_play=params.sess.getSessAutoPlay();
		if(params.sess.rz_SysMetaAAVOpt!=null && params.sess.rz_SysMetaAAVOpt.length()>0) {	
			String [] sopts=params.sess.rz_SysMetaAAVOpt.split(",");
			for(String s1 :sopts) {
				if(s1.startsWith("--")) {
					//may be OptString type ex. "--opt arg"
					//separate "--opt arg" to "--opt","arg" for matching parog's param synax
					String[] sm=s1.split("[\\s]+");  //split by spaces
					
					if(sm.length>1 && sm[0].equals("--auto_play")) { //override by scripts
						//--- for special opts
						auto_play=Integer.parseInt(sm[1]); 
						if(PMS.rz_debug>1) PMS.dbg("Rz_AudioAsVideoTC: in rz_SysMetaAAVOpt, found auto_play="+auto_play);
					}
					else {
						//--- path through to AAV_player
						for(String s2 :sm) {
							plist.add(s2);
						}
					}
				}
				else plist.add(s1);
			}
		}
		
		double timeseek=params.timeseek;
		double timeseek_sv=timeseek;
		//--------------------------------------------------------------------------
		// for Autoplay Start (=0:Non, =1:serial, =2:random)
		//--------------------------------------------------------------------------
		if(auto_play>0) {
			float auto_play_igap=renderer.getRZ_AAV_AutoPlayIgap(); // inter audio gap time(sec)
			plist.add(""+auto_play_igap);
			
			//---- assert temp folder
			String aav_wkdir=temp_path+"/aav";
			File af=new File(aav_wkdir);
			if(!af.exists()) {
				af.mkdir();
			}
			if(!af.isDirectory()) {
				logger.error("RzAudioAsVideoTC: failed to create work folder, path="+af.getAbsolutePath());
				return null;
			}
			
			//---- create autoplay_list file
			//File f=new File(temp_path+"/aav","audiolist("+params.sess.id+").dat");
			File f=new File(af,"audiolist("+params.sess.id+").dat");
			double out[]={0,0};  //for return value, [0]:duration_total, [1]:list count
			int stt_pos=alistWrite(dlna,f,out);
			if(stt_pos<0) {
				logger.error("RzAudioAsVideoTC: failed to create audiolist, path="+f.getPath());
				return null;
			}
			double dur_ttl=out[0];  //total duration(playtime, sec) in audio list
			dur_ttl+=auto_play_igap*out[1];
			
			plist.add("--auto_play");
			plist.add(""+auto_play);
			plist.add("--auto_play_list");
			plist.add(f.getAbsolutePath());
			plist.add("--auto_play_igap");
			plist.add(""+auto_play_igap);
			
			//--------------------------------------------------------------------------
			// Judge play pos(next/prev) in autoPlay list
			//--------------------------------------------------------------------------
			// ToDO : [stt_pos1_sb] should be real last played pos on player side.
			//        b/c following [stt_pos1_prev] calced by server side is not accurate.
			//        especially, when some audio's duration in list are unknown.
			// HowTo: Read lastPlayPos info from player's log, or do whole followings on player side
			//--------------------------------------------------------------------------
			if(renderer.getRZ_AAV_AutoPlayTrics()>0) {  //test for play next/prev operation
				if(PMS.rz_trace>1) {
					PMS.trace("==== RzAudioAsVideoTC: RZ_AAV_AutoPlayTrics ON");
					PMS.trace("RzAudioeAsVideoTC: TrickJudge Start"
						+", pos0(terminal_new)="     +stt_pos
						+", pos0(terminal_cur)="     +params.sess.playPos0
						+", pos1(inner_cur)="        +params.sess.playPos1
						+", pos1(inner_base)="       +params.sess.playPos1_base
						+", params.timeseek(sec)="   +(timeseek)
						+", sess.playTime(sec)="     +(params.sess.playTime/1000.0)
						+", sess.playTimeTotal(sec)="+(params.sess.playTimeTotal/1000.0)
						+", sess.seek_bias="+(params.sess.seek_bias)
					);
				}

				if(params.sess.playPos0<0) params.sess.playPos0=stt_pos;
				if(params.sess.playPos1<0) params.sess.playPos1=stt_pos;
				if(params.sess.playPos1_base<0) params.sess.playPos1_base=stt_pos;

				//--- real pos on list
				int dir=stt_pos-params.sess.playPos0;  // =0: same pos, >0:next, <0: prev
				int stt_pos1;	 // new pos as the result of seek
				int stt_pos1_sb; // base pos for seek
				if(dir!=0) {// next/prev
					stt_pos1_sb=params.sess.playPos1;   	//current PlayPos on serverSide
				}
				else {// pause/skip
					stt_pos1_sb=params.sess.playPos1_base;	//current BasePos on serverSide
				}
				stt_pos1=stt_pos1_sb;  // set fale safe
				
				if(true) {
					//this method is good for auto_play=1(serial)
					//but, for auto_play=2(shuffle): this can't handle pause/restart, think better way 
					if(params.sess.playTimeTotal>60*1000) {  //total playtime from autoPlay started, to ignore preview play 
						long new_seek[]={params.sess.playTime};
						double seek_add=0,seek=0;
						
						if(dir!=0) { // next/prev
							seek=params.sess.playTime/1000.0-params.sess.seek_bias;
						}
						else {  // pause/skip
							seek=timeseek-params.sess.seek_bias;;
						}
						if(seek>dur_ttl) {  //cycled in list
							double seek0=seek;
							seek=Math.IEEEremainder(seek,dur_ttl);  //seek in last cycle 
							seek_add=seek0-seek;  //memory cycles
						}
						
						stt_pos1=getNextAudio(dlna,stt_pos1_sb,dir,seek,auto_play_igap,new_seek);
						new_seek[0] +=seek_add;  //recover cycles
						
						if(dir!=0) { // next/prev
							params.sess.playTime=0;  //playtime from current audio
							params.sess.seek_bias=0;
							params.sess.playPos1_base=stt_pos1;
						}
						else { // pause/skip
							timeseek -= (new_seek[0]/1000.0);   
						}
						params.sess.playPos0=stt_pos;
						params.sess.playPos1=stt_pos1;
					}
					else {
						stt_pos1=stt_pos;
						params.sess.playPos0=stt_pos;
						params.sess.playPos1=stt_pos;
					}
					if(timeseek_sv>0) {  //original timeseek
						//---- Only for IAV, memory seek miss-match  
						//---- b/c currently IAV player can't accurate timeseek and bigin clip_itvl*n near the timeseek
						//double delta=Math.IEEEremainder(timeseek,clip_itvl);
						//params.sess.seek_bias=delta;  // timeseek- clip_itvl*n  

						//---- calib playTime by timeseek
						double diff=(long)(timeseek_sv*1000)-params.sess.playTime;
						params.sess.playTime=(long)(timeseek_sv*1000);
						params.sess.playTimeTotal+=diff;
					}
				}
				plist.add("--auto_play_spos");
				plist.add(""+stt_pos1); 
				
				if(PMS.rz_trace>1) {
					PMS.trace("RzAudioeAsVideoTC: TrickJudge End "
						+", pos0(terminal_new)="     +stt_pos
						+", pos0(terminal_cur)="     +params.sess.playPos0
						+", pos1(inner_cur)="        +params.sess.playPos1
						+", pos1(inner_base)="       +params.sess.playPos1_base
						+", params.timeseek(sec)="   +(timeseek)
						+", sess.playTime(sec)="     +(params.sess.playTime/1000.0)
						+", sess.playTimeTotal(sec)="+(params.sess.playTimeTotal/1000.0)
						+", sess.seek_bias="+(params.sess.seek_bias)
					);
				}
			}
			else {
				plist.add("--auto_play_spos");
				plist.add(""+stt_pos);  //absolute pos
			}
		}
		//--------------------------------------------------------------------------
		// for Autoplay End
		//--------------------------------------------------------------------------

		if(timeseek>0) {
			plist.add("--timeseek");
			plist.add(""+timeseek);
		}
		
		//---------------------------------------------------------------------------
		// Now, all params fixed: Let's kick player
		//---------------------------------------------------------------------------
		//---- set params to cmdarray
		String cmdArray[] = plist.toArray(new String[plist.size()]);

		//---- setup pipe process
		ProcessWrapper mkfifo_process = pipe.getPipeProcess();

		//---- setup player(transcoder process)
		cmdArray = finalizeTranscoderArgs(
			this,
			audio_in,
			dlna,
			media,
			params,
			cmdArray);

		ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, params);
		pw.attachProcess(mkfifo_process);
		
		//---- run pipe process
		mkfifo_process.runInNewThread();
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
		}
		pipe.deleteLater();

		//---- run player(transcoder process)
		pw.runInNewThread(dlna);
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
		}
		return pw;
	}

	//get audio to play: stt_pos + seek in audio list
	private int getNextAudio (DLNAResource dlna, int stt_pos, int dir, double seek, float igap, long new_seek[]) {
		DLNAResource pa=null,ch;
		DLNAMediaInfo media=null;
		DLNAMediaAudio audio=null;
		int pos=0,apos=0,apos_prev=0,apos_prev0=-1,apos_next0=-1;
		int cnt=0;
		double ttime=0,ttime_prev=0,ttime_prev2=0;

		if(PMS.rz_trace>1) PMS.trace("getNextAudio: Start, stt_pos="+stt_pos+", dir="+dir+", seek(sec)="+seek+", igap(sec)="+igap);
		
		if(dlna==null) return stt_pos;
		pa=dlna.getParent();
		if(pa==null) return stt_pos;
		int lmax=pa.getChildren().size();
		boolean found=false;
		int found2=0;
		double dur=0;
		
		//for (DLNAResource ch : pa.getChildren()) {
		while (true) {
			if(PMS.rz_debug>1) PMS.dbg("getNextAudio: pos="+pos);
			ch=pa.getChildren().get(pos);
			if(found && apos_next0<0) { //previously found stt_pos
				apos_next0=apos;  //memory valid next of stt_pos
			}
			if(!found && apos>=stt_pos) {
				found=true; //found stt_pos
				if(apos_prev0<0) apos_prev0=apos_prev; //memory valid prev of stt_pos
			}
			if(!ch.isFolder() && ch.getExt()!=null && ch.getExt().isAudio()) {
				if(found) {
					//set duration(sec) default, for that have no mediainfo, ex. web contents
					//long dur enable next/prev ope, even if accurate dur is unknown
					dur=dur_unk; //default secs
					media=ch.getMedia();
					if(media!=null) {
						if(media.getAudioCodes().size() > 0) {
							audio=media.getAudioCodes().get(0);
							if(audio!=null) {
								if(media.getDurationInSeconds()>0) {
									dur=media.getDurationInSeconds()+igap;
								}
							}
						}
					}
					
					ttime_prev2=ttime_prev;
					ttime_prev=ttime;
					ttime+=dur;
					
					if(PMS.rz_debug>1) {
						PMS.dbg("getNextAudio:"
							+" found audio in list, apos="+apos
							+", dur(sec)="+(dur)
							+", total time(sec)="+ttime
						);
					}
					
					if(dir==0) {  //same pos
						if(ttime>seek) {
							ttime=ttime_prev;   //current pos
							break;
						}
					}
					else if(dir>0) {   //next pos
						if(found2>0) {
							ttime=ttime_prev;
							break;
						}
						else if(ttime>seek) {
							found2=1;  //break on next audio pos
						}
					}
					else {  //prev pos
						if(ttime>seek) {
							apos=apos_prev;	//get prev audio pos
							ttime=ttime_prev2;  
							break;
						}
					}
				}
				apos_prev=apos;
				apos++;
			}
			pos++;
			if(pos>=lmax) {
				apos=0;  //pos in audio children
				pos=0;   //pos in all children
			}
			cnt++;
			if(cnt>lmax*2) {   //fale safe: avoid infinit loop
				//come here in case of list has unknown duration audios: seek will never over the duration 
				if(PMS.rz_debug>1) PMS.dbg("getNextAudio: apos not found, return original apos");
				if(dir==0) apos=stt_pos;  //same 
				else if(dir>0) apos=apos_next0;  //next
				else apos=apos_prev0; //prev
				break;
			}
		}
		if(new_seek!=null) {
			new_seek[0]=(long)(ttime*1000);  //set to target audio's start time in audio list
		}
		return apos;
	}
	
	//create and write audio_palylist
	private int alistWrite (DLNAResource dlna, File ofile, double out[]) {
		
		DLNAResource pa=dlna.getParent();
		PrintWriter pw=null;
		try {
			pw = new PrintWriter(ofile,"UTF-8");  //force encoding utf-8
		} catch (IOException e) {
			PMS.dbg("alistWrite: file open error="+e.getMessage());
		}
		if(pw==null) {
			return -1;
		}
		
		double dur_ttl=0; 	//total audio play time
		int cnt_ttl=0;		//total audio count in list
		if(out!=null) {
			out[0]=0;  	//dur_ttl
			out[1]=0;	//cnt_ttl
		}
		
		String str=null;
		DLNAMediaInfo media=null;
		int stt_pos=0;
		int i=0;
		String title;
		
		for (DLNAResource ch : pa.getChildren()) {
			if(ch.isFolder()) continue;
			if(ch.getExt()!=null && ch.getExt().isAudio()) {
				if(dlna==ch) {
					if(PMS.rz_debug>1) PMS.dbg("rz_ImageAsVideoTC: found stt_pos="+i);
					stt_pos=i;
				}
				
				//--- audio file path
				pw.println("path="+ch.getSystemName());  // use shortName(getShortFileNameIfWideChars)
				//pw.println("path="+ch.getSrcPath());  // Bad when unicode-filename: can't pass it as Python Process() arg
				title=null;
				double dur=dur_unk;
				
				//--- audio info
				media=ch.getMedia();
				DLNAMediaAudio audio=null;
				if(media!=null) {
					if(media.getAudioCodes().size() > 0) {
						audio=media.getAudioCodes().get(0);
					}
				}
				if(audio!=null) {
					//---- media basic info
					pw.println("acodec="+audio.getAudioCodec());
					pw.println("channel="+audio.getNrAudioChannels());
					pw.println(String.format("kbps=%.1f",media.getBitrate()/1000.0));
					if(media.getDurationInSeconds()>0) {
						pw.println("duration="+media.getDurationInSeconds());
						dur=media.getDurationInSeconds();
					}
					
					//---- media tags
					str=audio.getGenre();
					if(str!=null && !str.isEmpty()) {
						pw.println("genre="+str);
					}
					str=audio.getArtist();
					if(str!=null && !str.isEmpty()) {
						pw.println("artist="+str);
					}
					str=audio.getAlbum();
					if(str!=null && !str.isEmpty()) {
						pw.println("album="+str);
					}
					str=audio.getSongname();
					if(str!=null && !str.isEmpty()) {
						//pw.println("title="+str);
						title=str;
					}
				}
				if(title!=null) {  
					pw.println("title="+title);
				}
				else {//not found in tags
					pw.println("title="+ch.getName());
				}
				dur_ttl+=dur;
				cnt_ttl+=1;
				i+=1;
			}
		}
		pw.close();
		if(out!=null) {
			out[0]=dur_ttl;
			out[1]=cnt_ttl;
		}
		return stt_pos;
	}
	
	@Override
	public JComponent config() {
		return null;
	}
}
