package net.pms.dlna;

//import java.io.*;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.util.ArrayList;

import net.pms.PMS;
import net.pms.formats.Format;
import net.pms.configuration.RendererConfiguration;
import net.pms.util.PMSUtil;	// regzamod
import net.pms.util.FileUtil;	// regzamod
//import net.pms.dlna.MapFile;	// regzamod

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

public class PlaylistFolder extends DLNAResource {
	private static final Logger logger = LoggerFactory.getLogger(PlaylistFolder.class);
	private File playlistfile;
	private boolean valid = true;

	public File getPlaylistfile() {
		return playlistfile;
	}

	public PlaylistFolder(File f) {
		playlistfile = f;
		sotype=SO_PLAYLIST;
		rz_MetaSortType=PMS.SORT_ORIGIN;
		setLastmodified(playlistfile.lastModified());
		//setLastmodified(System.currentTimeMillis()); //cause nullpointer exception, why?
	}

	@Override
	public boolean  needSort() {	//regzamod
		//children(play list) shouldn't be sorted !!
		return true;
	}

	@Override
	public InputStream getInputStream() throws IOException {
		return null;
	}

	@Override
	public String getName() {
		return playlistfile.getName();
	}
	@Override
	public String getDpName() {	//regzamod
		return getName();
	}

	@Override
	public String  getSrcPath() {	//regzamod
		return playlistfile.getAbsolutePath();
	}

	@Override
	public String getSystemName() {
		return playlistfile.getName();
	}

	@Override
	public boolean isFolder() {
		return true;
	}

	@Override
	public boolean isValid() {
		return valid;
	}

	@Override
	public long length() {
		return 0;
	}
	
	@Override
	public void discoverChildren() {	//regzamod, add
		//main porpose of this func: list up all children for later analyzeChildren
		//shoud be more refined, as for exec conditions
	}

	@Override
	public boolean analyzeChildren(int count) {	//regzamod, add
		//analyzeChildren may process partialy, upto count.
		//so, max counts must be knowned before, i.e. by discoverChildren()
		//shoud be more refined, as for exec conditions
		resolve_in();
		return true;
	}
	
	@Override
	public boolean isRefreshNeeded() {
		boolean needf=false;
		File folder;
		long systim=System.currentTimeMillis();
		long lastim=getLastRefreshTime();
		
		setLastRefreshTime(systim);
		
		if(PMS.rz_trace>0) PMS.trace("PlaylistFolder.isRefreshNeeded: Start, rz_update_check_pls="+PMS.rz_update_check_pls);
		
		if(PMS.rz_update_check_pls>0) {
			//long stime=20000; //20000msec= 20sec
			long stime=PMS.rz_update_check_min;
			if(lastim+stime > systim) {  //a trickey way! to avoid recreate when simultanious getList-call before re-creation finishes
				if(PMS.rz_trace>0) PMS.trace("PlaylistFolder.isRefreshNeeded: PlaylistFolder.isRefreshNeeded: return FALSE "
					+"(recently updated (within " +stime +" msec)" );
				return false;
			}
		}
		else {
			if(PMS.rz_trace>0) PMS.trace("PlaylistFolder.isRefreshNeeded: PMS.rz_update_check_pls<=0: need No Check, return FALSE");
			return false;
		}
		
		//check accurately
		File folder_prev=null;
		for(DLNAResource ch : getChildren()) {
			if((PMS.rz_update_check_pls&0x1)!=0) { // check pxf new createed
				folder=ch.getBodysParentFolder();
				if(folder_prev!=folder) {
					folder_prev=folder;
					//if(folder!=null && folder.lastModified() > getLastmodified()) {
					if(folder!=null && folder.lastModified() > lastim) {
						if(PMS.rz_trace>1) PMS.trace("PlaylistFolder.isRefreshNeeded: folder.lastModified()>getLastmodified(), folder="+folder.getPath());
						needf=true;
						break;
					}
				}
			}
			if((PMS.rz_update_check_pls&0x2)!=0) { // check pxf modified 
				if(ch.needReCreate()) {
					if(PMS.rz_trace>1) PMS.trace("PlaylistFolder.isRefreshNeeded: ch.needReCreate()=true, ch="+ch.getSrcPath());
					needf=true;
					break;
				}
			}
		}
		if(PMS.rz_trace>0) {
			long tim2=System.currentTimeMillis();
			PMS.trace("PlaylistFolder.isRefreshNeeded: End, return "+needf+", Elapsed(msec)="+(tim2-systim) );
		}
		setLastRefreshTime(systim);
		return needf;
	}
	
	@Override
	public void refreshChildren() {	//regzamod, add
		if(PMS.rz_debug>1) PMS.dbg("PlaylistFolder.refreshChildren: child need refresh --> exec refresh");
		
		// TODO: this is whole list re-creation: expensive way. 
		// shud be refined to re-create only needed child.
		resolved=false;
		//PMS.dbg("PlaylistFolder.refreshChildren: Playlist="+getName()+", needf=true, exec resolve_in()");
		resolve_in();
		//setLastmodified(System.currentTimeMillis());
	}

	@Override
	public boolean needReCreate() {	//regzamod, add
		if(getLastmodified()<playlistfile.lastModified()) {
			//PMS.dbg("PlaylistFolder.needReCreate: date unmatch, may be updated --> return true");
			return true;
		}
		//PMS.dbg("PlaylistFolder.needReCreate: date match, may be not updated --> return false");
		return false;
	}
	
	//regzamod, Origilary, this.resolove() called twice when parent dir opened.
	//and NOT called when PlaylistFolder its'self opend. why?
	//following is temporaly fix: should be revised
	private synchronized void resolve_in() {
		long tim1=System.currentTimeMillis();
		if(PMS.rz_trace>0) PMS.trace("PlaylistFolder.resolve_in: Start");
		
		if (PMS.rz_debug>1) {	//regzamod
			PMS.dbg("PlaylistFolder: ===> resolve called name="+playlistfile.getName());	//regzamod
			PMS.dbg("PlaylistFolder: childrenNumber()="+childrenNumber());	//regzamod
		}
		
		if(true) {
			if(resolved) return;
		}
		//logger.info("PlaylistFolder: register children");	//regzamod
		//Once, delete all children, othewise old children remain !!		
		//all childlen will be re-created by following AddChild 
		//getChildren().clear();	// regzamod
		ClearChildren();
		deleteMenus();
		
		String enc=PMS.getConfiguration().getRZ_PlaylistEncode();
		BufferedReader br;
		//logger.info("PlaylistFolder.resolve: Playlist Text Encode="+enc);
			
		if (playlistfile.length() < 10000000) {
			ArrayList<Entry> entries = new ArrayList<Entry>();
			boolean m3u = false;
			boolean pls = false;
			boolean pls_noExtend=false;
			
			int skip_bytes[]={0};
			try {
				if(enc.equals((String)"AutoDetect")) {	//regzamod
					enc=FileUtil.rzCharaCodeDetect(playlistfile.getPath(),skip_bytes);
					if(enc==null) {
						enc=PMS.get().getDefaultTextEncode();
						logger.warn("PlaylistFolder: failed to autodetect text encode, use default enc="+enc
							+", PlayList="+playlistfile.getPath());
					}
				}
				FileInputStream fis=new FileInputStream(playlistfile);
				if(skip_bytes[0]>0) {	// Unicode BOM exists
					fis.skip(skip_bytes[0]);
				}
				if(enc!= null) {
					br = new BufferedReader(new InputStreamReader(fis,enc));	//regzamod
				}
				else {
					br = new BufferedReader(new FileReader(playlistfile));	// Original
				}
				
				String line;
				while (!m3u && !pls && (line = br.readLine()) != null) {
					line = line.trim();
					if (line.startsWith("#EXTM3U")) {
						m3u = true;
						if(PMS.rz_debug>1) logger.debug("Reading m3u playlist: " + playlistfile.getName());
					} else if (line.length() > 0) {
						if (line.equals("[playlist]")) {
							pls = true;
							if(PMS.rz_debug>1) logger.debug("Reading PLS playlist: " + playlistfile.getName());
						}
					}
				}			
				if(pls==false && playlistfile.getName().toLowerCase().endsWith(".pls")) { //regzamod
					//may be plain pls without No Extensions
					pls=true;
					pls_noExtend=true;
					// you reached tail of file, in case of above-tag not found.
					// so, must rewind the read pos, but awwww!! StreamReader can't --> re-open fis/br
					br.close();
					fis.close();
					fis=new FileInputStream(playlistfile);
					if(enc!= null) {
						br = new BufferedReader(new InputStreamReader(fis,enc));	//regzamod
					}
					else {
						br = new BufferedReader(new FileReader(playlistfile));	// Original
					}
				}
				PMS.dbg("PlaylistFoledr: pls="+pls+", pls_noExtend="+pls_noExtend+", m3u="+m3u);

				String fileName = null;
				String title = null;
				int num=0;
				while ((line = br.readLine()) != null) {
					line = line.trim();
					if (pls) {
						if(line.length() > 0 && !line.startsWith("#")) {
							//PMS.dbg("PlaylistFoledr: valid line="+line);
							if(pls_noExtend) {// add regzam, no-extension format: i.e. simple list of filepath
								Entry entry = new Entry();
								entry.fileName = line;
								File f=new File(line);
								entry.title=f.getName();
								entries.add(entry);
								//PMS.dbg("PlaylistFoledr: Add pls_noExtend entry.fileName="+entry.fileName+", title="+entry.title);
							} //end of noExtend
							else {
								int eq = line.indexOf("=");
								if (eq != -1) {
									String value = line.substring(eq + 1);
									String var = line.substring(0, eq).toLowerCase();
									//fileName = null;
									title = null;
									int index = 0;
									if (var.startsWith("file")) {
										index = Integer.valueOf(var.substring(4));
										fileName = value;
									} else if (var.startsWith("title")) {
										index = Integer.valueOf(var.substring(5));
										title = value;
									}
									if (index > 0) {
										while (entries.size() < index) {
											entries.add(null);
										}
										Entry entry = entries.get(index - 1);
										if (entry == null) {
											entry = new Entry();
											entries.set(index - 1, entry);
										}
										if (fileName != null) {
											entry.fileName = fileName;
										}
										if (title != null) {
											entry.title = title;
										}
										else if(fileName!=null) {  // add regzam, for empty title
											File f=new File(fileName);
											entry.title=f.getName();
										}
									} //Line contains index
								} //line contains "="
							} //ExtendFormat
						} //line.length()>0
					} else if (m3u) {
						if (line.startsWith("#EXTINF:")) {
							line = line.substring(8).trim();
							if (line.matches("^-?\\d+,.+")) {
								title = line.substring(line.indexOf(",") + 1).trim();
							} else {
								title = line;
							}
						} else if (line.length() > 0 && !line.startsWith("#")) {
							fileName = line;
							Entry entry = new Entry();
							entry.fileName = fileName;
							entry.title = title;
							entries.add(entry);
							title = null;
						}
					}
				}
				br.close();
				fis.close();
			} catch (NumberFormatException e) {
				logger.error(null, e);
			} catch (IOException e) {
				logger.error(null, e);
			}
			int cnt=0;
			for (Entry entry : entries) {
				if (entry == null) {
					continue;
				}
				String fileName = entry.fileName;
				if(PMS.rz_debug>1) logger.trace("Adding " + (pls ? "PLS " : (m3u ? "M3U " : "")) + "entry: " + entry);
				//if (!fileName.toLowerCase().startsWith("http://") && !fileName.toLowerCase().startsWith("mms://")) {
				if(fileName.toLowerCase().startsWith("http://")
					|| fileName.toLowerCase().startsWith("https://")
					|| fileName.toLowerCase().startsWith("rtmp://")
					|| fileName.toLowerCase().startsWith("rtsp://")
					|| fileName.toLowerCase().startsWith("mms://")) {
					//--- web contents
					WebVideoStream ch1=new WebVideoStream(entry.title, fileName, null);
					ch1.setSerialId(++cnt);
					addChild(ch1);
				}
				else {
					//--- local real file
					File en1 = new File(playlistfile.getParentFile(), fileName);
					File en2 = new File(fileName);
					File en=null;
					
					//logger.info("en1=" +en1.getAbsolutePath()+ "Exist="+en1.exists());	//regzamod
					//logger.info("en2=" +en2.getAbsolutePath()+ "Exist="+en2.exists());	//regzamod
					boolean metaf=false;
					if(fileName.toLowerCase().endsWith("."+PMS.rz_MetafileSuffix))	{	//regzamod
						metaf=true;
					}
					DLNAResource ch=null;
					if(en1.exists()) en=en1;
					else if(en2.exists()) en=en2;
					if (en!=null) {
						if(metaf) {
							//ch=new rz_PlayMetaFile(en, entry.title);
							ch=rz_PlayMetaFile.managePxm(en, null, 1);
						}
						else {
							ch=new RealFile(en, entry.title);
						}
						if(ch!=null) {
							ch.setSerialId(++cnt);
							addChild(ch);
							//valid = true;
							File pxf=new File(en.getAbsolutePath()+"."+PMS.rz_ProfileSuffix);
							//PMS.dbg("PlaylistFolder.resolve_in: entry path="+en.getAbsolutePath());
							if(pxf.exists()) {	// pxf exists
								//PMS.dbg("PlaylistFolder.resolve_in: exec pxf_read");
								rz_PlayMetaFile.pxf_read(ch,pxf);
							}
							if(entry.title!=null) { //override pxf/pxm's title
								ch.dname=entry.title;
							}
						}
					}
				}
			}
			if(cnt>0) valid=true;
		}
		
		long tim2=System.currentTimeMillis();
		resolved=true;
		setLastRefreshTime(tim2);
		setLastmodified(tim2);
		
		if(PMS.rz_trace>0) {
			PMS.trace("PlaylistFolder.resolve_in: End, Elapsed(msec)="+(tim2-tim1));
		}

	}

	private static class Entry {
		public String fileName;
		public String title;

		@Override
		public String toString() {
			return "[" + fileName + "," + title + "]";
		}
	}
	
	//---- regzamod, add end --------------------------------

}
