package eu.dnetlib.functionality.notification.dao;

import java.net.URL;
import java.util.Date;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

import eu.dnetlib.domain.functionality.NotificationEvent;
import eu.dnetlib.domain.functionality.NotificationQuery;
import eu.dnetlib.domain.functionality.NotificationResult;
import eu.dnetlib.domain.functionality.NotificationSchedule;
import eu.dnetlib.domain.functionality.NotificationSubscription;

/**
 * This bean implements notification DAO using sets to store queries and events in memory.
 * @author thanos@di.uoa.gr
 * @see NotificationDAO
 * @see eu.dnetlib.domain.functionality.NotificationQuery
 * @see eu.dnetlib.domain.functionality.NotificationEvent
 * @see NotificationDAOException
 *
 */
public class MockNotificationDAO implements NotificationDAO {
	private final SortedSet<NotificationQuery> queries;
	private final SortedSet<NotificationSchedule> schedules;
	private final SortedSet<NotificationEvent> events;
	private final SortedSet<NotificationResult> results;
	private final SortedSet<NotificationSubscription> subscriptions;
	
	public MockNotificationDAO() {
		queries = new TreeSet<NotificationQuery>();
		schedules = new TreeSet<NotificationSchedule>();
		events = new TreeSet<NotificationEvent>();
		results = new TreeSet<NotificationResult>();
		subscriptions = new TreeSet<NotificationSubscription>();
	}
	
	@Override
	public int countQueries() {
		return queries.size();
	}

	@Override
	public SortedSet<NotificationQuery> getQueries(final int limit, final int offset) {
		final SortedSet<NotificationQuery> result = new TreeSet<NotificationQuery>();
		int i = 0;
		for (NotificationQuery query : queries) {
			if (i < offset) // skip
				i++;
			else if (i < offset + limit) { // add to result
				result.add(query);
				i++;
			} else // done
				break;
		}
		return result;
	}

	@Override
	public NotificationQuery getQuery(final String queryId) {
		for (NotificationQuery query : queries) {
			if (query.getQueryId().equals(queryId))
				return query;
		}
		return null;
	}

	@Override
	public void saveQuery(final NotificationQuery query) {
		queries.add(query);
	}

	@Override
	public void deleteQuery(final String queryId) {
		for (Iterator<NotificationQuery> i = queries.iterator(); i.hasNext(); ) { // remove query
			if (i.next().getQueryId().equals(queryId)) {
				i.remove();
				break;
			}
		}
		for (Iterator<NotificationSchedule> i = schedules.iterator(); i.hasNext(); ) { // remove schedule
			if (i.next().getQueryId().equals(queryId)) {
				i.remove();
				break;
			}
		}
		for (Iterator<NotificationEvent> i = events.iterator(); i.hasNext(); ) { // remove events
			if (i.next().getQueryId().equals(queryId))
				i.remove();
		}
		for (Iterator<NotificationResult> i = results.iterator(); i.hasNext(); ) { // remove results
			if (i.next().getQueryId().equals(queryId))
				i.remove();
		}
		for (Iterator<NotificationSubscription> i = subscriptions.iterator(); i.hasNext(); ) { // remove subscriptions
			if (i.next().getQueryId().equals(queryId))
				i.remove();
		}
	}

	@Override
	public int countSchedules() {
		return schedules.size();
	}

	@Override
	public SortedSet<NotificationSchedule> getSchedules(final int limit, final int offset) {
		final SortedSet<NotificationSchedule> result = new TreeSet<NotificationSchedule>();
		int i = 0;
		for (NotificationSchedule schedule : schedules) {
			if (i < offset) // skip
				i++;
			else if (i < offset + limit) { // add to result
				result.add(schedule);
				i++;
			} else // done
				break;
		}
		return result;
	}

	@Override
	public SortedSet<NotificationSchedule> getEnabledSchedules(final int limit, final int offset) {
		final SortedSet<NotificationSchedule> result = new TreeSet<NotificationSchedule>();
		int i = 0;
		for (NotificationSchedule schedule : schedules) {
			if (i < offset) { // skip
				if (schedule.isEnabled())
					i++;
			} else if (i < offset + limit) { // add to result
				if (schedule.isEnabled()) {
					result.add(schedule);
					i++;
				}
			} else // done
				break;
		}
		return result;
	}

	@Override
	public NotificationSchedule getSchedule(final String queryId) {
		for (NotificationSchedule schedule : schedules) {
			if (schedule.getQueryId().equals(queryId))
				return schedule;
		}
		return null;
	}

	@Override
	public void saveSchedule(final NotificationSchedule schedule) throws NotificationDAOException {
		if (getQuery(schedule.getQueryId()) == null)
			throw new NotificationDAOException("error saving schedule " + schedule + ": query " + schedule.getQueryId() + " does not exist");
		schedules.add(schedule);
	}

	@Override
	public void deleteSchedule(final String queryId) {
		for (Iterator<NotificationSchedule> i = schedules.iterator(); i.hasNext(); ) {
			if (i.next().getQueryId().equals(queryId)) {
				i.remove();
				return;
			}
		}
	}

	@Override
	public int countEvents() {
		return events.size();
	}

	@Override
	public SortedSet<NotificationEvent> getEvents(final int limit, final int offset) {
		final SortedSet<NotificationEvent> result = new TreeSet<NotificationEvent>();
		int i = 0;
		for (NotificationEvent event : events) {
			if (i < offset) // skip
				i++;
			else if (i < offset + limit) { // add to result
				result.add(event);
				i++;
			} else // done
				break;
		}
		return result;
	}

	@Override
	public int countResults() {
		return results.size();
	}

	@Override
	public SortedSet<NotificationResult> getResults(final int limit, final int offset) {
		final SortedSet<NotificationResult> result = new TreeSet<NotificationResult>();
		int i = 0;
		for (NotificationResult r : results) {
			if (i < offset) // skip
				i++;
			else if (i < offset + limit) { // add to result
				result.add(r);
				i++;
			} else // done
				break;
		}
		return result;
	}

	@Override
	public NotificationResult getResult(final String queryId, final Date date, final String resultId) {
		for (NotificationResult result : results) {
			if (result.getQueryId().equals(queryId) && result.getDate().equals(date) && result.getResultId().equals(resultId))
				return result;
		}
		return null;
	}

	@Override
	public NotificationResult getPreviousResult(final String queryId, final Date date, final String resultId) {
		NotificationResult result = null;
		for (NotificationResult r : results) {
			if (r.getQueryId().equals(queryId) && r.getDate().before(date) && r.getResultId().equals(resultId)) // current result is earlier than the date specified
				result = r;
			else if (r.getQueryId().equals(queryId) && r.getResultId().equals(resultId)) // current result is not earlier that the date specified
				break;
		}
		return result;
	}

	@Override
	public NotificationResult getLastResult(final String queryId, final String resultId) {
		NotificationResult result = null;
		for (NotificationResult r : results) {
			if (r.getQueryId().equals(queryId) && r.getResultId().equals(resultId))
				result = r;
		}
		return result;
	}

	@Override
	public void saveResult(final NotificationResult result) throws NotificationDAOException {
		if (getQuery(result.getQueryId()) == null)
			throw new NotificationDAOException("error saving result " + result + ": query " + result.getQueryId() + " does not exist");
		events.add(new NotificationEvent(result.getQueryId(), result.getDate()));
		results.add(result);
	}

	@Override
	public int countSubscriptions() {
		return subscriptions.size();
	}

	@Override
	public SortedSet<NotificationSubscription> getSubscriptions(final int limit, final int offset) {
		final SortedSet<NotificationSubscription> result = new TreeSet<NotificationSubscription>();
		int i = 0;
		for (NotificationSubscription subscription : subscriptions) {
			if (i < offset) // skip
				i++;
			else if (i < offset + limit) { // add to result
				result.add(subscription);
				i++;
			} else // done
				break;
		}
		return result;
	}

	@Override
	public SortedSet<NotificationSubscription> getEnabledSubscriptions(final String queryId, final int limit, final int offset) {
		final SortedSet<NotificationSubscription> result = new TreeSet<NotificationSubscription>();
		int i = 0;
		for (NotificationSubscription subscription : subscriptions) {
			if (i < offset) { // skip
				if (subscription.isEnabled() && subscription.getQueryId().equals(queryId))
					i++;
			} else if (i < offset + limit) { // add to result
				if (subscription.getQueryId().equals(queryId) && subscription.isEnabled()) { 
					result.add(subscription);
					i++;
				}
			} else // done
				break;
		}
		return result;
	}

	@Override
	public NotificationSubscription getSubscription(final String queryId, final URL alertService) {
		for (NotificationSubscription subscription : subscriptions) {
			if (subscription.getQueryId().equals(queryId) && subscription.getAlertService().equals(alertService))
				return subscription;
		}
		return null;
	}

	@Override
	public void saveSubscription(final NotificationSubscription subscription) throws NotificationDAOException {
		if (getQuery(subscription.getQueryId()) == null)
			throw new NotificationDAOException("error saving subscription " + subscription + ": query " + subscription.getQueryId() + " does not exist");
		subscriptions.add(subscription);
	}

	@Override
	public void deleteSubscription(final String queryId, final URL alertService) {
		for (Iterator<NotificationSubscription> i = subscriptions.iterator(); i.hasNext(); ) {
			final NotificationSubscription subscription = i.next();
			if (subscription.getQueryId().equals(queryId) && subscription.getAlertService().equals(alertService)) {
				i.remove();
				return;
			}
		}
	}
}
