package org.inria.bmajwatcher.server;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * 
 * Persists the jobs in an XML file.
 * 
 * @author rsabas
 *
 */
public class JobSerializer {

	private static JobSerializer serializer = null;
	
	public static final String JOBS 		= "jobs";
	public static final String JOB 			= "job";
	public static final String JOB_NAME 	= "name";
	public static final String JOB_CRON 	= "cron";
	public static final String BANK 		= "bank";
	public static final String BANK_NAME 	= "name";
	public static final String USER			= "user";
	public static final String LOGIN		= "login";
	
	private static Logger log = Logger.getLogger(JobSerializer.class);
	
	private static String xmlFile = "";
	
	private Element root;
	private Document document;
	private XMLOutputter writer = new XMLOutputter(Format.getPrettyFormat());
	
	public static JobSerializer getInstance(String path) {
		if (serializer == null)
			serializer = new JobSerializer(path);
		
		return serializer;
	}
	
	/**
	 * Private constructor
	 */
	private JobSerializer(String path) {
		
		xmlFile = path + "/jobs.xml";
		
		SAXBuilder xmlBuilder = new SAXBuilder("org.apache.xerces.parsers.SAXParser", true);
		xmlBuilder.setFeature("http://apache.org/xml/features/validation/schema", true);
		
		try {
			if (new File(xmlFile).exists()) {
				document = xmlBuilder.build(new File(xmlFile));
				root = document.getRootElement();
			} else {
				log.warn("Jobs file does not exist : " + xmlFile);
				root = new Element(JOBS);
			}
			
		} catch (JDOMException e) {
			log.error("Malformed xml file", e);
			e.printStackTrace();
			System.exit(1);
		} catch (IOException e) {
			log.error("", e);
		}
	}
	
	private Element findJob(String jobName) {
		List<?> users = root.getChildren(USER);
		for (Object usr : users) {
			Element elt = (Element) usr;
			List<?> jobs = elt.getChildren(JOB);
			for (Object job : jobs) {
				Element current = (Element) job;
				if (current.getAttributeValue(JOB_NAME).equals(jobName)) {
					return current;
				}
			}
		}
		return null;
	}
	
	private Element findUser(String login) {
		List<?> users = root.getChildren(USER);
		for (Object usr : users) {
			Element elt = (Element) usr;
			if (elt.getAttributeValue(LOGIN).equals(login)) {
				return elt;
			}
		}
		return null;
	}
	
	private void persistJobs() {
		try {
			writer.output(document, new FileOutputStream(xmlFile));
		} catch (FileNotFoundException e) {
			log.error("", e);
		} catch (IOException e) {
			log.error("", e);
		}
	}

	public Element getJobs() {
		return root;
	}
	
	public Map<String, List<String>> getJobsAsMap(String user) {
		
		
		boolean admin = UserManagerServiceImpl.adminLogin().equals(user);
		
		List<Element> userNodes = new ArrayList<Element>();
		Map<String, List<String>> res = new HashMap<String, List<String>>();
		
		if (!admin) {
			// Get every user in his group
			List<String> usersInGroups = UserManagerServiceImpl.getGroupUserFromLogin(user);
			for (String u : usersInGroups) {
				Element usr = findUser(u);
				if (usr != null)
					userNodes.add(usr);
			}
		} else {
			List<?> users = root.getChildren(USER);
			for (Object usr : users) {
				userNodes.add((Element) usr);
			}
		}
		
		for (Element usr : userNodes) {
			List<?> jobs = usr.getChildren(JOB);
			for (Object elt : jobs) {
				List<String> bks = new ArrayList<String>();
				List<?> banks = ((Element) elt).getChildren(BANK);
				for (Object bk : banks) {
					bks.add(((Element) bk).getAttributeValue(BANK_NAME));
				}
				Element e = (Element) elt;
				String key = e.getAttributeValue(JOB_NAME) + ":" + e.getAttributeValue(JOB_CRON);
				res.put(key, bks);
			}
		}
		
		return res;
	}

	public void addBanksToJob(String[] banks, String jobName) {
		for (String bankName : banks) {
			Element job = findJob(jobName);
			if (job != null) {
				Element bank = new Element(BANK);
				bank.setAttribute(BANK_NAME, bankName);
				job.addContent(bank);
			}
		}
		persistJobs();
	}

	public void createJob(String user, String label, String cron) {
		Element usr = findUser(user);
		
		if (usr == null) {
			usr = new Element(USER);
			usr.setAttribute(LOGIN, user);

			root.addContent(usr);
		}
		
		Element job = new Element(JOB);
		job.setAttribute(JOB_NAME, label);
		job.setAttribute(JOB_CRON, cron);
		
		usr.addContent(job);
		
		persistJobs();
		
	}

	public void deleteJob(String label) {
		Element job = findJob(label);
		if (job != null) {
			job.getParentElement().removeContent(job);
		}
		
		persistJobs();
	}

	public void removeBanksFromJob(String[] bankNames, String jobName) {
		for (String bankName : bankNames) {
			Element job = findJob(jobName);
			if (job != null) {
				List<?> banks = job.getChildren(BANK);
				for (Object bank : banks) {
					Element elt = (Element) bank;
					if (elt.getAttributeValue(BANK_NAME).equals(bankName)) {
						job.removeContent(elt);
						break;
					}
				}
			}
		}
		persistJobs();
	}

	public boolean renameJob(String oldName, String newName) {
		if (!jobExists(newName)) {
			Element job = findJob(oldName);
			if (job != null)
				job.setAttribute(JOB_NAME, newName);
			
			persistJobs();
			return true;
		}
		return false;
	}

	public void setCron(String name, String cron) {
		Element job = findJob(name);
		if (job != null) {
			job.setAttribute(JOB_CRON, cron);
		}
		persistJobs();
	}
	
	/**
	 * Browses every user and returns whether this job exists.
	 * 
	 * @param jobName
	 * @return
	 */
	public boolean jobExists(String jobName) {
		List<?> users = root.getChildren(USER);
		for (Object u : users) {
			Element user = (Element) u;
			List<?> jobs = user.getChildren(JOB);
			for (Object j : jobs) {
				if (((Element) j).getAttributeValue(JOB_NAME).equals(jobName))
					return true;
			}
		}
		
		return false;
	}
	
	public static String getJobsFileName() {
		return xmlFile;
	}
	
}
