package eu.dnetlib.data.utility.objectpackaging;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.io.SAXReader;

import eu.dnetlib.data.utility.objectpackaging.rmi.ObjectPackagingException;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

public class ObjectPackagesCollection {
	public static final int DEFAULTPAGESIZE = 10;
	private String collID;
	protected List<ObjectQueue> listQueues;
	protected List<String> historyList;
	private static CacheManager cacheManager = CacheManager.create();
	private Cache cache;
	private int numberOfComputedItems;
	private String lastID;

	protected ObjectPackagesCollection(List<ObjectQueue> list) throws ObjectPackagingException {
		
		if (list == null) throw new ObjectPackagingException("ObjectQueue List is null");
		if (list.size() == 0) throw new ObjectPackagingException("ObjectQueue List is empty");
		
		this.collID = "opc." + UUID.randomUUID();
		this.listQueues = list;
		this.numberOfComputedItems = 0;
		this.historyList = new ArrayList<String>();
		this.lastID = "";
		
		
		this.cache = new Cache(collID,   // name
				5000,	                  // maxElementsInMemory
				false,                    // overflowToDisk
				false,                    // eternal
				86400,                    // timeToLiveSeconds,
				86400);                   // timeToIdleSeconds
		cacheManager.addCache(cache);
		
		
		updateCache();
	}

	private void updateCache() {
		cache.put(new Element("queues", listQueues));
		cache.put(new Element("numberOfComputedItems", new Integer(numberOfComputedItems)));
		cache.put(new Element("historyList", historyList));
		cache.put(new Element("lastID", lastID));
	}

	@SuppressWarnings("unchecked")
	protected ObjectPackagesCollection(String collID) throws ObjectPackagingException {
		this.cache = cacheManager.getCache(collID);
		if (this.cache == null) throw new ObjectPackagingException("Invalid Collection Id: " + collID);
		
		
		Object queues = cache.get("queues").getObjectValue();
		if (!(queues instanceof List)) throw new ObjectPackagingException("Invalid cached element");
		this.listQueues = (List<ObjectQueue>) queues;
		
		Object noci  = cache.get("numberOfComputedItems").getObjectValue();
		if (!(noci instanceof Integer)) throw new ObjectPackagingException("Invalid cached element");
		this.numberOfComputedItems = (Integer) noci;
		
		Object hist = cache.get("historyList").getObjectValue();
		if (!(hist instanceof List)) throw new ObjectPackagingException("Invalid cached element");
		this.historyList = (List<String>) hist;

		Object lastID = cache.get("lastID").getObjectValue();
		if (!(lastID instanceof String)) throw new ObjectPackagingException("Invalid cached element");
		this.lastID = (String) lastID;

		
		this.collID = collID; 
	}

	public String getID() {
		return collID;
	}

	public List<String> calculateMorePackages(int size) throws DocumentException, ObjectPackagingException {
		List<String> response = new ArrayList<String>();

		String testID = null;
				 
		
		org.dom4j.Element item = DocumentHelper.createElement("objectRecord");
		
		String historyItem = "";
		
		while (response.size() < size) {
			if (areElementsInQueues()) {
				int idxQueue = findMinElementInQueues();
				String currID = listQueues.get(idxQueue).watchNextElementId();
				if (isOrdered(currID, lastID)) {
					throw new ObjectPackagingException("Queues are not ordered");
				} else {
					lastID = currID + "";
				}
				
				int posQueue = listQueues.get(idxQueue).getGivenItems() + 1; // +1 because ResultSets begin with 1  
				
				if (testID == null) {
					testID = currID;
					addToItem(item, listQueues.get(idxQueue).fetchNextElement());
					historyItem += "|" + idxQueue + "," + posQueue;
				} else if (testID.equals(currID)) {
					addToItem(item, listQueues.get(idxQueue).fetchNextElement());
					historyItem += "|" + idxQueue + "," + posQueue;
				} else {
					addItemToResponse(response, item, historyItem);
					testID = currID;
					item = DocumentHelper.createElement("objectRecord");
					historyItem = "";
				}
			} else {
				addItemToResponse(response, item, historyItem);
				break;
			}
		}
		
		updateCache();
		
		return response;
	}


	private void addItemToResponse(List<String> response, org.dom4j.Element item, String historyItem) {
		if (item.elements().size()  > 0) {
			response.add(item.asXML());
			if (historyItem != null) {
				historyList.add(historyItem + "|");
				this.numberOfComputedItems += 1;
			}
		}
	}

	private void addToItem(org.dom4j.Element itemRoot, String s) throws DocumentException {
		SAXReader reader = new SAXReader();
		Document child = reader.read(new StringReader(s));
		itemRoot.add(child.getRootElement());
	}

	protected int findMinElementInQueues() throws ObjectPackagingException {
		int ret = -1;
		String val = null;
		for (int i=0; i<listQueues.size(); i++) {
			if (val == null) {
				val = listQueues.get(i).watchNextElementId();
				ret = i;
			} else {
				String tmp = listQueues.get(i).watchNextElementId();
				if ((tmp != null)&&(isOrdered(tmp, val))) {
					val = tmp;
					ret = i;
				}
			}
		}
		return ret;
	}

	private boolean isOrdered(String val1, String val2) {
		return (val1.compareTo(val2) < 0);
	}

	protected boolean areElementsInQueues() {
		for (ObjectQueue q : listQueues) {
			if (!q.isEmpty()) return true;
		}
		return false;
	}

	public int getNumberOfComputedItems() {
		return numberOfComputedItems;
	}

	public List<String> retrieveOldPackages(int fromPosition, int toPosition) throws ObjectPackagingException, DocumentException {
		Map<Integer, Map<String,Integer>> intervals = new HashMap<Integer, Map<String,Integer>>();
		
		for (int i=fromPosition; i<=toPosition;i++) {
			String[] arr = historyList.get(i-1).split("\\|");
			for (int j=0; j<arr.length; j++) {
				String[] a = arr[j].split(",");
				if (a.length != 2) continue;
				int rs = Integer.parseInt(a[0]);
				int pos = Integer.parseInt(a[1]) -1 ;
				
				Map<String,Integer> value;
				if (!intervals.containsKey(rs)) {
					value = new HashMap<String, Integer>();
					value.put("from", pos);
				} else {
					value = intervals.get(rs);
				}
				value.put("to", pos);
				intervals.put(rs, value);
			}
		}
		
	
		
		Map<Integer, List<String>> mapList = new HashMap<Integer, List<String>>(); 
		for (Integer key : intervals.keySet()) {
			Map<String, Integer> value = intervals.get(key);
			mapList.put(key, listQueues.get(key).obtainOldItems(value.get("from"), value.get("to")));
		}
	
		List<String> res = new ArrayList<String>();
		for (int i=fromPosition; i<=toPosition;i++) {
			
			org.dom4j.Element item = DocumentHelper.createElement("objectRecord");
			String[] arr = historyList.get(i-1).split("\\|");
			for (int j=0; j<arr.length; j++) {
				String[] a = arr[j].split(",");
				if (a.length != 2) continue;
				int rs = Integer.parseInt(a[0]);
				int pos = Integer.parseInt(a[1]) - 1;
				addToItem(item, mapList.get(rs).get(pos - intervals.get(rs).get("from")));
			}
			addItemToResponse(res, item, null);
		}
		
		
		return res;
	}

	


}
