package net.pms.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;

import net.pms.PMS;
import net.pms.dlna.DLNAMediaInfo;
import net.pms.dlna.DLNAMediaSubtitle;

import org.apache.commons.lang.StringUtils;
import org.mozilla.universalchardet.UniversalDetector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

	public static File isFileExists(String f, String ext) {
		return isFileExists(new File(f), ext);
	}

	public static String getExtension(String f) {
		int point = f.lastIndexOf(".");
		if (point == -1) {
			return null;
		}
		return f.substring(point + 1);
	}

	public static String getFileNameWithoutExtension(String f) {
		int point = f.lastIndexOf(".");
		if (point == -1) {
			point = f.length();
		}
		return f.substring(0, point);
	}

	public static File getFileNameWitNewExtension(File parent, File f, String ext) {
		File ff = isFileExists(new File(parent, f.getName()), ext);
		if (ff != null && ff.exists()) {
			return ff;
		}
		return null;
	}

	public static File getFileNameWitAddedExtension(File parent, File f, String ext) {
		File ff = new File(parent, f.getName() + ext);
		if (ff.exists()) {
			return ff;
		}
		return null;
	}

	public static File isFileExists(File f, String ext) {
		int point = f.getName().lastIndexOf(".");
		if (point == -1) {
			point = f.getName().length();
		}
		File lowerCasedFilename = new File(f.getParentFile(), f.getName().substring(0, point) + "." + ext.toLowerCase());
		if (lowerCasedFilename.exists()) {
			return lowerCasedFilename;
		}

		File upperCasedFilename = new File(f.getParentFile(), f.getName().substring(0, point) + "." + ext.toUpperCase());
		if (upperCasedFilename.exists()) {
			return upperCasedFilename;
		}
		return null;
	}

	public static boolean doesSubtitlesExists(File file, DLNAMediaInfo media) {
		return doesSubtitlesExists(file, media, true, null);
	}
	public static boolean doesSubtitlesExists(File file, DLNAMediaInfo media, String fname) {
		return doesSubtitlesExists(file, media, true, fname);
	}

	public static boolean doesSubtitlesExists(File file, DLNAMediaInfo media, boolean usecache, String fname) {
		boolean found = browseFolderForSubtitles(file.getParentFile(), file, media, usecache,fname);
		
		//PMS.dbg("doesSubtitlesExists: called, fname="+fname+", fileName="+file.getName());
			
		String alternate = PMS.getConfiguration().getAlternateSubsFolder();
		if (StringUtils.isNotBlank(alternate)) { // https://code.google.com/p/ps3mediaserver/issues/detail?id=737#c5
			File subFolder = new File(alternate);
			if (!subFolder.isAbsolute()) {
				subFolder = new File(file.getParent() + "/" + alternate);
				try {
					subFolder = subFolder.getCanonicalFile();
				} catch (IOException e) {
				}
			}
			if (subFolder.exists()) {
				found = found || browseFolderForSubtitles(subFolder, file, media, usecache,fname);
			}
		}
		//PMS.dbg("doesSubtitlesExists: return found="+found+", fname="+fname+", fileName="+file.getName()); 
		return found;
	}
	
	static Map<File, File[]> cache;
	
	public static void ClearSubtitlesCache() {
		cache=null;
	}

	private synchronized static boolean browseFolderForSubtitles(File subFolder, File file, DLNAMediaInfo media
		, boolean usecache, String fname) {
			
		boolean found = false;
		if(subFolder==null) return found;
		
		if (!usecache) {
			cache = null;
		}
		if (cache == null) {
			cache = new HashMap<File, File[]>();
		}
		File allSubs[] = cache.get(subFolder);
		if (allSubs == null) {
			allSubs = subFolder.listFiles();
			if (allSubs != null) {
				cache.put(subFolder, allSubs);
			}
		}
		String fileName;
		if(fname==null) {
			fileName=getFileNameWithoutExtension(file.getName()).toLowerCase();
		}
		else {
			fileName=fname.toLowerCase();
		}
		if (allSubs != null) {
			for (File f : allSubs) {
				if (f.isFile() && !f.isHidden()) {
					String fName = f.getName().toLowerCase();
					for (int i = 0; i < DLNAMediaSubtitle.subExtensions.length; i++) {
						String ext = DLNAMediaSubtitle.subExtensions[i];
						if (fName.length() > ext.length() && fName.startsWith(fileName) && fName.endsWith("." + ext)) {
							int a = fileName.length();
							int b = fName.length() - ext.length() - 1;
							String code = "";
							if (a <= b) // handling case with several dots: <video>..<extension>
							{
								code = fName.substring(a, b);
							}
							if (code.startsWith(".")) {
								code = code.substring(1);
							}

							boolean exists = false;
							if (media != null) {
								for (DLNAMediaSubtitle sub : media.getSubtitlesCodes()) {
									if (f.equals(sub.getFile())) {
										exists = true;
									} else if (ext.equals("idx") && sub.getType() == DLNAMediaSubtitle.MICRODVD) { // VOBSUB
										sub.setType(DLNAMediaSubtitle.VOBSUB);
										exists = true;
									} else if (ext.equals("sub") && sub.getType() == DLNAMediaSubtitle.VOBSUB) { // VOBSUB
										sub.setFile(f);
										exists = true;
									}
								}
							}
							if (!exists) {
								DLNAMediaSubtitle sub = new DLNAMediaSubtitle();
								sub.setId(100 + (media == null ? 0 : media.getSubtitlesCodes().size())); // fake id, not used
								sub.setFile(f);
								sub.checkUnicode();
								if (code.length() == 0 || !Iso639.getCodeList().contains(code)) {
									sub.setLang(DLNAMediaSubtitle.UND);
									sub.setType(i + 1);
									if (code.length() > 0) {
										sub.setFlavor(code);
										if (sub.getFlavor().contains("-")) {
											String flavorLang = sub.getFlavor().substring(0, sub.getFlavor().indexOf("-"));
											String flavorTitle = sub.getFlavor().substring(sub.getFlavor().indexOf("-") + 1);
											if (Iso639.getCodeList().contains(flavorLang)) {
												sub.setLang(flavorLang);
												sub.setFlavor(flavorTitle);
											}
										}
									}
								} else {
									sub.setLang(code);
									sub.setType(i + 1);
								}
								//found = true;
								if (media != null) {
									media.getSubtitlesCodes().add(sub);
								}
							}
							found = true;  //anyway (new found or already exists)
						}
					}
				}
			}
		}
		return found;
	}
	
	//---- regzamod, add --------------------------------
	public static String rzCharaCodeDetect(String filepath, int skip_bytes[]) {
		byte[] buf = new byte[4096];
		int[] bsav= new int[10];
		java.io.FileInputStream fis=null;
		int cnt=0;
		int nread;	
		
		skip_bytes[0]=0;
		
		try {
			fis = new java.io.FileInputStream(filepath);	// 判別器を構築	
		} catch (FileNotFoundException e) {
			logger.error(null, e);
		} 
		if(fis==null) {
			return null;
		}
		UniversalDetector detector = new UniversalDetector(null);	// 判別器にデータを与える	
		
		try {
			while ((nread = fis.read(buf)) > 0 && !detector.isDone()) {	
				if(cnt==0) {
					bsav[0]=buf[0]&0xff;
					bsav[1]=buf[1]&0xff;
					bsav[2]=buf[2]&0xff;
					//logger.info(String.format("bsav=0x%x%x%x",bsav[0],bsav[1],bsav[2]));	//regzamod
				}
				cnt++;
				detector.handleData(buf, 0, nread);	
			}
			fis.close();
		} catch (IOException e) {
			logger.error(null, e);
		} 
		// データの終わりを判別器に通知する	
		detector.dataEnd(); 	
		// 判別結果を取得する	
		String encoding = detector.getDetectedCharset();	
		if (encoding != null) {	  
			//System.out.println("Detected encoding = " + encoding);	
		} else {	  
			//System.out.println("No encoding detected.");
		}
		
		// Someone should teach Mojira's foolish Detector
		if(encoding=="SHIFT_JIS") {
			encoding="MS932";	
		} 
		else if(encoding=="WINDOWS-1252") {  // what?
			encoding="MS932";
		} 
		else if (encoding=="UTF-8") {
			//logger.info("rzCharaCodeDetect: UTF-8");	//regzamod
			if(bsav[0] ==0xef && bsav[1] ==0xbb && bsav[2] ==0xbf) {
				//logger.info("rzCharaCodeDetect: skip_bytes=3");	//regzamod
				skip_bytes[0]=3;
			}
		}
		else if (encoding=="UTF-16BE" || encoding=="UTF-16LE") {
			if((bsav[0]==0xff && bsav[1]==0xfe) || (bsav[0]==0xfe && bsav[1]==0xff)) {
				skip_bytes[0]=2;
			}
		}
		else if (encoding=="GB18030" || encoding=="UTF-16") {
			encoding="UTF-16";
			if((bsav[0]==0xff && bsav[1]==0xfe) || (bsav[0]==0xfe && bsav[1]==0xff)) {
				//skip_bytes[0]=2;  // will be judged by java
			}
		}
		detector.reset(); 
		if(PMS.rz_debug>2) {
			PMS.dbg("rzCharaCodeDetect: fpath="+filepath+", Encode="+encoding+", skip_bytes="+skip_bytes[0]);	//regzamod
		}
		return encoding;
	}

}
