package org.inria.bmajwatcher.server.data;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.inria.biomaj.exe.bank.BankFactory;
import org.inria.biomaj.exe.bank.BiomajBank;
import org.inria.biomaj.session.bank.BiomajSQLQuerier;
import org.inria.biomaj.session.bank.ProductionDirectory;
import org.inria.biomaj.singleton.BiomajInformation;
import org.inria.biomaj.sql.SQLConnection;
import org.inria.biomaj.sql.SQLConnectionFactory;
import org.inria.biomaj.utils.BiomajConst;
import org.inria.biomaj.utils.BiomajException;
import org.inria.biomaj.utils.BiomajUtils;
import org.inria.bmajwatcher.server.JobSerializer;
import org.inria.bmajwatcher.server.UserManagerServiceImpl;
import org.inria.bmajwatcher.server.utils.Utils;


public class BankRetriever {
	
	public static final String RELEASE = "_release";
	public static final String SESSION_COUNT = "_sessionCount";
	public static final String LAST_SESSION = "_lastSession";
	public static final String DURATION = "_duration";
	public static final String PROD_DIR = "_prodDir";
	public static final String PROD_DIRS = "_prodDirs";
	public static final String FILE_DOWNLOAD_COUNT = "_downloadCount";
	public static final String BANDWIDTH = "_bandwidth";
	public static final String DOWNLOAD_SIZE = "_downloadSize";
	public static final String BANK_SIZE = "_bankSize";
	public static final String LAST_LOG = "_logFile";
	public static final String PREPROCESSES = "_preProcesses";
	public static final String POSTPROCESSES = "_postProcesses";
	public static final String URL = "_url";
	
	private String cachedResult = "";
	private String lastParams = ""; // Parameters of last request
	private long lastCall = System.currentTimeMillis();
	
	
	private static BankRetriever bankRetriever = new BankRetriever();
	
	private int error = 0;
	private int total = 0;
	private int updating = 0;
	
	private final Lock lock = new ReentrantLock();
	private static Logger log = Logger.getLogger(BankRetriever.class);
	
	public static BankRetriever getInstance() {
		return bankRetriever;
	}
	
	
	
	
	public String getBankInfo(String bankName, String parentId, String userKey) {
		
		
		int i = 0;
		StringBuilder sb = new StringBuilder();
		sb.append("<response><status>0</status><data>");
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		
		String[] split = parentId.split("_:_");
		
		if (UserManagerServiceImpl.userHasAccessToBank(userKey, bankName) && (parentId.equals("null") || parentId.isEmpty() || split.length == 2)) { // If > 2, do not try to retrieve info
			try {
				if (parentId.equals("null") || parentId.isEmpty()) { // Get configs
					for (Map<String, String> config : BiomajSQLQuerier.getConfigurations(BiomajSQLQuerier.getBankId(bankName))) {
						sb.append("<record id=\"" + ("configuration_:_" + config.get(BiomajSQLQuerier.CONFIGURATION_ID)) + "\" ");
						sb.append("parent=\"" + 0 + "\" ");
						sb.append("value=\"Configuration [" + config.get(BiomajSQLQuerier.DATE) + "]\" />");
					}
					
				} else if (split[0].equals("configuration")) { // Get config content
					
					// Get configuration info
					String query = "SELECT * FROM configuration WHERE idconfiguration=" + split[1];
					SQLConnection connection = SQLConnectionFactory.getConnection();
					Statement stat = connection.getStatement();
					ResultSet rs = connection.executeQuery(query, stat);
					
					if (rs.next()) {
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"File : " + rs.getString(BiomajSQLQuerier.FILE) + "\" />");
					}
					
					SQLConnectionFactory.closeConnection(stat);
					
					// Get updates
					for (Map<String, String> update : BiomajSQLQuerier.getConfigUpdates(Long.valueOf(split[1]))) {
						sb.append("<record id=\"" + ("update_:_" + update.get(BiomajSQLQuerier.UPDATE_ID)) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Update [Release = " + update.get(BiomajSQLQuerier.UPDATE_RELEASE) + "]\" />");
					}
					
				} else if (split[0].equals("update")) { // Get update content
					
					// Update info
					String query = "SELECT * FROM updateBank WHERE idupdateBank=" + split[1];
					SQLConnection connection = SQLConnectionFactory.getConnection();
					Statement stat = connection.getStatement();
					ResultSet rs = connection.executeQuery(query, stat);
					
					if (rs.next()) {
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Updated ? " + rs.getBoolean(BiomajSQLQuerier.UPDATED) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Start : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.UPDATE_START).getTime())) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"End : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.UPDATE_END).getTime())) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Production directory : " + rs.getString(BiomajSQLQuerier.PRODUCTION_DIR_PATH) + " (" + rs.getString(BiomajSQLQuerier.SIZE_RELEASE) + ")\" />");
						
					}
					
					SQLConnectionFactory.closeConnection(stat);
					
					// Get sessions
					for (Map<String, String> session : BiomajSQLQuerier.getUpdateSessions(Integer.valueOf(split[1]))) {
						sb.append("<record id=\"" + ("session_:_" + session.get(BiomajSQLQuerier.SESSION_ID)) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Session [Status = " + session.get(BiomajSQLQuerier.SESSION_STATUS) + "]\" />");
					}
					
				} else if (split[0].equals("session")) { // Session content
					
					// Session info
					String query = "SELECT * FROM session WHERE idsession=" + split[1];
					SQLConnection connection = SQLConnectionFactory.getConnection();
					Statement stat = connection.getStatement();
					ResultSet rs = connection.executeQuery(query, stat);
					
					if (rs.next()) {
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Start : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.SESSION_START).getTime())) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"End : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.SESSION_END).getTime())) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Log : " + rs.getString(BiomajSQLQuerier.LOG_FILE) + "\" />");
						
					}
					
					SQLConnectionFactory.closeConnection(stat);
					
					for (Map<String, String> task : BiomajSQLQuerier.getSessionTasks(Long.valueOf(split[1]))) {
						sb.append("<record id=\"" + ("task_:_" + task.get(BiomajSQLQuerier.TASK_ID)) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Task : " + task.get(BiomajSQLQuerier.TASK_TYPE) + " [Status = " + task.get(BiomajSQLQuerier.TASK_STATUS) + "]\" />");
					}
					
					
				} else if (split[0].equals("task")) { // Task content
					
					// Task info
					String query = "SELECT * FROM sessionTask WHERE idsessionTask=" + split[1];
					SQLConnection connection = SQLConnectionFactory.getConnection();
					Statement stat = connection.getStatement();
					ResultSet rs = connection.executeQuery(query, stat);
					
					if (rs.next()) {
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Start : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.TASK_START).getTime())) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"End : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.TASK_END).getTime())) + "\" />");
						
						String type = rs.getString(BiomajSQLQuerier.TASK_TYPE);
						
						if (type.equalsIgnoreCase("release")) {
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Value : " + rs.getString(BiomajSQLQuerier.VALUE) + "\" />");
							
						} else if (type.equalsIgnoreCase("check")) {
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Files to extract : " + rs.getInt(BiomajSQLQuerier.NB_EXTRACT) + "\" />");
							
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Local online files : " + rs.getInt(BiomajSQLQuerier.NB_LOCAL_ONLINE_FILES) + "\" />");
							
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Local offline files : " + rs.getInt(BiomajSQLQuerier.NB_LOCAL_OFFLINE_FILES) + "\" />");
							
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Files to download : " + rs.getInt(BiomajSQLQuerier.NB_DOWNLOADED_FILES) + "\" />");
							
						} else if (type.equalsIgnoreCase("download")) {
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Download speed : " + rs.getFloat(BiomajSQLQuerier.BANDWIDTH) + " MB/s\" />");
							
						} else if (type.equalsIgnoreCase("addLocalFiles")) {
							
						} else if (type.equalsIgnoreCase("makeRelease")) {
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Moved files : " + rs.getInt(BiomajSQLQuerier.NB_FILES_MOVED) + "\" />");
							
							sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
							sb.append("parent=\"" + parentId + "\" ");
							sb.append("value=\"Copied files : " + rs.getInt(BiomajSQLQuerier.NB_FILES_COPIED) + "\" />");
							
						} else if (type.equalsIgnoreCase("deployment")) {
							
						} else if (type.equalsIgnoreCase("preprocess") || type.equalsIgnoreCase("postprocess")) {
							for (Map<String, String> meta : BiomajSQLQuerier.getTaskMetaprocesses(Integer.valueOf(split[1]))) {
								sb.append("<record id=\"" + ("metaprocess_:_" + meta.get(BiomajSQLQuerier.META_ID)) + "\" ");
								sb.append("parent=\"" + parentId + "\" ");
								sb.append("value=\"Metaprocess [" + meta.get(BiomajSQLQuerier.META_NAME) + "]" +
										" [" + meta.get(BiomajSQLQuerier.META_STATUS) + "]\" />");
							}
						}
						
					}
						
					SQLConnectionFactory.closeConnection(stat);
					
				} else if (split[0].equals("metaprocess")) {
					
					String query = "SELECT * FROM metaprocess WHERE idmetaprocess='" + split[1] + "'";
					SQLConnection connection = SQLConnectionFactory.getConnection();
					Statement stat = connection.getStatement();
					ResultSet rs = connection.executeQuery(query, stat);
					
					if (rs.next()) {
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Start : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.META_START).getTime())) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"End : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.META_END).getTime())) + "\" />");
						
					}
					
					SQLConnectionFactory.closeConnection(stat);
					
					for (Map<String, String> process : BiomajSQLQuerier.getMetaprocessProcesses(split[1])) {
						sb.append("<record id=\"" + ("process_:_" + process.get(BiomajSQLQuerier.PROC_ID)) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Process [" + process.get(BiomajSQLQuerier.PROC_NAME) + "]\" />");
					}
						
				} else if (split[0].equals("process")) {
					
					String query = "SELECT * FROM process WHERE idprocess=" + split[1];
					SQLConnection connection = SQLConnectionFactory.getConnection();
					Statement stat = connection.getStatement();
					ResultSet rs = connection.executeQuery(query, stat);
					
					if (rs.next()) {
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Exe : " + rs.getString(BiomajSQLQuerier.PROC_EXE) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Args : " + rs.getString(BiomajSQLQuerier.PROC_ARGS) + "\" />");
						
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"Start : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.PROC_START).getTime())) + "\" />");
						
						sb.append("<record id=\"" + (parentId + "_:_" + i++) + "\" ");
						sb.append("parent=\"" + parentId + "\" ");
						sb.append("value=\"End : " + sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.PROC_END).getTime())) + "\" />");
						
					}
					
					SQLConnectionFactory.closeConnection(stat);
				}
			} catch (SQLException ex) {
				ex.printStackTrace();
				log.error(ex);
			}
		}
		
		sb.append("</data></response>");
		
		return sb.toString();
	}
	
	
	/**
	 * Returns a string that contains the bank list in XML format.
	 * 
	 * @param biomajRoot biomaj root directory
	 * @param userKey key identifying bank owner
	 * @return
	 */
	public String getBankList(String biomajRoot, String _type, String _formats, String _name, String _release, String _latestSession, String _status, String _isGroup, String _visibility, String _isSched, String userKey) {
		
		lock.lock();
		
		String currentParams = "";
		currentParams += _type + _formats + _name + _release + _latestSession + _status + _isGroup + _visibility + _isSched + userKey;
		
		// Check cache
		long currentTime = System.currentTimeMillis();
		if (currentTime - lastCall < 1000 && !cachedResult.trim().isEmpty() && currentParams.equals(lastParams)) { // < 1s
			lastCall = currentTime;
			lastParams = currentParams;
			
			lock.unlock();
			return cachedResult;
		}
		
		lastParams = currentParams;
		lastCall = currentTime;
		
		int curError = 0;
		int curTotal = 0;
		int userTotal = 0;
		int curUpdating = 0;
		
		boolean isAdmin = UserManagerServiceImpl.isAdminKey(userKey);
		String query = "";
		List<Integer> users = null;
		if (isAdmin) {
			query = "SELECT session,path,ref_idbank,name,visibility,ref_iduser,auth_key FROM productionDirectory,bank,bw_user WHERE state='available' AND ref_idbank=idbank AND ref_iduser=iduser " +
					"ORDER BY ref_idbank";
			
		} else if (!userKey.isEmpty() && (users = UserManagerServiceImpl.getGroupUsersFromKey(userKey)).size() > 0) {
			query = "SELECT session,path,ref_idbank,name,visibility,ref_iduser,auth_key FROM productionDirectory,bank,bw_user " +
					"WHERE state='available' AND ref_idbank=idbank AND ref_iduser=iduser AND idbank IN " +
					"(SELECT idbank FROM bank WHERE ref_iduser IN (SELECT ref_iduser FROM bw_user_has_group WHERE ref_idgroup IN " +
					"(SELECT ref_idgroup FROM bw_user_has_group WHERE";
			
			for (Integer id : users) {
				query += " ref_iduser=" + id + " OR";
			}
			query = query.substring(0, query.length() - 3);
			query += ")) OR visibility=true) ORDER BY ref_idbank";
			
		} else {
			query = "SELECT session,path,ref_idbank,name,visibility,ref_iduser,auth_key FROM productionDirectory,bank,bw_user WHERE state='available' AND ref_idbank=idbank AND ref_iduser=iduser " +
					"AND idbank IN (SELECT idbank FROM bank WHERE visibility=true) ORDER BY ref_idbank"; 
		}
		
		SQLConnection connection = SQLConnectionFactory.getConnection();
		Statement stat = connection.getStatement();
		ResultSet rs = connection.executeQuery(query, stat);
		Map<String, Map<String, String>> data = new HashMap<String, Map<String,String>>();
		Map<String, Map<Long, String>> bankPaths = new HashMap<String, Map<Long, String>>();
		try {
			while (rs.next()) {
				Map<String, String> bank = new HashMap<String, String>(); // Current bank
				String bankName = rs.getString(BiomajSQLQuerier.BANK_NAME);
				bank.put(BiomajSQLQuerier.BANK_NAME, bankName);
				bank.put(BiomajSQLQuerier.BANK_ID, String.valueOf(rs.getInt(3)));
				bank.put("visibility", String.valueOf(rs.getBoolean(5)));
				bank.put("owner", String.valueOf(rs.getInt(6)));
				bank.put("key", rs.getString(7));
				
				if (!bankPaths.containsKey(bankName)) {
					// Get status and session end
					long confId = BiomajSQLQuerier.getLatestConfigurationWithSession(rs.getInt(3), false);
					query = "SELECT status,endTime FROM session where ref_idupdateBank IN " +
							"(SELECT idupdateBank FROM updateBank WHERE ref_idconfiguration=" + confId + ") and endTime=" +
							"(SELECT max(endTime) FROM session where ref_idupdateBank IN " +
							"(SELECT idupdateBank FROM updateBank WHERE ref_idconfiguration=" + confId + "))";
					Statement stat2 = connection.getStatement();
					ResultSet rs2 = connection.executeQuery(query, stat2);
					rs2.next();
					bank.put(BiomajSQLQuerier.SESSION_STATUS, rs2.getBoolean(BiomajSQLQuerier.SESSION_STATUS) ? "OK" : "KO");
					bank.put(BiomajSQLQuerier.SESSION_END, BiomajUtils.dateToString(
							new Date(rs2.getTimestamp(BiomajSQLQuerier.SESSION_END).getTime()), Locale.US));
					SQLConnectionFactory.closeConnection(stat2);
					
					// Stores the path for later processing
					Map<Long, String> pd = new HashMap<Long, String>();
					pd.put(rs.getLong(BiomajSQLQuerier.DIR_SESSION), rs.getString(BiomajSQLQuerier.DIR_PATH));
					bankPaths.put(bankName, pd);
					
					data.put(bankName, bank);
				} else {
					bankPaths.get(bankName).put(rs.getLong(BiomajSQLQuerier.DIR_SESSION), rs.getString(BiomajSQLQuerier.DIR_PATH));
				}
			}
			SQLConnectionFactory.closeConnection(stat);
		} catch (SQLException ex) {
			SQLConnectionFactory.closeConnection(stat);
			log.error(ex);
			lock.unlock();
			
			return "";
		}
		
		StringBuilder content = new StringBuilder();
		
		String tmpDir = "";
		try {
			tmpDir = BiomajInformation.getInstance().getProperty(BiomajInformation.TMPDIR);
			if (!tmpDir.startsWith("/") && biomajRoot != null)
				tmpDir = biomajRoot + "/" + tmpDir.substring(1);
			else if (biomajRoot == null)
				log.warn("Could not find tmp directory");
		} catch (BiomajException e) {
			tmpDir = biomajRoot + "/tmp";
			log.error(e);
		}
		
		Map<String, String> releases = getBankReleases(bankPaths);
		
		
		// Building response
		for (String bankName : data.keySet()) {
			
			String line = "";
			
			String session = data.get(bankName).get(BiomajSQLQuerier.SESSION_END);
			
			// Check bank name, release, session, status
			if (!bankName.contains(_name) || !releases.get(bankName).contains(_release) || !session.contains(_latestSession))
				continue;
			
			String formats = "";
			String dbType = "";
			try {
				BiomajBank b = new BankFactory().createBank(bankName, false);
				Properties props = b.getPropertiesFromBankFile();
				if (props.containsKey(BiomajConst.dbFormatsProperty))
					formats = props.getProperty(BiomajConst.dbFormatsProperty);
				if (props.containsKey(BiomajConst.typeProperty))
					dbType = props.getProperty(BiomajConst.typeProperty);
			} catch (BiomajException e1) {
				e1.printStackTrace();
			}
			
			// Check format and type
			if (!formats.contains(_formats) || !dbType.contains(_type))
				continue;
			
			line += "<record id=\"" + data.get(bankName).get(BiomajSQLQuerier.BANK_ID) + "\" " +
					"name=\"" + bankName + "\" " +
					"formats=\"" + formats + "\" " +
					"type=\"" + dbType + "\" ";
			
			
			boolean own = !userKey.isEmpty() && userKey.equals(data.get(bankName).get("key"));
			line += "owner=\"" + own + "\" ";
			
			boolean isInGroup = (own || users != null && users.contains(Integer.valueOf(data.get(bankName).get("owner"))));
			
			if (!String.valueOf(isInGroup).contains(_isGroup))
				continue;
			line += "group=\"" + isInGroup + "\" ";
			
			String status = "-";
			String visibility = "";
			String isSched = "";
			
			if (isAdmin || isInGroup) {
				userTotal++;
				if (Boolean.valueOf(data.get(bankName).get("visibility")))
					visibility = "public";
//					line += "visibility=\"" + "public" + "\" ";
				else
					visibility = "private";
//					line += "visibility=\"" + "private" + "\" ";
				
				
				if (new File(tmpDir + "/" + bankName + ".lock").exists()) {
					status = "Updating";
//					line += "status=\"Updating\" ";
					curUpdating++;
				} else {
					status = data.get(bankName).get(BiomajSQLQuerier.SESSION_STATUS);
//					line += "status=\"" + s + "\" ";
					if (status.equalsIgnoreCase("KO"))
						curError++;
				}
				
				if (isScheduled(data.get(bankName).get(BiomajSQLQuerier.BANK_NAME))) {
					isSched = "sched.png";
//					line += "isSched=\"" + "sched.png" + "\" ";
				}
				
			}/* else {
				line += "status=\"-\" ";
				line += "visibility=\"\" ";
				line += "isSched=\"\" ";
			}*/
			
			if (!status.contains(_status) || !visibility.contains(_visibility) || !isSched.contains(_isSched))
				continue;
			
			
			line += "status=\"" + status + "\" ";
			line += "visibility=\"" + visibility + "\" ";
			line += "isSched=\"" + isSched + "\" ";
			
			line += "release=\"" + releases.get(bankName) + "\" session=\"" + session + "\" />";
			
			
			content.append(line);
			curTotal++;
			
		}
		
		StringBuilder res = new StringBuilder();
		
		String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
				+ "<response>" +
				"<status>0</status>" +
				"<startRow>0</startRow>" +
				"<endRow>" + curTotal + "</endRow>" +
				"<totalRows>" + curTotal + "</totalRows>" +
				"<data>\n";
		
		res.append(header);
		res.append(content);
		res.append("</data></response>");
		
		error = curError;
		total = curTotal;
		if (!userKey.isEmpty())
			total = userTotal;
		updating = curUpdating;
		
		cachedResult = res.toString();
		
		lock.unlock();
		
		return cachedResult;
		
	}
	
	
	/**
	 * Returns the for each bank its latest release from the provided production directories.
	 * 
	 * @param banks
	 * @return
	 */
	private Map<String, String> getBankReleases(Map<String, Map<Long, String>> banks) {
		
		Map<String, String> res = new HashMap<String, String>();
		SQLConnection connection = SQLConnectionFactory.getConnection();
		Statement stat = connection.getStatement();
		
		for (String bankName : banks.keySet()) {
			
			long max = -1;
			// Get latest session
			for (Long session : banks.get(bankName).keySet()) {
				max = max > session ? max : session;
			}
			
			String query = "SELECT updateRelease FROM updateBank WHERE idupdateBank=" +
					"(SELECT ref_idupdateBank FROM session where idsession=" + max + ");";
			ResultSet rs = connection.executeQuery(query, stat);
			String release = "???";
			try {
				if (rs.next())
					release = rs.getString(1);
			} catch (SQLException e) {
				log.error(e);
			}
			
			res.put(bankName, release);
		}
		SQLConnectionFactory.closeConnection(stat);
		
		return res;
	}
	
	public void getBankDependencies(String bank, Map<String, String> res) {
		BankFactory bf = new BankFactory();
		try {
			BiomajBank bb = bf.createBank(bank, false);
			String value = "";
			if ((value = bb.getPropertiesFromBankFile().getProperty("db.source")) != null) {
				for (String source : value.split(",")) {
					getBankDependencies(source, res);
				}
				res.put(bank, value);
			}
		} catch (BiomajException e) {
			res = null;
			log.error(e);
		}
	}
	
	public Map<String, String> getBankDetail(String bankName) {		
		
		Map<String, String> info = new HashMap<String, String>();
		File file = BankFactory.getBankPath(bankName);
		String path = file.getAbsolutePath();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String lastModif = sdf.format(new Date(file.lastModified()));
		String tmp = path.substring(0, path.lastIndexOf('/'));
		String user = tmp.substring(tmp.lastIndexOf('/') + 1);
		BankFactory bf = new BankFactory();
		
		try {
			BiomajBank bb = bf.createBank(bankName, false);
			Properties props = bb.getPropertiesFromBankFile();
			
			// General info
			info.put("owner", user);
			info.put("name", bb.getDbName());
			info.put("dbFullName", bb.getFullDbName());
			info.put("date", lastModif);
			info.put("_url", props.getProperty("protocol") + "://" + (props.getProperty("server") + "/" + props.getProperty("remote.dir")).replaceAll("//", "/"));
			info.put("remoteFiles", props.getProperty("remote.files"));
			info.put("remoteExcludedFiles", props.getProperty("remote.excluded.files"));
			info.put("localFiles", props.getProperty("local.files"));
			info.put("versionDirectory", (props.getProperty("data.dir") + "/" + props.getProperty("dir.version")).replaceAll("//", "/"));
			info.put("offlineDirectory", (props.getProperty("data.dir") + "/" + props.getProperty("offline.dir.name")).replaceAll("//", "/"));
			
			StringBuilder sbPs = new StringBuilder();
			// Preprocess
			if (props.containsKey("db.pre.process")) { // Contains preprocess
				String metas = props.getProperty("db.pre.process");
				sbPs.append("BLOCK=;");
				if (metas != null) {
					for (String meta : metas.split(",")) {
						sbPs.append("META=" + meta + ";");
						String pss = props.getProperty(meta);
						if (pss != null) {
							for (String ps : pss.split(",")) {
								String exe = props.getProperty(ps + ".exe");
								sbPs.append("PS=" + ps + "_:_" + exe + ";");
							}
						}
					}
				}
			}
			
			info.put("_preProcesses", sbPs.toString());
			
			// Postprocesses
			sbPs = new StringBuilder();
			String[] blocks = null;
			if (props.containsKey("db.post.process")) {
				blocks = new String[1];
				blocks[0] = "";
			} else if (props.containsKey("BLOCKS")) {
				blocks = props.getProperty("BLOCKS").split(",");
			}
			
			if (blocks != null) {
				for (String block : blocks) {
					sbPs.append("BLOCK=" + block + ";");
					String metas;
					if (block.isEmpty()) {
						metas = props.getProperty("db.post.process");
					} else {
						metas = props.getProperty(block + ".db.post.process");
					}
					
					if (metas != null) {
						for (String meta : metas.split(",")) {
							sbPs.append("META=" + meta + ";");
							String pss = props.getProperty(meta);
							if (pss != null) {
								for (String ps : pss.split(",")) {
									String exe = props.getProperty(ps + ".exe");
									sbPs.append("PS=" + ps + "_:_" + exe + ";");
								}
							}
						}
					}
				}
			}
			
			info.put("_postProcesses", sbPs.toString());
			
		} catch (BiomajException e) {
			e.printStackTrace();
		}
		
		// Production directories and release info
		List<ProductionDirectory> lPd = BiomajSQLQuerier.getAvailableProductionDirectories(bankName);

		if (lPd.size() == 0)
			return info;

		long goodSession = 0 ;
		Date t = null;
		for (ProductionDirectory pd : lPd) {
			if (t == null)
				goodSession = pd.getSession();
			else if (t.getTime() < pd.getCreationDate().getTime())
				goodSession = pd.getSession();
		}
		
		Map<String, String> update = BiomajSQLQuerier.getUpdateFromId(goodSession);
		if (update != null) {
			
			SQLConnection connection = SQLConnectionFactory.getConnection();
			Statement stat = connection.getStatement();
			// Get session info
			String query = "SELECT * FROM session WHERE idsession=" + update.get(BiomajSQLQuerier.ID_LAST_SESSION);
			ResultSet rs = connection.executeQuery(query, stat);
			String sessionStart = "";
			String logFile = "";
			String duration = "";
			try {
				if (rs.next()) {
					sessionStart = sdf.format(new Date(rs.getTimestamp(BiomajSQLQuerier.SESSION_START).getTime()));
					logFile = rs.getString(BiomajSQLQuerier.LOG_FILE);
					duration = Utils.millisToHumanReadableDuration(rs.getTimestamp(BiomajSQLQuerier.SESSION_END).getTime() - rs.getTimestamp(BiomajSQLQuerier.SESSION_START).getTime());
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
			// Get sessionTask info
			String bandwidth = "";
			String dl = "";
			query = "SELECT * FROM sessionTask WHERE idsessionTask IN " +
					"(SELECT ref_idsessionTask FROM session_has_sessionTask WHERE ref_idsession=" + update.get(BiomajSQLQuerier.ID_LAST_SESSION) +
					" AND (taskType='download' OR taskType='check'))";
			
			rs = connection.executeQuery(query, stat);
			try {
				while (rs.next()) {
					if (rs.getString(BiomajSQLQuerier.TASK_TYPE).equals("download")) {
						bandwidth = rs.getString(BiomajSQLQuerier.BANDWIDTH);
					} else {
						dl = rs.getString(BiomajSQLQuerier.NB_DOWNLOADED_FILES);
					}
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
			SQLConnectionFactory.closeConnection(stat);
			
			info.put("_release", update.get(BiomajSQLQuerier.UPDATE_RELEASE));
			info.put("_sessionCount", update.get(BiomajSQLQuerier.NB_SESSIONS));
			info.put("_lastSession", sessionStart);
			info.put("_duration", duration);
			info.put("_prodDir", update.get(BiomajSQLQuerier.PRODUCTION_DIR_PATH));
			info.put("_downloadCount", dl);
			info.put("_bandwidth", bandwidth);
			info.put("_downloadSize", update.get(BiomajSQLQuerier.SIZE_DOWNLOAD));
			info.put("_bankSize", update.get(BiomajSQLQuerier.SIZE_RELEASE));
			info.put("_logFile", logFile);
			
		}
		
		if (lPd.size() > 0) {
			StringBuilder strb = new StringBuilder();
			Collections.sort(lPd);
			strb.append("<ul>");
			for (ProductionDirectory pd : lPd) {
				strb.append("<li>" + BiomajUtils.dateToString(pd.getCreationDate(), Locale.getDefault())+ " " + pd.getPath() + "</li>");
			}
			strb.append("</ul>");
			info.put(PROD_DIRS, strb.toString());
		}
		
		info.put("_dependencies", "");
		
		return info;
	}
	
	/**
	 * Checks in the jobs.xml file if the specified bank is scheduled.
	 * 
	 * @param bankName
	 * @return
	 */
	private boolean isScheduled(String bankName) {
		// We should use some kind of xml parser to properly search for the bank
		// but it is so much easier this way (and faster ?)
		
		try {
			BufferedReader br = new BufferedReader(new FileReader(JobSerializer.getJobsFileName()));
			String line = null;
			try {
				while ((line = br.readLine()) != null) {
					if (line.contains("name=\"" + bankName + "\""))
						return true;
				}

				br.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
		return false;
	}
	
	public int getError() {
		lock.lock(); // wait for error count to be processed
		int err = error;
		lock.unlock();
		return err;
		
	}
	
	public int getUpdating() {
		lock.lock(); // wait for updating count to be processed
		int upd = updating;
		lock.unlock();
		return upd;
	}
	
	public int getTotal() {
		lock.lock(); // wait for total count to be processed
		int tot = total;
		lock.unlock();
		return tot;
	}
	
}
