package eu.dnetlib.server;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import eu.dnetlib.client.shared.Data;
import eu.dnetlib.efg1914.authoring.components.Frame;
import eu.dnetlib.efg1914.authoring.components.Item;
import eu.dnetlib.efg1914.authoring.components.Theme;
import eu.dnetlib.efg1914.authoring.components.Topic;

public class ComponentsRegistry implements Serializable {
	/**
	 * 
	 */

	private static Logger log = Logger.getLogger("ComponentsRegistry.java");
	private static final long serialVersionUID = 1L;
	// maximum time between cache clear-ups for components
	// set to milliseconds-> 600000 = 10 minutes
	private double MAX_UPDATE_WAITING_TIME = 360000000;
	// maximum time a marked component can stay active- set to 15 minutes
	// done to prevent infinite locking of items
	private double MAX_ACTIVE_WAITING_TIME = 360000000;

	private ConcurrentHashMap<Date, Item> newItems;
	private ConcurrentHashMap<Date, Item> updatedItems;

	private ConcurrentHashMap<Date, String> deletedItems;

	private ConcurrentHashMap<Date, Frame> newFrames;
	private ConcurrentHashMap<Date, String> deletedFrames;

	private ConcurrentHashMap<String, Date> registeredUsers;
	private Date updateNumber;

	private ConcurrentHashMap<Date, Frame> updatedFrames;

	private ConcurrentHashMap<Date, Topic> newTopics;

	private ConcurrentHashMap<Date, Theme> newThemes;

	private ConcurrentHashMap<Date, String> deletedTopics;

	private ConcurrentHashMap<Date, Topic> updatedTopics;

	private ConcurrentHashMap<Date, String> deletedThemes;

	private ConcurrentHashMap<Date, Theme> updatedThemes;

	private ConcurrentHashMap<String, Date> activeComponentsLog;

	private ConcurrentHashMap<String, String> activeComponents;

	public ComponentsRegistry() {
		Date date = new Date();
		updateNumber = new Timestamp(date.getTime());
		this.deletedItems = new ConcurrentHashMap<Date, String>();
		this.newItems = new ConcurrentHashMap<Date, Item>();
		this.updatedItems = new ConcurrentHashMap<Date, Item>();

		this.deletedFrames = new ConcurrentHashMap<Date, String>();
		this.newFrames = new ConcurrentHashMap<Date, Frame>();
		this.updatedFrames = new ConcurrentHashMap<Date, Frame>();

		this.deletedTopics = new ConcurrentHashMap<Date, String>();
		this.updatedTopics = new ConcurrentHashMap<Date, Topic>();
		this.newTopics = new ConcurrentHashMap<Date, Topic>();

		this.updatedThemes = new ConcurrentHashMap<Date, Theme>();
		this.deletedThemes = new ConcurrentHashMap<Date, String>();
		this.newThemes = new ConcurrentHashMap<Date, Theme>();

		this.registeredUsers = new ConcurrentHashMap<String, Date>();
		this.activeComponents = new ConcurrentHashMap<String, String>();
		this.activeComponentsLog = new ConcurrentHashMap<String, Date>();

	}

	// ITEMS
	//
	public void addItem(Item it) {

		Date date = new Date();
		if (it != null) {
			Timestamp t = new Timestamp(date.getTime());
			log.info("\n\n+++++++++++++++++ added  new item at " + t);
			this.newItems.put(t, it);
		}
	}

	public void addDeletedItem(String id) {

		Date date = new Date();
		if (id != null) {
			log.info("\n\n+++++++++++++++++ added  deleted item at " + id);
			this.deletedItems.put(new Timestamp(date.getTime()), id);

			unmarkComponent(id, null);

		}
	}

	public void addUpdatedItem(Item it) {

		Date date = new Date();
		if (it != null) {
			log.info("\n\n+++++++++++++++++ added  update item at " + it.toString());
			this.updatedItems.put(new Timestamp(date.getTime()), it);

		}
	}

	// FRAMES
	//
	public void addFrame(Frame it) {
		Date date = new Date();
		if (it != null) {
			this.newFrames.put(new Timestamp(date.getTime()), it);
		}
	}

	public void addUpdatedFrame(Frame it) {
		Date date = new Date();
		if (it != null) {
			this.updatedFrames.put(new Timestamp(date.getTime()), it);
		}
	}

	public void addDeletedFrame(String id) {
		Date date = new Date();

		if (id != null) {
			this.deletedFrames.put(new Timestamp(date.getTime()), id);
			unmarkComponent(id, null);
		}
	}

	// TOPICS
	//
	public void addTopic(Topic it) {
		Date date = new Date();
		if (it != null) {
			this.newTopics.put(new Timestamp(date.getTime()), it);
		}
	}

	public void addDeletedTopic(String id) {
		Date date = new Date();
		if (id != null) {
			this.deletedTopics.put(new Timestamp(date.getTime()), id);
			unmarkComponent(id, null);
		}
	}

	public void addUpdatedTopic(Topic it) {

		Date date = new Date();
		if (it != null) {
			this.updatedTopics.put(new Timestamp(date.getTime()), it);
		}
	}

	// THEMES

	public void addTheme(Theme it) {
		Date date = new Date();
		if (it != null) {
			this.newThemes.put(new Timestamp(date.getTime()), it);
		}
	}

	public void addDeletedTheme(String id) {
		Date date = new Date();
		if (id != null) {
			this.deletedThemes.put(new Timestamp(date.getTime()), id);
			unmarkComponent(id, null);
		}
	}

	public void addUpdatedTheme(Theme it) {
		Date date = new Date();
		if (it != null) {
			this.updatedThemes.put(new Timestamp(date.getTime()), it);
		}
	}

	public void destroy() {
		this.deletedItems.clear();
		this.newItems.clear();

		this.updatedItems.clear();

		this.deletedFrames.clear();
		this.newFrames.clear();
		this.updatedFrames.clear();

		this.deletedTopics.clear();
		this.updatedTopics.clear();
		this.newTopics.clear();

		this.updatedThemes.clear();
		this.deletedThemes.clear();
		this.newThemes.clear();
		this.activeComponents.clear();
	}

	public void markComponent(String componentId, String userId) {
		if (componentId != null) {
			this.getActiveComponents().put(componentId, userId);
			Date date = new Date();
			this.activeComponentsLog.put(componentId, new Timestamp(date.getTime()));
		}
	}

	public void unmarkComponent(String componentId, String userId) {
		if (componentId != null && !this.getActiveComponents().isEmpty() && this.getActiveComponents().containsKey(componentId)) {

			this.getActiveComponents().remove(componentId);

			this.getActiveComponentsLog().remove(componentId);

		}
	}

	public void clearUpCache() {
		// int sec = 600;
		//
		// Timestamp later = new Timestamp(retry_date.getTime() + sec * 1000);
		//
		// or if you want relative to "now":
		//
		// Timestamp later = new Timestamp(System.currentTimeMillis() + sec *
		// 1000);

		Timestamp updateThreshold = new Timestamp((long) (System.currentTimeMillis() - 360000 * 1000));

		// Timestamp activeComponentsThreshold = new Timestamp((long)
		// (System.currentTimeMillis() - MAX_ACTIVE_WAITING_TIME * 1000));
		Timestamp activeComponentsThreshold = new Timestamp((long) (System.currentTimeMillis() - 360000 * 1000));

		// log.info("\n\n thresholds : " + activeComponentsThreshold + "/// " +
		// updateThreshold);
		// synchronized (newItems)
		{
			Iterator<Entry<Date, Item>> iterator = this.newItems.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Item> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.newItems.remove(entry.getKey());
					log.info("clearing up  items cache ....");
				}

			}

		}
		// synchronized (newFrames)
		{
			Iterator<Entry<Date, Frame>> iterator = this.newFrames.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Frame> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.newFrames.remove(entry.getKey());
					log.info("clearing up  frames cache ....");

				}

			}
		}

		// synchronized (newTopics)
		{
			Iterator<Entry<Date, Topic>> iterator = this.newTopics.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Topic> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.newTopics.remove(entry.getKey());
					log.info("clearing up  topics cache ....");
				}
			}
		}

		// synchronized (newThemes)
		{
			Iterator<Entry<Date, Theme>> iterator = this.newThemes.entrySet().iterator();

			while (iterator.hasNext()) {
				Entry<Date, Theme> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.newThemes.remove(entry.getKey());
					log.info("clearing up  themes cache ....");
				}
			}

		}
		// synchronized (deletedItems)
		{
			Iterator<Entry<Date, String>> iterator = this.deletedItems.entrySet().iterator();

			while (iterator.hasNext()) {

				Entry<Date, String> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.deletedItems.remove(entry.getKey());
					log.info("clearing up  deleted items cache ....");

				}

			}
		}
		// synchronized (deletedFrames)
		{
			Iterator<Entry<Date, String>> iteratorS = this.deletedFrames.entrySet().iterator();
			while (iteratorS.hasNext()) {

				Entry<Date, String> entry = iteratorS.next();
				if (entry.getKey().before(updateThreshold)) {

					this.deletedFrames.remove(entry.getKey());
					log.info("clearing up  deleted frames cache ....");

				}
			}
		}

		// synchronized (deletedTopics)
		{
			Iterator<Entry<Date, String>> iteratorS = this.deletedTopics.entrySet().iterator();
			while (iteratorS.hasNext()) {
				Entry<Date, String> entry = iteratorS.next();
				if (entry.getKey().before(updateThreshold)) {

					this.deletedTopics.remove(entry.getKey());
					log.info("clearing up  deleted topics cache ....");
				}

			}
		}

		// synchronized (deletedThemes)
		{

			Iterator<Entry<Date, String>> iterator = this.deletedThemes.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, String> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.deletedThemes.remove(entry.getKey());
					log.info("clearing up  deleted themes cache ....");
				}
			}
		}

		// synchronized (updatedItems)
		{
			Iterator<Entry<Date, Item>> iterator = this.updatedItems.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Item> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.updatedItems.remove(entry.getKey());
					log.info("clearing up  updated items cache ....");
				}

			}
		}
		// synchronized (updatedFrames)
		{
			Iterator<Entry<Date, Frame>> iterator = this.updatedFrames.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Frame> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.updatedFrames.remove(entry.getKey());
					log.info("clearing up  updated frames cache ....");
				}

			}
		}
		// synchronized (updatedTopics)
		{
			Iterator<Entry<Date, Topic>> iterator = this.updatedTopics.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Topic> entry = iterator.next();
				if (entry.getKey().before(updateThreshold)) {

					this.updatedTopics.remove(entry.getKey());
					log.info("clearing up  updated topics cache ....");
				}

			}
		}
		// synchronized (updatedThemes)
		{

			Iterator<Entry<Date, Theme>> iterator = this.updatedThemes.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Theme> entry = iterator.next();

				if (entry.getKey().before(updateThreshold)) {

					this.updatedThemes.remove(entry.getKey());
					log.info("clearing up  updated themes cache ....");
				}

			}
		}
		// synchronized (activeComponentsLog)
		{
			Iterator<Entry<String, Date>> iteratorA = this.activeComponentsLog.entrySet().iterator();
			while (iteratorA.hasNext()) {
				Entry<String, Date> entry = iteratorA.next();

				if (entry.getValue().before(activeComponentsThreshold)) {
					log.info("clearing active....");

					this.activeComponents.remove(entry.getKey());

					this.activeComponentsLog.remove(entry.getKey());
				}
			}

		}
	}

	public Data receiveUpdates(String user) {
		Data data = new Data();
		Date lastUpdateTime = this.registeredUsers.get(user);

		Date newLastUpdateTime = lastUpdateTime;

		if (!this.newItems.isEmpty()) {
			Iterator<Entry<Date, Item>> iterator = this.newItems.entrySet().iterator();
			while (iterator.hasNext()) {

				Entry<Date, Item> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime)) {
					data.getNewItems().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}

				}

			}
		}
		if (!this.newFrames.isEmpty()) {
			Iterator<Entry<Date, Frame>> iterator = this.newFrames.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Frame> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{
					data.getNewFrames().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}

				}

			}
		}

		if (!this.newTopics.isEmpty()) {
			Iterator<Entry<Date, Topic>> iterator = this.newTopics.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Topic> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{

					data.getNewTopics().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}

			}

		}

		if (!this.newThemes.isEmpty()) {

			Iterator<Entry<Date, Theme>> iterator = this.newThemes.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Theme> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{

					data.getNewThemes().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}

			}

		}

		if (!this.deletedItems.isEmpty()) {

			Iterator<Entry<Date, String>> iterator = this.deletedItems.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, String> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime)) {
					data.getDeletedItems().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}
		}

		if (!this.deletedFrames.isEmpty()) {
			Iterator<Entry<Date, String>> iterator = this.deletedFrames.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, String> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime)) {

					data.getDeletedFrames().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}
		}

		if (!this.deletedTopics.isEmpty()) {

			Iterator<Entry<Date, String>> iterator = this.deletedTopics.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, String> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{

					data.getDeletedTopics().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}
		}

		if (!this.deletedThemes.isEmpty()) {
			Iterator<Entry<Date, String>> iterator = this.deletedTopics.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, String> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{

					data.getDeletedThemes().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}
		}

		if (!this.updatedItems.isEmpty()) {
			Iterator<Entry<Date, Item>> iterator = this.updatedItems.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Item> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{
					data.getUpdatedItems().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}

		}
		if (!this.updatedFrames.isEmpty()) {
			Iterator<Entry<Date, Frame>> iterator = this.updatedFrames.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Frame> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{

					data.getUpdatedFrames().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}
		}

		if (!this.updatedTopics.isEmpty()) {

			Iterator<Entry<Date, Topic>> iterator = this.updatedTopics.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Topic> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime))

				{

					data.getUpdatedTopics().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}
		}

		if (!this.updatedThemes.isEmpty()) {
			Iterator<Entry<Date, Theme>> iterator = this.updatedThemes.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<Date, Theme> entry = iterator.next();
				if (entry.getKey().after(lastUpdateTime)) {
					data.getUpdatedThemes().add(entry.getValue());
					if (entry.getKey().after(newLastUpdateTime)) {
						newLastUpdateTime = entry.getKey();
					}
				}
			}
		}
		if (this.activeComponents != null && !this.activeComponents.isEmpty()) {
			data.getActiveComponents().putAll(this.activeComponents);

		}

		this.registeredUsers.put(user, newLastUpdateTime);

		return data;
	}

	public void registerUser(String user) {
		if (user != null) {
			Date date = new Date();
			this.registeredUsers.put(user, new Timestamp(date.getTime()));
		}
	}

	public void removeUser(String user) {
		if (user != null) {

			this.registeredUsers.remove(user);

			Iterator<Entry<String, String>> iterator = this.activeComponents.entrySet().iterator();
			while (iterator.hasNext()) {
				Entry<String, String> e = iterator.next();
				if (e.getValue().equals(user)) {

					this.activeComponents.remove(e.getKey());

					this.activeComponentsLog.remove(e.getKey());

				}
			}
		}
	}

	public static Logger getLog() {
		return log;
	}

	public static void setLog(Logger log) {
		ComponentsRegistry.log = log;
	}

	public ConcurrentHashMap<Date, Item> getNewItems() {
		return newItems;
	}

	public void setNewItems(ConcurrentHashMap<Date, Item> newItems) {
		this.newItems = newItems;
	}

	public ConcurrentHashMap<Date, Item> getUpdatedItems() {
		return updatedItems;
	}

	public void setUpdatedItems(ConcurrentHashMap<Date, Item> updatedItems) {
		this.updatedItems = updatedItems;
	}

	public ConcurrentHashMap<Date, String> getDeletedItems() {
		return deletedItems;
	}

	public void setDeletedItems(ConcurrentHashMap<Date, String> deletedItems) {
		this.deletedItems = deletedItems;
	}

	public ConcurrentHashMap<Date, Frame> getNewFrames() {
		return newFrames;
	}

	public void setNewFrames(ConcurrentHashMap<Date, Frame> newFrames) {
		this.newFrames = newFrames;
	}

	public ConcurrentHashMap<Date, String> getDeletedFrames() {
		return deletedFrames;
	}

	public void setDeletedFrames(ConcurrentHashMap<Date, String> deletedFrames) {
		this.deletedFrames = deletedFrames;
	}

	public ConcurrentHashMap<String, Date> getRegisteredUsers() {
		return registeredUsers;
	}

	public void setRegisteredUsers(ConcurrentHashMap<String, Date> registeredUsers) {
		this.registeredUsers = registeredUsers;
	}

	public Date getUpdateNumber() {
		return updateNumber;
	}

	public void setUpdateNumber(Date updateNumber) {
		this.updateNumber = updateNumber;
	}

	public ConcurrentHashMap<Date, Frame> getUpdatedFrames() {
		return updatedFrames;
	}

	public void setUpdatedFrames(ConcurrentHashMap<Date, Frame> updatedFrames) {
		this.updatedFrames = updatedFrames;
	}

	public ConcurrentHashMap<Date, Topic> getNewTopics() {
		return newTopics;
	}

	public void setNewTopics(ConcurrentHashMap<Date, Topic> newTopics) {
		this.newTopics = newTopics;
	}

	public ConcurrentHashMap<Date, Theme> getNewThemes() {
		return newThemes;
	}

	public void setNewThemes(ConcurrentHashMap<Date, Theme> newThemes) {
		this.newThemes = newThemes;
	}

	public ConcurrentHashMap<Date, String> getDeletedTopics() {
		return deletedTopics;
	}

	public void setDeletedTopics(ConcurrentHashMap<Date, String> deletedTopics) {
		this.deletedTopics = deletedTopics;
	}

	public ConcurrentHashMap<Date, Topic> getUpdatedTopics() {
		return updatedTopics;
	}

	public void setUpdatedTopics(ConcurrentHashMap<Date, Topic> updatedTopics) {
		this.updatedTopics = updatedTopics;
	}

	public ConcurrentHashMap<Date, String> getDeletedThemes() {
		return deletedThemes;
	}

	public void setDeletedThemes(ConcurrentHashMap<Date, String> deletedThemes) {
		this.deletedThemes = deletedThemes;
	}

	public ConcurrentHashMap<Date, Theme> getUpdatedThemes() {
		return updatedThemes;
	}

	public void setUpdatedThemes(ConcurrentHashMap<Date, Theme> updatedThemes) {
		this.updatedThemes = updatedThemes;
	}

	public ConcurrentHashMap<String, Date> getActiveComponentsLog() {
		return activeComponentsLog;
	}

	public void setActiveComponentsLog(ConcurrentHashMap<String, Date> activeComponentsLog) {
		this.activeComponentsLog = activeComponentsLog;
	}

	public ConcurrentHashMap<String, String> getActiveComponents() {
		return activeComponents;
	}

	public void setActiveComponents(ConcurrentHashMap<String, String> activeComponents) {
		this.activeComponents = activeComponents;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	public double getMAX_UPDATE_WAITING_TIME() {
		return MAX_UPDATE_WAITING_TIME;
	}

	public void setMAX_UPDATE_WAITING_TIME(double mAX_UPDATE_WAITING_TIME) {
		MAX_UPDATE_WAITING_TIME = mAX_UPDATE_WAITING_TIME;

	}

	public double getMAX_ACTIVE_WAITING_TIME() {
		return MAX_ACTIVE_WAITING_TIME;
	}

	public void setMAX_ACTIVE_WAITING_TIME(double activeComponentsClearUp) {
		MAX_ACTIVE_WAITING_TIME = activeComponentsClearUp;
	}

}
