package com.hammurapi.store;

import java.util.Comparator;
import java.util.Iterator;
import java.util.concurrent.locks.Lock;

import com.hammurapi.common.Util;
import com.hammurapi.extract.CommutativeAnd;
import com.hammurapi.extract.Extractor;
import com.hammurapi.extract.Predicate;
import com.hammurapi.store.Index.Type;

public abstract class LiveView<T, PK, S extends Store<T,PK,S>> implements Store<T, PK, S> {
	
	protected S master;
	private Predicate<T, S> selector;

	public LiveView(S master, Predicate<T, S> selector) {
		this.master = master;
		this.selector = selector;
	}

	public Iterator<T> iterator() {
		return master.get(selector).iterator();
	}

	public Handle<T, PK, S> put(T obj, Predicate<T, S>... validators) {
		if (matches(obj)) {
			return master.put(obj, validators);			
		}
		throw new StoreException("Object does not match the view predicate.");
	}

	public Iterable<T> getAll() {
		return master.get(selector);
	}

	public int queryAll(QueryTask<T, PK, S> processor) {
		return master.query(selector, processor);
	}

	public T getByPrimaryKey(PK primaryKey) {		
		T ret = master.getByPrimaryKey(primaryKey);
		return matches(ret) ? ret : null;
	}

	@SuppressWarnings("unchecked")
	private Boolean matches(T ret) {
		return selector.extract((S) this, null, Util.wrap(ret));
	}

	public Iterable<T> get(Predicate<T, S> selector) {
		return master.get(and(selector));
	}

	@SuppressWarnings("unchecked")
	private Predicate<T, S> and(Predicate<T, S> selector) {
		return selector==null ? this.selector : new CommutativeAnd<T, S>(-1, null, selector, this.selector);
	}

	public int query(
			Predicate<T, S> selector,
			QueryTask<T, PK, S> processor) {
		
		return master.query(and(selector), processor);
	}

	public <V> int query(
			Predicate<T, S> selector,
			Extractor<T, V, S> extractor,
			QueryTask<V, PK, S> processor) {
		return master.query(and(selector), extractor, processor);
	}

	public <V> int queryMultiple(
			Predicate<T, S> selector,
			Extractor<T, Iterable<V>, S> extractor,
			Predicate<V, S> valueSelector,
			QueryTask<V, PK, S> processor) {
		
		return master.queryMultiple(
				and(selector), 
				extractor, 
				valueSelector,
				processor);
	}

	public Lock readLock() {
		return master.readLock();
	}

	public Lock writeLock() {
		return master.writeLock();
	}

	public <V> Iterable<V> get(
			Predicate<T, S> selector,
			Extractor<T, V, S> extractor, 
			boolean ordered,
			Comparator<V> comparator) {
		
		return master.get(
				and(selector), 
				extractor, 
				ordered, 
				comparator);
	}

	public <V> Iterable<V> getMultiple(
			Predicate<T, S> selector,
			Extractor<T, Iterable<V>, S> extractor,
			Predicate<V, S> valueSelector, 
			boolean ordered,
			Comparator<V> comparator) {
		
		return master.getMultiple(
				and(selector), 
				extractor, 
				valueSelector, 
				ordered,
				comparator);
	}

	public void clear() {
		master.remove(selector);
	}

	public boolean remove(T obj) {
		return matches(obj) ? master.remove(obj) : false;
	}

	public boolean removeByPrimaryKey(PK primaryKey) {
		T obj = master.getByPrimaryKey(primaryKey);
		if (obj==null) {
			return false;
		}
		if (matches(obj)) {
			return master.removeByPrimaryKey(primaryKey);
		}
		return false;
	}

	public int remove(Predicate<T, S> selector) {
		return master.remove(and(selector));
	}

	public int update(
			Predicate<T, S> selector,
			UpdateTask<T, PK, S> updater) {
		
		return master.update(and(selector), updater);
	}

	public <V, ST extends T> Index<T, ST, PK, V, S> addIndex(
			Predicate<T, S> predicate,
			Extractor<ST, V, S> extractor, 
			Type type,
			boolean ordered, Comparator<V> comparator) {
		
		return master.addIndex(
				and(predicate), 
				extractor, 
				type, 
				ordered, 
				comparator);
	}

	public Extractor<T, PK, S> getPrimaryKeyExtractor() {
		return master.getPrimaryKeyExtractor();
	}

	public S createView(
			Predicate<T, S> selector,
			ViewType viewType) {
		
		return master.createView(and(selector), viewType);
	}

	protected S getMaster() {
		return master;
	}
		
}
