/*
 * PS3 Media Server, for streaming any medias to your PS3.
 * Copyright (C) 2008  A.Brochard
 *
 * 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.
 */
package net.pms.dlna;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import net.pms.PMS;
import net.pms.io.OutputParams;
import net.pms.io.ProcessWrapperImpl;
import net.pms.util.FileUtil;
import net.pms.util.ProcessUtil;
import net.pms.formats.Format;
// Ditlew
import net.pms.configuration.RendererConfiguration;

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

//public class DVDISOTitle extends DLNAResource {
public class DVDISOTitle extends DLNAResource implements DVDISOTitleIF {
	private static final Logger logger = LoggerFactory.getLogger(DVDISOTitle.class);
	private File f;
	private int title;
	private String chapter_str;
	private long length;
	//private boolean resolved;
	private String mediaPath;
	private String mediaCachePath;
	//private String name;
	private static String DB_DVD_PATH_PREFIX="[DVD_DEV://]"; //dvd specific prefix of mediapath in mediaDB 

	public DVDISOTitle(File f, int title) {
		this.f = f;
		this.title = title;
		setLastmodified(f.lastModified());
		if(PMS.rz_debug>1) PMS.dbg("DVDISOTitle: f.getName="+f.getName());
		if(f.getName().equals("VIDEO_TS")) {
			sotype=SO_DVDDEV;	//may be on real DVD dev
			mediaPath=String.format("%s/Title%d/%d",DB_DVD_PATH_PREFIX,title,getLastmodified());
			mediaCachePath=String.format("%s/Title%d",DB_DVD_PATH_PREFIX,title);
			//mediaCachePath contains wildcard(of H2DB SQL), so must be escaped by '<' for treated literally
			mediaCachePath=mediaCachePath.replace("%", "<%").replace("_", "<_").replace("'", "<'")+"%"; 
		}
	}
	public DVDISOTitle(File f, int title, String chapter_str) {	//regzamod, add
		this.f = f;
		this.title = title;
		this.chapter_str = chapter_str;
		setLastmodified(f.lastModified());
		if(PMS.rz_debug>1) PMS.dbg("DVDISOTitle: f.getName="+f.getName());
		if(f.getName().equals("VIDEO_TS")) {
			sotype=SO_DVDDEV;	//may be on real DVD dev
			mediaPath=String.format("%s/Title%d/%d",DB_DVD_PATH_PREFIX,title,getLastmodified());
			//mediaCachePath use wildcard, escape is '<' 
			mediaCachePath=String.format("%s/Title%d",DB_DVD_PATH_PREFIX,title);
			mediaCachePath=mediaCachePath.replace("%", "<%").replace("_", "<_").replace("'", "<'")+"%"; 
		}
	}

	@Override
	public boolean isRefreshNeeded() {
		if(!f.exists()) {
			if(PMS.rz_debug>1) PMS.dbg("DVDISOTitle.isRefreshNeeded: file not exist path="+f.getAbsolutePath());
			return false;
		}
		if(f.lastModified()!=getLastmodified() || !resolved) {
			//PMS.dbg("DVDISOTitle.isRefreshNeeded: Lastmodified differ file.lastModified, return true");
			resolved=false;
			return true;
		}
		return false;
	}
	
	@Override
	public boolean needReCreate() {
		return (!resolved);
	}

	@Override
	public void resolve() {
		//---- regzamod add start
		//PMS.dbg("DVDISOTitle.resolve: name="+getName()+"path="+f.getAbsolutePath()+", resolved="+resolved);
		if(!f.exists()) {
			if(PMS.rz_debug>1) PMS.dbg("DVDISOTitle.resolve: file not exist path="+f.getAbsolutePath());
			return;
		}
		if(resolved) {
			if(f.lastModified()!=getLastmodified()) {
				resolved=false;
			}
			else {
				return;
			}
		}
		else {
			//PMS.dbg("DVDIDOfile.resolve: not resolved yet --> exec resolve, name="+getName());
		}
		setLastmodified(f.lastModified());  //re-set 
		if(getMediaLib(mediaPath,f)) {
			//found MediaCache
			getMedia().setMediaparsed(true);
			super.resolve();
			resolved=true;	//regzamod
			return;
		}
		//---- regzamod add end
		//PMS.dbg("DVDTitle.resolve: MediaCache not found, exec mediaparse, getName_st="+getName_st());
		
		String cmd[] = new String[]{PMS.getConfiguration().getMplayerPath(), "-identify", "-endpos", "0", "-v", "-ao", "null", "-vc", "null", "-vo", "null", "-dvd-device", ProcessUtil.getShortFileNameIfWideChars(f.getAbsolutePath()), "dvd://" + title,"-quiet","-quiet"};
		OutputParams params = new OutputParams(PMS.getConfiguration());
		params.maxBufferSize = 1;
		if (PMS.getConfiguration().isDvdIsoThumbnails()) {
			try {
				params.workDir = PMS.getConfiguration().getTempFolder();
			} catch (IOException e1) {
			}
			cmd[2] = "-frames";
			cmd[3] = "2";
			cmd[7] = "-quiet";
			cmd[8] = "-quiet";
			String frameName = "" + this.hashCode();
			frameName = "mplayer_thumbs:subdirs=\"" + frameName + "\"";
			frameName = frameName.replace(',', '_');
			cmd[10] = "jpeg:outdir=" + frameName;
		}
		else if(chapter_str!=null) {
			//PMS.dbg("DVDISOTitle: chapter No. setted="+chapter_str);
			cmd[cmd.length-2]="-chapter";
			cmd[cmd.length-1]=chapter_str;
		}
		//PMS.dbg("DVDISOTitle: cmd="+cmd);
		
		params.log = true;
		final ProcessWrapperImpl pw = new ProcessWrapperImpl(cmd, params, true, false);
		Runnable r = new Runnable() {

			public void run() {
				try {
					Thread.sleep(20000);
				} catch (InterruptedException e) {
				}
				pw.stopProcess();
			}
		};
		Thread failsafe = new Thread(r);
		failsafe.start();
		pw.run();
		List<String> lines = pw.getOtherResults();

		String duration = null;
		int nbsectors = 0;
		String fps = null;
		String aspect = null;
		String width = null;
		String height = null;
		ArrayList<DLNAMediaAudio> audio = new ArrayList<DLNAMediaAudio>();
		ArrayList<DLNAMediaSubtitle> subs = new ArrayList<DLNAMediaSubtitle>();
		
		if (getMedia() == null) { //regzamod, add
			setMedia(new DLNAMediaInfo());
		}
		
		if (lines != null) {
			for (String line : lines) {
				if (line.startsWith("DVD start=")) {
					nbsectors = Integer.parseInt(line.substring(line.lastIndexOf("=") + 1).trim());
				}
				if (line.startsWith("audio stream:")) {
					DLNAMediaAudio lang = new DLNAMediaAudio();
					lang.setId(Integer.parseInt(line.substring(line.indexOf("aid: ") + 5, line.lastIndexOf(".")).trim()));
					String str=line.substring(line.indexOf("language: ") + 10, line.lastIndexOf(" aid")).trim();
					if(str.length()>3) {
						logger.warn("DVD audio.lang length over limit(3)="+str);
						lang.setLang(DLNAMediaLang.UND);
					}
					else {
						lang.setLang(str);
					}
					int end = line.lastIndexOf(" langu");
					if (line.lastIndexOf("(") < end && line.lastIndexOf("(") > line.indexOf("format: ")) {
						end = line.lastIndexOf("(");
					}
					lang.setCodecA(line.substring(line.indexOf("format: ") + 8, end).trim());
					if (line.contains("(stereo)")) {
						lang.setNrAudioChannels(2);
					} else {
						lang.setNrAudioChannels(6);
					}
					audio.add(lang);
				}
				if (line.startsWith("subtitle")) {
					DLNAMediaSubtitle lang = new DLNAMediaSubtitle();
					lang.setId(Integer.parseInt(line.substring(line.indexOf("): ") + 3, line.lastIndexOf("language")).trim()));
					lang.setLang(line.substring(line.indexOf("language: ") + 10).trim());
					//if (lang.getLang().equals("unknown")) {
					if (lang.getLang().startsWith("unknown")) {  //regzamod
						lang.setLang(DLNAMediaLang.UND);
					}
					lang.setType(DLNAMediaSubtitle.EMBEDDED);
					subs.add(lang);
				}
				if (line.startsWith("ID_VIDEO_WIDTH=")) {
					width = line.substring(line.indexOf("ID_VIDEO_WIDTH=") + 15).trim();
				}
				if (line.startsWith("ID_VIDEO_HEIGHT=")) {
					height = line.substring(line.indexOf("ID_VIDEO_HEIGHT=") + 16).trim();
				}
				if (line.startsWith("ID_VIDEO_FPS=")) {
					fps = line.substring(line.indexOf("ID_VIDEO_FPS=") + 13).trim();
				}
				if (line.startsWith("ID_LENGTH=")) {
					duration = line.substring(line.indexOf("ID_LENGTH=") + 10).trim();
				}
				if (line.startsWith("ID_VIDEO_ASPECT=")) {
					aspect = line.substring(line.indexOf("ID_VIDEO_ASPECT=") + 16).trim();
				}
			}
		}
		else {
			logger.error("DVDISOTitle: MediaParse by Mplayer seems failed, null returned");
			return;
		}

		if (PMS.getConfiguration().isDvdIsoThumbnails()) {
			try {
				String frameName = "" + this.hashCode();
				frameName = PMS.getConfiguration().getTempFolder() + "/mplayer_thumbs/" + frameName + "00000001/0000000";
				frameName = frameName.replace(',', '_');
				File jpg = new File(frameName + "2.jpg");
				if (jpg.exists()) {
					InputStream is = new FileInputStream(jpg);
					int sz = is.available();
					if (sz > 0) {
						getMedia().setThumb(new byte[sz]);
						is.read(getMedia().getThumb());
					}
					is.close();
					if (!jpg.delete()) {
						jpg.deleteOnExit();
					}
					if (!jpg.getParentFile().delete()) {
						jpg.getParentFile().delete();
					}
				}
				jpg = new File(frameName + "1.jpg");
				if (jpg.exists()) {
					if (!jpg.delete()) {
						jpg.deleteOnExit();
					}
					if (!jpg.getParentFile().delete()) {
						jpg.getParentFile().delete();
					}
				}
			} catch (IOException e) {
				logger.trace("Error in DVD ISO thumbnail retrieval: " + e.getMessage());
			}
		}

		length = nbsectors * 2048;

		double d = 0;
		if (duration != null) {
			d = Double.parseDouble(duration);
		}

		getMedia().setAudioCodes(audio);
		getMedia().setSubtitlesCodes(subs);

		if (duration != null) {
			getMedia().setDuration(d);
		}
		getMedia().setFrameRate(fps);
		getMedia().setAspect(aspect);
		getMedia().setDvdtrack(title);
		getMedia().setContainer("iso");
		getMedia().setCodecV("mpeg2video");
		try {
			getMedia().setWidth(Integer.parseInt(width));
			getMedia().setHeight(Integer.parseInt(height));
		} catch (NumberFormatException nfe) {
		}
		if(fps==null && aspect==null ) {
			logger.warn("DVDISOTitle.resolve: failed, try later, media="+mediaPath);
		}
		else {
			getMedia().setMediaparsed(true);
			super.resolve();
			resolved=true;	//regzamod
			saveMediaLib(mediaPath, f);	//regzamod
			
			//PMS.dbg("DVDTitle.resolve: call finalize(), getName_st="+getName_st());
			InputFile input = new InputFile();
			input.setFile(f);
			getMedia().finalize(Format.VIDEO, input, getName_st());
		}
	}

	public long getLength() {
		return length;
	}

	@Override
	public InputStream getInputStream() throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getName() {
		//if(dname2!=null) return dname2;
		if(dname!=null) return dname;
		return "Title " + title;
	}
	
	public String getName_st() {  //name for external subtitle search 
		if(sotype==SO_DVDDEV) {  // DVD device
			return "Title " + title;
		}
		else {  // ISO file
			return f.getName()+"-Title " + title;
		}
	}
	
	@Override
	public void setName(String n) {	//regzamod
		dname=n;
	}
	
	@Override
	public String getDpName() {	//regzamod
		return getName();
	}

	@Override
	public String getSystemName() {
		return f.getAbsolutePath();
	}

	@Override
	public String  getSrcPath() {	//regzamod
		return f.getAbsolutePath();
	}
	
	@Override
	public int  getTitleNum() {	//regzamod
		return title;
	}

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

	@Override
	public boolean isValid() {
		if (getExt() == null) {
			setExt(PMS.get().getAssociatedExtension("dummy.iso"));
		}
		return true;
	}

	@Override
	public long length() {
		return DLNAMediaInfo.TRANS_SIZE;
	}

	// Ditlew
	public long length(RendererConfiguration mediaRenderer) {
		// WDTV Live at least, needs a realistic size for stop/resume to works proberly. 2030879 = ((15000 + 256) * 1024 / 8 * 1.04) : 1.04 = overhead
		int cbr_video_bitrate = getDefaultRenderer().getCBRVideoBitrate();
		return (cbr_video_bitrate > 0) ? (long) (((cbr_video_bitrate + 256) * 1024 / 8 * 1.04) * getMedia().getDurationInSeconds()) : length();
	}

	@Override
	public InputStream getThumbnailInputStream() throws IOException {
		File cachedThumbnail = null;
		File thumbFolder = null;
		boolean alternativeCheck = false;
		while (cachedThumbnail == null) {
			if (thumbFolder == null) {
				thumbFolder = f.getParentFile();
			}
			cachedThumbnail = FileUtil.getFileNameWitNewExtension(thumbFolder, f, "jpg");
			if (cachedThumbnail == null) {
				cachedThumbnail = FileUtil.getFileNameWitNewExtension(thumbFolder, f, "png");
			}
			if (cachedThumbnail == null) {
				cachedThumbnail = FileUtil.getFileNameWitAddedExtension(thumbFolder, f, ".cover.jpg");
			}
			if (cachedThumbnail == null) {
				cachedThumbnail = FileUtil.getFileNameWitAddedExtension(thumbFolder, f, ".cover.png");
			}
			if (alternativeCheck) {
				break;
			}
			if (StringUtils.isNotBlank(PMS.getConfiguration().getAlternateThumbFolder())) {
				thumbFolder = new File(PMS.getConfiguration().getAlternateThumbFolder());
				if (!thumbFolder.exists() || !thumbFolder.isDirectory()) {
					thumbFolder = null;
					break;
				}
			}
			alternativeCheck = true;
		}
		if (cachedThumbnail != null) {
			return new FileInputStream(cachedThumbnail);
		} else if (getMedia() != null && getMedia().getThumb() != null) {
			return getMedia().getThumbnailInputStream();
		} else {
			return getResourceInputStream("images/cdrwblank-256.png");
		}
	}
	
	boolean getMediaLib(String fileName, File file) {
		if(fileName==null) return false;
		if(PMS.getConfiguration().getUseCache()) {
			DLNAMediaDatabase database = PMS.get().getDatabase();
			if (database != null) {
				ArrayList<DLNAMediaInfo> medias = database.getData(fileName, file.lastModified());
				if (medias != null && medias.size() >= 1) {
					setMedia(medias.get(0));
					getMedia().setDvdtrack(title); //Currently, Dvdtrack is not saved in DB !!
					if(getExt()!=null && getExt().isVideo() && getMedia().getWidth()<=0) {
						//may be failed before --> treat as not found
						database.delData(fileName,1); // once delete 
						logger.warn("DVDTitle.getMedia: media insane -> retry parse media, file="+file.getAbsolutePath());
						logger.warn("DVDTitle.getMedia: media aspect="+getMedia().getAspect_f()+",width="+getMedia().getWidth());
						return false;
					}
					else {
						InputFile input = new InputFile();
						input.setFile(file);
						//getMedia().finalize(getType(), input);
						//PMS.dbg("DVDTitle.getMediaLib: call finalize(), getName_st="+getName_st());
						getMedia().finalize(Format.VIDEO, input, getName_st());
						//found = true;

						if(medias.size() > 2) {
							logger.warn("DVDTitle.getMedia: data duplicated! correct dup-count="+medias.size()+", fileName="+fileName);
							database.delData(fileName,1); // once delete 
							database.insertData(fileName, file.lastModified(), getType(), getMedia());
						}
						return true;
					}
				}
				else {
					//is not error
					//logger.warn("DVDTitle.getMedia: database.getData failed, medias="+medias+", fileName="+fileName);
					//PMS.dbg("DVDTitle.getMedia: database.getData failed, medias="+medias+", fileName="+fileName);
					return false;
				}
			}
			return false;
		}
		else {
			return false;
		}
	}
	
	void saveMediaLib(String fileName, File file) {  //regzamod, add
		if(fileName==null) return;
		if (PMS.getConfiguration().getUseCache()) {
			DLNAMediaDatabase database = PMS.get().getDatabase();
			if (database != null) {
				if(getMedia().getWidth()<=0) {
					getMedia().setWidth(1);	//dummy set indicate parse succeeded 
				}
				database.insertData(fileName, file.lastModified(), Format.VIDEO, getMedia());
				//PMS.dbg("DVDISOTitle.saveMediaLib: database.insertData, fileName="+fileName+", media="+getMedia());
			}
		}
	}
	
	public String getMediaCachePath (int[] rc) {	//regzamod add
		if(true) {  //test
			rc[0]=1;//use wild card
			return mediaCachePath;
		}
		else {
			rc[0]=0;//not use wild card
			return mediaPath;
		}
	}
	
	public void setResolved(boolean set) {	//regzamod add
		if(set==false) {
			if(getMedia()!= null) {
				if(getMedia().isMediaparsed()) {
					getMedia().setMediaparsed(false);
				}
			}
			resolved=false;
		} 
	}


}
