/*
 * 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.
 */
//-----------------------------------------------------------------------------
// Regzamod 
//-----------------------------------------------------------------------------
package net.pms.dlna;

import java.io.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.StringTokenizer;
import java.util.Calendar;

import net.pms.PMS;
import net.pms.formats.Format;
import net.pms.util.FileUtil;
import net.pms.util.ProcessUtil;
import net.pms.util.PMSUtil;
import net.pms.dlna.DLNAResource;
import net.pms.dlna.virtual.VirtualFolder;
import net.pms.newgui.rz_WebTab;
import net.pms.dlna.virtual.VirtualVideoAction;
import net.pms.dlna.rz_IavSelFolder;
import net.pms.configuration.PmsConfiguration;
import static net.pms.util.StringUtil.convertNameToFileName;

import org.apache.commons.lang.StringUtils;

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

public class  rz_IavSelCtl {
	private static final Logger logger = LoggerFactory.getLogger(rz_IavSelCtl.class);
	private boolean resolved;
	private PmsConfiguration conf= PMS.getConfiguration();
	private String cpath_aav,cpath_iav;
	private rz_IavSelFolder sel_aav,sel_iav;
	private VirtualFolder sel_vf;
	private File base_folder;   //folder for save clip_files
	private File del_folder;  	//gabage-folder
	private String base_path;   //path of folder for save clip_files
	private String del_path;   	//path of gabage-folder for deleted files
	private static String FSEP=File.separator;
	private File clipFile;
	private RootFolder root;
	private String current_clip_path_v;
	private String current_clip_path_a;

	public ArrayList<String> cpathlist_a = new ArrayList<String>();	//audio clip_path_list
	public ArrayList<String> cpathlist_v = new ArrayList<String>();	//visual clip_path_list
	public int selpos_a;
	public int selpos_v;
	
	public rz_IavSelCtl(DLNAResource parent, RootFolder root) {
		this.root=root; //must be set before call createList()
		//PMS.dbg("rz_IavSelCtl: getRoot="+root);
		
		//try {
			//base_path=conf.getTempFolder()+FSEP+"clips";
			base_path=conf.getUserDataFolder()+FSEP+"clips";
			
			del_path=base_path+FSEP+"deleted";
		//} catch (IOException e) {
		//	logger.error("rz_IavSelCtl: failed to get clip folder_path, "+e.getMessage());
		//	return ;
		//}
		base_folder=new File(base_path);
		if(!base_folder.isDirectory()) {
			boolean rc=base_folder.mkdir();
			if(!rc) {
				logger.error("rz_IavSelCtl: failed to create clip folder="+base_path);
			}
		}
		del_folder=new File(del_path);
		if(!del_folder.isDirectory()) {
			boolean rc=del_folder.mkdir();
			if(!rc) {
				logger.error("rz_IavSelCtl: failed to create clip/deleted folder="+del_path);
			}
		}
		clipFile=new File(conf.getRZ_cliplist_conf());
		createList(parent,clipFile);
	}
	
	public void set_cpath(int clip_type,String path) {
		//PMS.dbg("rz_IavSelCtl.set_cpath: root="+root+", set path="+path);
		if(clip_type==1) {
			current_clip_path_a=path;
			//if(root!=null) root.audio_clip_path=path;
		}
		else {
			current_clip_path_v=path;
			//if(root!=null) root.visual_clip_path=path;
		}
	}
	
	public String getCurrentClipPath_a() {
		if(current_clip_path_a==null) return null;
		boolean rc=recoverClipFile(new File(current_clip_path_a));
		return (rc==true?current_clip_path_a:null);
	}
	public String getCurrentClipPath_v() {
		if(current_clip_path_v==null) return null;
		boolean rc=recoverClipFile(new File(current_clip_path_v));
		return (rc==true?current_clip_path_v:null);
	}
	
	public DLNAResource getSelRoot() {
		return (DLNAResource)sel_vf;
	}
	public DLNAResource getSelAAV() {
		return (DLNAResource)sel_aav;
	}
	public DLNAResource getSelIAV() {
		return (DLNAResource)sel_iav;
	}
	
	private void createList(DLNAResource pa, File f) {
		//File f=new File(conf.getRZ_cliplist_conf());
		int rc=loadClipPath(f);
		if(rc==0) {
			//---- create root for clip_selection
			sel_vf=new VirtualFolder("#- BackGround Clips -#",null);
			pa.addChildInternal(sel_vf);
			
			sel_iav=new rz_IavSelFolder("#- Select Audio Clips -#" ,1,this,pa.getDefaultRenderer());
			sel_vf.addChildInternal(sel_iav);
			sel_iav.sel_clip(selpos_a);
			
			//---- create clip_selection list
			sel_aav=new rz_IavSelFolder("#- Select Visual Clips -#",2,this,pa.getDefaultRenderer());
			sel_vf.addChildInternal(sel_aav);
			sel_aav.sel_clip(selpos_v);
			
			//---- create save button
			boolean enabled=false;
			int mode = 2;  		//=0:no-stat, 1:toggle_btn, =2:push_btn
			int btn_type = 0;	//=0:auto judge,1:file_type,=2:folder_type,=3:normal folder 
			rz_VirtualVideoActionF btn=new rz_VirtualVideoActionF(" Save to File",enabled,mode,btn_type) {
				@Override
				public boolean enable() {
					//PMS.dbg("Save to File: enable called -->exec saveClipPath, clipFile="+clipFile);
					int rc=saveClipPath(clipFile);
					enabled=rc==0?true:false;
					return (true);
				}
			};
			sel_vf.addChildInternal(btn);
		}
	}

	public File getDeletedFile(File f) {
		String str=del_path+FSEP+f.getName();
		return new File(str);	
	}
	
	public void delClipFile(File f) {
		File df=getDeletedFile(f);
		try {
			if(df.isFile()) {
				df.delete();
			}
			f.renameTo(df);
		} catch(Exception e){ 
			logger.error("delClipFile: Exception: "+ e.getMessage());
		}
	}
	
	public boolean recoverClipFile(File f) {
		if(!f.isAbsolute()) return true;  //can't check, good luck
		if(f.exists()) return true;
		File df=getDeletedFile(f);
		try {
			if(df.isFile()) {
				return (df.renameTo(f));
			}
		} catch(Exception e){ 
			logger.error("delClipFile: Exception: "+ e.getMessage());
			return false;
		}
		return false;
	}

	private int saveClipPath(File f) {
		//PMS.dbg("rz_IavSelCtl.saveClipPath: called, File="+f);
		try{
			PrintWriter pw = new PrintWriter(f);
			int selpos_a=0;
			int selpos_v=0;
			int i=0;
			
			//---- audio clips (for image bgm)
			pw.println("#---- Path of BackGround AudioClips");
			i=0;
			for(DLNAResource d: sel_iav.getChildren()) {
				//if(d instanceof rz_IavSelFolder) continue;
				if(!(d instanceof rz_VirtualVideoActionF)) continue;
				rz_VirtualVideoActionF b=(rz_VirtualVideoActionF)d;
				String path=b.getTargetPath();
				if(b.enabled) selpos_a=i;
				if(path!=null) {
					path=cpath_encode(path);  //conv specific_path in path_string to keyword
					pw.println("cpath_a="+path);
				}
				i++;
			}
			pw.println("selpos_a="+selpos_a);
			pw.println("");
			
			//---- visual clips (for audio bgv)
			pw.println("#---- Path of BackGround VisualClips");
			i=0;
			for(DLNAResource d: sel_aav.getChildren()) {
				//if(d instanceof rz_IavSelFolder) continue;
				if(!(d instanceof rz_VirtualVideoActionF)) continue;
				rz_VirtualVideoActionF b=(rz_VirtualVideoActionF)d;
				String path=b.getTargetPath();
				if(b.enabled) selpos_v=i;
				if(path!=null) {
					path=cpath_encode(path);  //conv specific_path in path_string to keyword
					pw.println("cpath_v="+path);
				}
				i++;
			}
			pw.println("selpos_v="+selpos_v);
			pw.close();
			return 0;
		} catch (IOException e){
			logger.error("saveClipPath: IOException: "+e.getMessage());
		}
		return -1;
	}
	
	private int loadClipPath(File clipFile) {
		try {
			LineNumberReader br = new LineNumberReader(new InputStreamReader(new FileInputStream(clipFile)) );
			String line = null;
			String str;
			while ((line = br.readLine()) != null) {
				line = line.trim();
				if(line.startsWith("cpath_a=")) {
					str=line.substring("cpath_a=".length()).trim();
					if(str.startsWith("\"") && str.endsWith("\"") && str.length()>2) {
						str=str.substring(1,str.length()-1);
					}
					if(str.length()<=0) continue;
					str=cpath_decode(str);  //conv keywords in path_string to rael value
					cpathlist_a.add(str);
				}
				else if(line.startsWith("cpath_v=")) {
					str=line.substring("cpath_v=".length()).trim();
					if(str.startsWith("\"") && str.endsWith("\"") && str.length()>2) {
						str=str.substring(1,str.length()-1);
					}
					if(str.length()<=0) continue;
					str=cpath_decode(str);  //conv keywords in path_string to rael value
					cpathlist_v.add(str);
				}
				else if(line.startsWith("selpos_a=")) {
					str=line.substring("selpos_a=".length()).trim();
					if(str.length()<=0) continue;
					selpos_a=Integer.parseInt(str);
				}
				else if(line.startsWith("selpos_v=")) {
					str=line.substring("selpos_v=".length()).trim();
					if(str.length()<=0) continue;
					selpos_v=Integer.parseInt(str);
				}
			}
			br.close();
			return 0;
		} catch (IOException e){
			logger.warn("loadClipPath: IOException: file="+clipFile.getName()+", "+ e.getMessage());
		}
		//return -1;
		return 0;  //go on, even if file not exist(treat as clip entry count=0)
	}
	
	/* theoretically, multi-linked child can't get parent. b/c parents isn't single/unique
	*/
	//---- btn for add folder as clip (include both real/virtual folder)
	public DLNAResource createClipAddBtn_folder(DLNAResource target) {
		String btn_name="#- Add to Clips (Current folder) -#";
		boolean enabled=false;
		int btn_mode=0;		//=0:no-stat, 1:toggle_btn, =2:push_btn
		int btn_type=2;		//=0:auto judge,1:file_type,=2:folder_type,=3:normal folder 
		rz_VirtualVideoActionF btn=new rz_VirtualVideoActionF(btn_name,enabled,btn_mode,btn_type) {
			@Override
			public boolean enable() {
				int cnt[]=new int[3];
				int rc=0;
				String errMsg=null;
				
				String[] tg_inf=getClipContents(target,cnt);
				String tg_name=tg_inf[0];
				String tg_path=tg_inf[1];
				if(tg_name==null || (cnt[0]<=0 && cnt[1]<=0 && cnt[2]<=0)) {
					//PMS.dbg("No valid visual/audio clips found");
					errMsg="No valid visual/audio clips found";
				}
				else {
					if(cnt[0]>=cnt[1] && cnt[0]>=cnt[2]){		//image clip
						errMsg=sel_aav.add_clip(tg_name,tg_path);
						//PMS.dbg("Added to Audio Clips, rc="+rc+", file="+f.getPath());
					}
					else if(cnt[1]>=cnt[0] && cnt[1]>=cnt[2]) {  //audio clip
						//PMS.dbg("Added to Visual Clips, rc="+rc+", file="+f.getPath());
						errMsg=sel_iav.add_clip(tg_name,tg_path);
					}
					else {//video clip
						String msg1,msg2;
						msg1=sel_iav.add_clip(tg_name,tg_path);
						msg2=sel_aav.add_clip(tg_name,tg_path);
						if(msg1!=null) errMsg=msg1;
						else errMsg=msg2;
						//PMS.dbg("Added to Audio Clips, rc="+rc+", file="+f.getPath());
					}
				}
				//PMS.dbg("Added to Audio Clips, errMsg="+errMsg);
				if(errMsg!=null) {
					setBtnName(errMsg);
					return false;
				}
				else {
					resetBtnName();
					return true;
				}
			}
		};
		btn.setTarget(target);
		return (DLNAResource)btn;
	}
	
	//---- btn for add single file (last played)
	public DLNAResource createClipAddBtn_file(DLNAResource target) {
		String btn_name="#- Add to Clips (Last Played File) -#";
		boolean enabled=false;
		int btn_mode=0;		//=0:no-stat, 1:toggle_btn, =2:push_btn
		int btn_type=2;		//=0:auto judge,1:file_type,=2:folder_type,=3:normal folder 
		rz_VirtualVideoActionF btn=new rz_VirtualVideoActionF(btn_name,enabled,btn_mode,btn_type) {
			@Override
			public boolean enable() {
				int cnt[]=new int[3];
				int rc=0;
				String errMsg=null;
				DLNAResource ch;
				rz_SessionInfo sess=PMS.getSessionInfoByDlna(target);
				ch=sess.CurrentDLNA;
				if(ch==null) {
					errMsg="Last Played File not found.";
					setBtnName(errMsg);
					return false;
				}
				// check media type
				if(ch.getExt()!=null) {
					if(ch.getExt().isImage()) {
						cnt[0]++;
					}
					else if(ch.getExt().isAudio()){
						cnt[1]++;
					}
					else if(ch.getExt().isVideo()){
						cnt[2]++;
					}
				}
				String tg_name=ch.getName();
				String tg_path=ch.getSrcPath();
				//PMS.dbg("createClipAddBtn_file: tg_name="+tg_name+", tg_path="+tg_path);
				if(tg_name==null || (cnt[0]<=0 && cnt[1]<=0 && cnt[2]<=0)) {
					//PMS.dbg("No valid visual/audio clips found");
					errMsg="No valid visual/audio clips found";
					setBtnName(errMsg);
					return false;
				}
				
				String sfx=".lst";
				File pf=new File(tg_path);
				String tgf_sfx="_hash_"+pf.hashCode()+sfx;
				String tgf_name=convertNameToFileName(tg_name);
				String tgf_path=base_path +FSEP+ tgf_name + tgf_sfx;	//real fileath
				String dname=ch.getParent().getName()+"/"+tg_name; 	//short filepath for disp in list
				try {
					File f=new File(tgf_path);
					PrintWriter pw = new PrintWriter(f,"UTF-8");	//force utf-8
					//---- write dp_name
					pw.println("#@name="+dname);
					//---- write clip entries
					//pw.println(ch.getSystemName()); // use shortName
					pw.println(ch.getSrcPath()); // use fullpath
					pw.close();
				} catch (IOException e) {
					logger.error("Error in createClipAddBtn_file: " + e.getMessage());
				}
				if(cnt[2]>(cnt[0]+cnt[1])) {  //image > audio
					String msg1,msg2;
					msg1=sel_aav.add_clip(dname,tgf_path);
					msg2=sel_iav.add_clip(dname,tgf_path);
					if(msg1!=null) errMsg=msg1;
					else errMsg=msg2;
				}
				else if(cnt[0]>cnt[1]) {  //image > audio
					errMsg=sel_aav.add_clip(dname,tgf_path);
					//PMS.dbg("Added to Visual Clips, rc="+rc+", file="+f.getPath());
				}
				else if(cnt[0]<cnt[1]){//audio > image
					errMsg=sel_iav.add_clip(dname,tgf_path);
					//PMS.dbg("Added to Audio Clips, rc="+rc+", file="+f.getPath());
				}
				
				//PMS.dbg("Added to Audio Clips, errMsg="+errMsg);
				if(errMsg!=null) {
					setBtnName(errMsg);
					return false;
				}
				else {
					resetBtnName();
					return true;
				}
			}
		};
		btn.setTarget(target);
		return (DLNAResource)btn;
	}
	
	//get contents for FileType Clip
	private String[] getClipContents (DLNAResource pa, int cnt[]) {
		cnt[0]=0;  //image count in clips
		cnt[1]=0;  //audio count in clips
		cnt[2]=0;  //video count in clips
		int i=0;
		String[] tg_inf={null,null};
		try {
			//---- deside clip_filename
			String spath=pa.getSrcPath();
			if(spath==null) {
				spath=pa.getResourceId();
			}
			File pf=new File(spath);
			if(pa.isFolder() && pa instanceof RealFile) {
				for (DLNAResource ch : pa.getChildren()) {
					//PMS.dbg("getClipContents: Normal Folder, child No."+i+", name="+ch.getName()+", Ext="+ch.getExt());
					if(ch.isFolder()) continue;
					if(ch.getExt()!=null) {
						if(ch.getExt().isImage()) {
							cnt[0]++;
						}
						else if(ch.getExt().isAudio()){
							cnt[1]++;
						}
						else if(ch.getExt().isVideo()){
							cnt[2]++;
						}
					}
				}
				tg_inf[0]=pf.getName();
				if(pf.getParentFile()!=null) {
					tg_inf[0]=pf.getParentFile().getName()+"/"+tg_inf[0];
				}
				tg_inf[1]=spath;
				return tg_inf;
			}
			
			String tg_name=convertNameToFileName(pa.getName());
			if(pa.getSubName()!=null) {
				tg_name=tg_name+"("+pa.getSubName()+")";
			}
			String tg_sfx="_hash_"+pf.hashCode()+".lst";
			String tg_path=base_path +FSEP+ tg_name + tg_sfx;	//real fileath
			String fname=pa.getParent().getName()+"/"+tg_name; 	//short filepath for disp in list
			File f=new File(tg_path);
			
			//PMS.dbg("getClipContents: target path="+spath+", name="+pa.getName()+", clipFileName="+clipFileName);
			//PMS.dbg("getClipContents: children="+pa.getChildren());
			//---- open/create clip_file
			//PrintWriter pw = new PrintWriter(f,conf.getRZ_MetafileEncode());
			//FileOutputStream o = new FileOutputStream(f);  
			//PrintWriter pw = new PrintWriter(o,"UTF-8");	//force utf-8
			PrintWriter pw = new PrintWriter(f,"UTF-8");	//force utf-8
			//---- write dp_name
			pw.println("#@name="+fname);
			
			//---- write clip entries
			i=0;
			for (DLNAResource ch : pa.getChildren()) {
				i++;
				//PMS.dbg("getClipContents: child No."+i+", name="+ch.getName()+", Ext="+ch.getExt());
				if(ch.isFolder()) continue;
				if(ch.getExt()!=null) {
					if(ch.getExt().isImage()) {
						cnt[0]++;
					}
					else if(ch.getExt().isAudio()){
						cnt[1]++;
					}
					else if(ch.getExt().isVideo()){
						cnt[2]++;
					}
					else {
						continue;
					}
					//pw.println(ch.getSystemName()); // shortName
					pw.println(ch.getSrcPath());  //full path
				}
			}
			pw.close();
			//tg_inf[0]=tg_name;
			tg_inf[0]=fname;  	//short filepath for disp in list
			tg_inf[1]=tg_path; 	//real filepath
			return tg_inf;
		} catch (IOException e) {
			logger.error("Error in getClipContents: " + e.getMessage());
		}
		return tg_inf;
	}
	
	//replace/recover relative path for base_path (of clip_files)
	//for easy treatment when changed absolute path of clip_files
	private String cpath_encode(String path) {
		//absolute -> relative
		//return path; //test, retur same
		
		return path.replace(base_path,"$clip$");
	}
	private String cpath_decode(String path) {
		//relative -> absolute 
		//return path;  //test, retur same
		return path.replace("/",FSEP).replace("$clip$",base_path);
	}
}
