package net.pms.dlna.virtual;

import java.io.File;
import java.util.ArrayList;

import net.pms.PMS;
import net.pms.dlna.DLNAMediaDatabase;
import net.pms.dlna.DLNAResource;
import net.pms.dlna.DVDISOFile;
import net.pms.dlna.PlaylistFolder;
import net.pms.dlna.RealFile;
import net.pms.configuration.RendererConfiguration;

public class MediaLibraryFolder extends VirtualFolder {
	public static final int FILES = 0;
	public static final int TEXTS = 1;
	public static final int PLAYLISTS = 2;
	public static final int ISOS = 3;
	private String sqls[];
	private int expectedOutputs[];
	private DLNAMediaDatabase database;
	private int last_sort_type=-2;

	public MediaLibraryFolder(String name, String sql, int expectedOutput, RendererConfiguration r) {
		this(name, new String[]{sql}, new int[]{expectedOutput},r);
	}

	public MediaLibraryFolder(String name, String sql[], int expectedOutput[], RendererConfiguration r) {
		super(name, null);
		this.setDefaultRenderer(r);
		//PMS.dbg("MediaLibraryFolder2-1: name="+getName()+", sort_type="+this.rz_MetaSortType);
		
		if(false) {
			//---- regzam, add case insensitve statements: not supported by H2DB!!
			String[] sql2=new String[sql.length+2];
			sql2[0]="alter session set nls_comp='ANSI'";
			sql2[1]="alter session set nls_sort='BINARY_CI'";
			for (int i=0;i<sql.length;i++) {
				sql2[i+2]=sql[i];
			}
			this.sqls = sql2;
		}
		else {
			this.sqls = sql;
		}
		
		this.expectedOutputs = expectedOutput;
		this.database = PMS.get().getDatabase();
		this.sort_enabled=true; 
		this.rz_MetaSortType=PMS.SORT_NAME_NUM; 

		// double check the database has been initialized (via PMS.init -> PMS.initializeDatabase)
		// http://www.ps3mediaserver.org/forum/viewtopic.php?f=6&t=11474
		assert this.database != null;
		
		if(ctlFolder==null && PMS.getConfiguration().getRZ_menu_type()>=4) {
			//disp menues, even if only folders
			int ures=(isUnderResumeFolder(this)?1:0);// 
			createSortMenu(this,ures);
			createScriptMenuFolder(this,ures);
			createClearFolderCahceMenu(this,ures);
			createSelClipPath(this,ures);
			createVideoSettingMenu(this);
		}
		//PMS.dbg("MediaLibraryFolder: name="+getName()+", sort_type="+PMS.rz_SortTypeID2string(this.rz_MetaSortType));
	}
	
	public void setLastSortType(int sort_type) {
		last_sort_type=sort_type;
	}

	@Override
	public void discoverChildren() {
		long tim1=0;
		if(PMS.rz_trace>0) {
			tim1=System.currentTimeMillis();
			PMS.trace("MediaLibraryFolder.discoverChildren: Start, folder name="+getName()+", layer="+(sqls==null?0:sqls.length)
			+", sort_type="+PMS.rz_SortTypeID2string(this.rz_MetaSortType));
		}
		
		if (sqls.length > 0) {
			if(sqls.length==1) {  // I'm last layer 
				if(last_sort_type!= -2) {  // forced sort_type
					this.rz_MetaSortType=last_sort_type;
				}
				else {
					this.rz_MetaSortType=PMS.SORT_ORIGIN;
				}
			}
			
			String sql = sqls[0];
			int expectedOutput = expectedOutputs[0];
			if (sql != null) {
				sql = transformSQL(sql);
				if (expectedOutput == FILES) {
					//sort_enabled=true; //regzamod
					ArrayList<File> list = database.getFiles(sql);
					if (list != null) {
						if(true) {
							for (File f : list) {
								addChild(new RealFile(f));
							}
						}
						/*
						//divide to subfolders: old method, no need to subfoldering here! (done by super-class DLNAResource) 
						int pmax=999,pseg=900;
						if(list.size()<=pmax) {  
							//PMS.dbg("files count Over "+pmax+" --> Divide into subfolders");
							int sfmax=(list.size())/pseg+1;
							int sf,tcnt=0;
							for(sf=0;sf<sfmax;sf++) {
								VirtualFolder vf= new VirtualFolder("[ "+(sf*pseg)+1+" - "+((sf+1)*pseg)+" ]",null);
								addChild(vf);
								vf.sort_enabled=true;
								vf.rz_MetaSortType=PMS.SORT_ORIGIN;
								for (int i=0;i<pseg;i++) {
									File f=list.get(sf*pseg+i);
									vf.addChild(new RealFile(f));
									tcnt++;
									if(tcnt>=list.size()) break;
								}
								if(tcnt>=list.size()) break;
							}
						}
						*/
					}
					//if(!isDiscovered()) rz_MetaSortType=PMS.SORT_ORIGIN;
				} else if (expectedOutput == PLAYLISTS) {
					//sort_enabled=true; //regzamod
					ArrayList<File> list = database.getFiles(sql);
					if (list != null) {
						for (File f : list) {
							addChild(new PlaylistFolder(f));
						}
					}
					//if(!isDiscovered()) rz_MetaSortType=PMS.SORT_ORIGIN;  //initial default
				} else if (expectedOutput == ISOS) {
					//sort_enabled=true; //regzamod
					ArrayList<File> list = database.getFiles(sql);
					if (list != null) {
						for (File f : list) {
							addChild(new DVDISOFile(f));
						}
					}
					//if(!isDiscovered()) rz_MetaSortType=PMS.SORT_ORIGIN;
				} else if (expectedOutput == TEXTS) {
					ArrayList<String> list = database.getStrings(sql);
					if (list != null) {
						int cnt=0;
						for (String s : list) {
							String sqls2[] = new String[sqls.length - 1];
							int expectedOutputs2[] = new int[expectedOutputs.length - 1];
							System.arraycopy(sqls, 1, sqls2, 0, sqls2.length);
							System.arraycopy(expectedOutputs, 1, expectedOutputs2, 0, expectedOutputs2.length);
							//addChild(new MediaLibraryFolder(s, sqls2, expectedOutputs2));
							MediaLibraryFolder mf=new MediaLibraryFolder(s, sqls2, expectedOutputs2,this.getDefaultRenderer());
							addChild(mf);
							mf.setLastSortType(last_sort_type);  //inherit to child
							cnt++;
						}
					}
				}
			}
		}
		if(PMS.rz_trace>0) {
			long tim2=System.currentTimeMillis();
			PMS.trace("MediaLibraryFolder.discoverChildren: End, elapsed="+(tim2-tim1));
		}
	}

	@Override
	public void resolve() {
		super.resolve();
	}

	//public boolean needSort() {
	//	PMS.dbg("MediaLibralyFolder: needSort="+sort_enabled+", sort_type="+getSortType());
	//	return sort_enabled;
	//}

	private String transformSQL(String sql) {

		sql = sql.replace("${0}", transformName(getName()));
		if (getParent() != null) {
			sql = sql.replace("${1}", transformName(getParent().getName()));
			if (getParent().getParent() != null) {
				sql = sql.replace("${2}", transformName(getParent().getParent().getName()));
				if (getParent().getParent().getParent() != null) {
					sql = sql.replace("${3}", transformName(getParent().getParent().getParent().getName()));
					if (getParent().getParent().getParent().getParent() != null) {
						sql = sql.replace("${4}", transformName(getParent().getParent().getParent().getParent().getName()));
					}
				}
			}
		}
		return sql;
	}

	private String transformName(String name) {
		if (name.equals(DLNAMediaDatabase.NONAME)) {
			name = "";
		}
		name = name.replace("'", "''"); // issue 448
		return name;
	}

	@Override
	public boolean isRefreshNeeded() {
		long systim=System.currentTimeMillis();
		long lastim=getLastRefreshTime();
		
		setLastRefreshTime(systim);
		
		//PMS.dbg("==== MediaLibraryFolder.isRefreshNeeded: called "
		//	+", lastim="+lastim+", systim="+systim);
		
		if(lastim+2000 > systim) {  //tricky, to ignore useless simultaneous calls
			if(PMS.rz_debug>1) PMS.dbg("MediaLibraryFolder.isRefreshNeeded: aleady recently(within 2sec) updated, return FALSE");
			return false;
		}
		
		long updtim=database.getDBUpdateTime();
		
		if(PMS.rz_debug>1) {
			PMS.dbg("MediaLibraryFolder.isRefreshNeeded: called"
				+", lastim="+lastim
				+", updtim="+updtim
				+", systim="+systim);
		}
		
		if(updtim>0 && (lastim > updtim)) {
			if(PMS.rz_debug>1) PMS.dbg("MediaLibraryFolder.isRefreshNeeded: lastim > updtim (already updated) --> return FALSE");
			return false;
		}
		if(PMS.rz_debug>1) PMS.dbg("MediaLibraryFolder.isRefreshNeeded: lastim < updtim (new updated) --> return TRUE");
		return true;
	}

	@Override
	public void refreshChildren() {	// regzamod
		getChildren().clear();
		deleteMenus();
		if(ctlFolder==null && PMS.getConfiguration().getRZ_menu_type()>=4) {
			//disp menues, even if only folders
			int ures=(isUnderResumeFolder(this)?1:0);// 
			createSortMenu(this,ures);
			createScriptMenuFolder(this,ures);
			createClearFolderCahceMenu(this,ures);
			createSelClipPath(this,ures);
			createVideoSettingMenu(this);
		}
		discoverChildren();
	}
	
	//following mehod is difficult to make accurate refresh!
	public void refreshChildren_old() {
		if(PMS.rz_debug>1) PMS.dbg("==== MediaLibraryFolder.refreshChildren: called --> exec refresh");
		
		long systim=System.currentTimeMillis();
		setLastRefreshTime(systim);
		doSort=true;

		ArrayList<File> list = null;
		ArrayList<String> strings = null;
		int expectedOutput = 0;
		if (sqls.length > 0) {
			String sql = sqls[0];
			expectedOutput = expectedOutputs[0];
			if (sql != null) {
				sql = transformSQL(sql);
				if (expectedOutput == FILES || expectedOutput == PLAYLISTS || expectedOutput == ISOS) {
					list = database.getFiles(sql);
				} else if (expectedOutput == TEXTS) {
					strings = database.getStrings(sql);
				}
			}
		}
		ArrayList<File> addedFiles = new ArrayList<File>();
		ArrayList<String> addedString = new ArrayList<String>();
		ArrayList<DLNAResource> removedFiles = new ArrayList<DLNAResource>();
		ArrayList<DLNAResource> removedString = new ArrayList<DLNAResource>();
		int i = 0;
		if (list != null) {
			for (File f : list) {
				boolean present = false;
				for (DLNAResource d : getChildren()) {
					if (i == 0 && (!(d instanceof VirtualFolder) || (d instanceof MediaLibraryFolder))) {
						removedFiles.add(d);
					}
					String name = d.getName();
					long lm = d.getLastmodified();
					boolean video_ts_hack = (d instanceof DVDISOFile) && d.getName().startsWith(DVDISOFile.PREFIX) && d.getName().substring(DVDISOFile.PREFIX.length()).equals(f.getName());
					if ((f.getName().equals(name) || video_ts_hack) && f.lastModified() == lm) {
						removedFiles.remove(d);
						present = true;
					}
				}
				i++;
				if (!present) {
					addedFiles.add(f);
				}
			}
		}
		i = 0;
		if (strings != null) {
			for (String f : strings) {
				boolean present = false;
				for (DLNAResource d : getChildren()) {
					if (i == 0 && (!(d instanceof VirtualFolder) || (d instanceof MediaLibraryFolder))) {
						removedString.add(d);
					}
					String name = d.getName();
					if (f.equals(name)) {
						removedString.remove(d);
						present = true;
					}
				}
				i++;
				if (!present) {
					addedString.add(f);
				}
			}
		}

		for (DLNAResource f : removedFiles) {
			getChildren().remove(f);
		}
		for (DLNAResource s : removedString) {
			getChildren().remove(s);
		}
		for (File f : addedFiles) {
			if (expectedOutput == FILES) {
				addChild(new RealFile(f));
			} else if (expectedOutput == PLAYLISTS) {
				addChild(new PlaylistFolder(f));
			} else if (expectedOutput == ISOS) {
				addChild(new DVDISOFile(f));
			}
		}
		for (String f : addedString) {
			if (expectedOutput == TEXTS) {
				String sqls2[] = new String[sqls.length - 1];
				int expectedOutputs2[] = new int[expectedOutputs.length - 1];
				System.arraycopy(sqls, 1, sqls2, 0, sqls2.length);
				System.arraycopy(expectedOutputs, 1, expectedOutputs2, 0, expectedOutputs2.length);
				addChild(new MediaLibraryFolder(f, sqls2, expectedOutputs2, this.getDefaultRenderer()));
			}
		}

		//return removedFiles.size() != 0 || addedFiles.size() != 0 || removedString.size() != 0 || addedString.size() != 0;
	}
}
