package eu.dnetlib.data.utility.objectpackaging;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

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

import org.dom4j.DocumentException;

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

public class ObjectPackagesSplitter extends ObjectPackagingItems {

	public static final String SPLIT_PREFIX = "split.";
	public static final int DEFAULTPAGESIZE = 10;
	private String splitterId;
	private PackageQueue queue;
	private int numberOfComputedItems;
	private int numberOfComputedPackages;
	private int numberOfComputedElemsInPackage;
	private List<String> historyList;
	private List<String> lastListObjects;
	private Cache cache;
	private static CacheManager cacheManager = CacheManager.create();

	public ObjectPackagesSplitter(PackageQueue queue) throws ObjectPackagingException {
		if (queue == null) {
			throw new ObjectPackagingException("ObjectQueue is null");
		}

		this.splitterId = SPLIT_PREFIX + UUID.randomUUID();
		this.queue = queue;
		this.numberOfComputedItems = 0;
		this.numberOfComputedPackages = 0;
		this.numberOfComputedElemsInPackage = -1;
		this.historyList = new ArrayList<String>();
		this.lastListObjects = new ArrayList<String>();

		this.cache = new Cache(splitterId, // name
				EHCACHE_MAX_ELEMENT_IN_MEMORY, // maxElementsInMemory
				false, // overflowToDisk
				false, // eternal
				EHCACHE_TTL, // timeToLiveSeconds,
				EHCACHE_TTL); // timeToIdleSeconds
		cacheManager.addCache(cache);

		updateCache();
	}

	private void updateCache() {
		cache.put(new Element("queue", queue));
		cache.put(new Element("numberOfComputedItems", Integer.valueOf(numberOfComputedItems)));
		cache.put(new Element("numberOfComputedPackages", Integer.valueOf(numberOfComputedPackages)));
		cache.put(new Element("numberOfComputedElemsInPackage", Integer.valueOf(numberOfComputedElemsInPackage)));
		cache.put(new Element("historyList", historyList));
		cache.put(new Element("lastListObjects", lastListObjects));
	}

	public ObjectPackagesSplitter(String id) throws ObjectPackagingException {
		this.cache = cacheManager.getCache(id);
		if (this.cache == null) {
			throw new ObjectPackagingException("Invalid splitter Id: " + id);
		}

		this.queue = getTypedValueFromCache("queue", PackageQueue.class);
		this.numberOfComputedItems = getTypedValueFromCache("numberOfComputedItems", Integer.class);
		this.numberOfComputedPackages = getTypedValueFromCache("numberOfComputedPackages", Integer.class);
		this.numberOfComputedElemsInPackage = getTypedValueFromCache("numberOfComputedElemsInPackage", Integer.class);

		this.historyList = getStringListFromCache("historyList");
		this.lastListObjects = getStringListFromCache("lastListObjects");

		this.splitterId = id;
	}

	@SuppressWarnings("unchecked")
	private <T> T getTypedValueFromCache(String name, Class<T> class1) throws ObjectPackagingException {
		Object res = cache.get(name).getObjectValue();
		if (!class1.isInstance(res)) {
			throw new ObjectPackagingException("Invalid cached element");
		}
		return (T) res;
	}

	@SuppressWarnings("unchecked")
	protected final List<String> getStringListFromCache(String name) throws ObjectPackagingException {
		Object res = cache.get(name).getObjectValue();
		if (!(res instanceof List<?>)) {
			throw new ObjectPackagingException("Invalid cached element");
		}
		return (List<String>) res;
	}

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

		while ((response.size() < size) && ((!lastListObjects.isEmpty()) || (!queue.isEmpty()))) {
			if (lastListObjects.isEmpty()) {
				lastListObjects = queue.fetchNextListObject();
				numberOfComputedPackages += 1;
				numberOfComputedElemsInPackage = -1;
			} else {
				String elem = lastListObjects.remove(0);
				response.add(elem);
				numberOfComputedItems += 1;
				numberOfComputedElemsInPackage += 1;
				historyList.add(numberOfComputedPackages + "," + numberOfComputedElemsInPackage);
			}
		}
		updateCache();

		return response;
	}

	@Override
	public List<String> retrieveOldItems(int from, int to) throws DocumentException, ObjectPackagingException {
		String fst = historyList.get(from - 1);
		String lst = historyList.get(to - 1);

		String[] arr1 = fst.split(",");
		String[] arr2 = lst.split(",");

		if ((arr1.length != 2) || (arr2.length != 2)) {
			throw new ObjectPackagingException("Invalid history items");
		}

		int fromP = Integer.parseInt(arr1[0]);
		int toP = Integer.parseInt(arr2[0]);
		int pos1 = Integer.parseInt(arr1[1]);
		int pos2 = Integer.parseInt(arr2[1]);

		List<List<String>> elems = queue.obtainOldListObjects(fromP, toP);
		List<String> response = new ArrayList<String>();

		int length = elems.size();

		if (length == 0) {
			throw new ObjectPackagingException("No packages in history");
		} else if (length == 1) {
			response.addAll(elems.get(0).subList(pos1, pos2 + 1));
		} else {
			for (int i = 0; i < length; i++) {
				List<String> list = elems.get(i);
				if (i == 0) {
					response.addAll(list.subList(pos1, list.size()));
				} else if (i == length - 1) {
					response.addAll(list.subList(0, pos2 + 1));
				} else {
					response.addAll(list);
				}
			}
		}
		return response;
	}

	public String getID() {
		return splitterId;
	}

	@Override
	public int getNumberOfComputedItems() {
		return numberOfComputedItems;
	}

	@Override
	public boolean isComputationItemsEnded() {
		return queue.isEmpty() && lastListObjects.isEmpty();
	}

	public List<String> getHistoryList() {
		return historyList;
	}

}
