package com.hammurapi.common.concurrent;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;

import com.hammurapi.common.Context;
import com.hammurapi.convert.Converter;
import com.hammurapi.convert.ConvertingService;

/**
 * 
 * @author Pavel Vlasov
 *
 * @param <KP> Key path
 * @param <KE> Key path element
 */
public abstract class AbstractPropertySet<KP,KE> implements PropertySet<KP>, ReadWriteLock {
	
	protected Converter converter;
	protected ExecutorService executorService;
	protected PropertySet<KP>[] shadows;
//	protected ClassLoader classLoader;
	protected Context context;
	
	private PropertySet<KP> shadowPropertySet = new PropertySet<KP>() {

		@Override
		public <T> T getAdapter(Class<T> type, Context context) {
			throw new UnsupportedOperationException(); // Maybe implement.
		}

		@Override
		public Set<KP> keySet() {
			HashSet<KP> ret = new HashSet<KP>();
			
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					ret.addAll(ch.keySet());
				}
			}
			return ret;
		}

		@Override
		public PropertySet<KP> subset(KP prefix) {
			return new AbstractSubSet<KP>(this, prefix) {

				@Override
				protected KP buildPath(KP prefix, KP key) {
					return AbstractPropertySet.this.buildPath(prefix, key);
				}
				
			};
		}

		@Override
		public void remove(KP key) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void clear() {
			throw new UnsupportedOperationException();
		}

		@Override
		public void mount(KP prefix, PropertySet<KP> source) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void unmount(KP prefix) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void setAll(PropertySet<KP> source) {
			throw new UnsupportedOperationException();
		}

		@Override
		public Object get(KP key) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					Object ret = ch.get(key);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return null;
		}

		@Override
		public Object get(KP key, Object defaultValue) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					Object ret = ch.get(key);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return defaultValue;
		}

		@Override
		public <T> T get(KP key, Class<T> type) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					T ret = ch.get(key, type);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return null;
		}

		@Override
		public <T> T get(KP key, Class<T> type, T defaultValue) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					T ret = ch.get(key, type);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return defaultValue;
		}

		@Override
		public Object get(KP key, long timeout, TimeUnit unit) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					Object ret = ch.get(key, timeout, unit);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return null;
		}

		@Override
		public Object get(KP key, Object defaultValue, long timeout, TimeUnit unit) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					Object ret = ch.get(key, timeout, unit);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return defaultValue;
		}

		@Override
		public <T> T get(KP key, Class<T> type, long timeout, TimeUnit unit) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					T ret = ch.get(key, type, timeout, unit);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return null;
		}

		@Override
		public <T> T get(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
			for (PropertySet<KP> ch: shadows) {
				if (ch!=null) {
					T ret = ch.get(key, type, timeout, unit);
					if (ret!=null) {
						return ret;
					}
				}
			}
			return defaultValue;
		}

		@Override
		public void set(KP key, Object value) {
			throw new UnsupportedOperationException();
			
		}

		@Override
		public void setInvocable(KP key, Invocable<PropertySet<KP>, ?, KP, ?> invocable) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void cook(KP key, Callable<?> callable) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void cook(KP key, Callable<?> callable, long delay, TimeUnit timeUnit) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void cooking(KP key, Future<?> future) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void lazy(KP key, Callable<?> callable) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void cancel(KP key) {
			throw new UnsupportedOperationException();
		}

		@Override
		public void cancelAll() {
			throw new UnsupportedOperationException();
		}

		@Override
		public Object invoke(KP key, Object... args) {
			throw new UnsupportedOperationException();
		}

		@Override
		public Object shadowInvoke(KP key, Object... args) {
			throw new UnsupportedOperationException();
		}

		@Override
		public Object shadowGet(KP key) {
			throw new UnsupportedOperationException();
		}

		@Override
		public Object shadowGet(KP key, Object defaultValue) {
			throw new UnsupportedOperationException();
		}

		@Override
		public <T> T shadowGet(KP key, Class<T> type) {
			throw new UnsupportedOperationException();
		}

		@Override
		public <T> T shadowGet(KP key, Class<T> type, T defaultValue) {
			throw new UnsupportedOperationException();
		}
		

		@Override
		public Object shadowGet(KP key, long timeout, TimeUnit unit) {
			throw new UnsupportedOperationException();
		}

		@Override
		public Object shadowGet(KP key, Object defaultValue, long timeout, TimeUnit unit) {
			throw new UnsupportedOperationException();
		}

		@Override
		public <T> T shadowGet(KP key, Class<T> type, long timeout, TimeUnit unit) {
			throw new UnsupportedOperationException();
		}

		@Override
		public <T> T shadowGet(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
			throw new UnsupportedOperationException();
		}

		@Override
		public PropertySet<KP> createVersion() {
			throw new UnsupportedOperationException();
		}
		
	};
	
	/**
	 * @return Store for values.
	 */
	protected abstract Map<KE, Object> getValueStore();
	
	/**
	 * 
	 * @return
	 */
	protected abstract Map<KE, AbstractPropertySet<KP,KE>> getSubSetStore();
	
	/**
	 * Creates a property set which shares lock and chain with this one.
	 * @return
	 */
	protected abstract AbstractPropertySet<KP,KE> newInstance(KE key);
	
	protected abstract Map<KE, PropertySet<KP>> getMounts();
	
	protected AbstractPropertySet(
			ExecutorService executorService, 
			Converter converter,
//			ClassLoader classLoader,
			Context context,
			PropertySet<KP>... shadows) {
		this.executorService = executorService;
		this.converter = converter==null ? ConvertingService.CONVERTER : converter;
//		this.classLoader = classLoader==null ? getClass().getClassLoader() : classLoader;
		this.context = context;
		this.shadows = shadows;		
	}
	
	protected abstract KP buildPath(int startIdx, Object... elements);

	protected KP buildPath(Object... elements) {
		return buildPath(0, elements);
	}
	
	protected abstract KP buildPath(KP prefix, Object... elements);
	
	protected abstract KP buildPath(KE prefix, KP suffix);
	
	protected abstract KE[] tokenize(KP path);
	
	/**
	 * Versions object for forward-only change propagation purposes.
	 * @param <T>
	 * @param source
	 * @return
	 */
	protected abstract <T> T version(T source);
	
	@Override
	public Set<KP> keySet() {
		readLock().lock();
		HashSet<KP> ret = new HashSet<KP>();
		try {
			for (KE key: getValueStore().keySet()) {
				ret.add(buildPath(key));
			}
			
			for (Map.Entry<KE, PropertySet<KP>> entry: getMounts().entrySet()) {
				for (KP path: entry.getValue().keySet()) {
					ret.add(buildPath(entry.getKey(), path));
				}
			}
			
			for (Map.Entry<KE, AbstractPropertySet<KP, KE>> entry: getSubSetStore().entrySet()) {
				if (!getMounts().containsKey(entry.getKey())) { // If not shadowed
					for (KP path: entry.getValue().keySet()) {
						ret.add(buildPath(entry.getKey(), path));
					}
				}
			}			
		} finally {
			readLock().unlock();
		}
		
		ret.addAll(shadowPropertySet.keySet());
		return ret;
	}

	@Override
	public PropertySet<KP> subset(KP prefix) {
		writeLock().lock();
		try {
			return subset(tokenize(prefix),0,true);
		} finally {
			writeLock().unlock();
		}
	}

	protected PropertySet<KP> subset(KE[] prefix, int idx, boolean create) {
		PropertySet<KP> mount = getMounts().get(prefix[idx]);
		if (mount!=null) {
			if (prefix.length==idx+1) {
				return mount;
			}
			
			return mount.subset(buildPath(idx+1, prefix));
		}
		
		AbstractPropertySet<KP, KE> ret = getSubSetStore().get(prefix[idx]);
		if (ret==null) {
			if (!create) {
				return null;
			}
			ret = newInstance(prefix[idx]);
			getSubSetStore().put(prefix[idx], ret);
		}
		
		if (prefix.length==idx+1) {
			return ret;
		}
		
		return ret.subset(prefix, idx+1, create);
	}
	
	@Override
	public void remove(KP key) {
		writeLock().lock();
		try {
			remove(tokenize(key), 0);
		} finally {
			writeLock().unlock();
		}
	}

	protected void remove(KE[] path, int idx) {
		if (path.length==idx+1) {
			getValueStore().remove(path[idx]);
		} else {			
			PropertySet<KP> mount = getMounts().get(path[idx]);
			if (mount!=null) {
				mount.remove(buildPath(idx+1, path));
			} else {			
				AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
				if (ss!=null) {
					ss.remove(path, idx+1);
				}
			}
		}
	}
	
	@Override
	public void clear() {
		writeLock().lock();
		try {
			getValueStore().clear();
			for (PropertySet<KP> m: getMounts().values()) {
				m.clear();
			}
			for (Map.Entry<KE, AbstractPropertySet<KP, KE>> e: getSubSetStore().entrySet()) {
				if (!getMounts().containsKey(e.getKey())) {
					e.getValue().clear();
				}
			}
			for (int i=0; i<shadows.length; ++i) {
				shadows[i]=null;
			}
		} finally {
			writeLock().unlock();
		}
	}

	@Override
	public void mount(KP prefix, PropertySet<KP> source) {
		writeLock().lock();
		try {
			mount(tokenize(prefix), 0, source);
		} finally {
			writeLock().unlock();
		}
	}

	protected void mount(KE[] path, int idx, PropertySet<KP> source) {
		if (path.length==idx+1) {
			getMounts().put(path[idx], source);
		} else {			
			AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
			if (ss==null) {
				ss = newInstance(path[idx]);
				getSubSetStore().put(path[idx], ss);
			}
			
			ss.mount(path, idx+1, source);
		}
	}
	
	@Override
	public void unmount(KP prefix) {
		writeLock().lock();
		try {
			unmount(tokenize(prefix), 0);
		} finally {
			writeLock().unlock();
		}
	}
	
	protected void unmount(KE[] path, int idx) {
		if (path.length==idx+1) {
			getMounts().remove(path[idx]);
		} else {			
			AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
			if (ss!=null) {
				ss.unmount(path, idx+1);
			}
		}
	}
	
	@Override
	public void setAll(PropertySet<KP> source) {
		writeLock().lock();
		try {
			for (KP path: source.keySet()) {
				set(path, source.get(path));
			}
		} finally {
			writeLock().unlock();
		}
	}

	@Override
	public Object get(KP key) {
		Object ret;
		Object[] va = {null};
		readLock().lock();
		try {
			ret = get(tokenize(key), 0, va);
		} finally {
			readLock().unlock();
		}
		
		// Chained version
		if (va[0]!=null) {
			set(key, va[0]);
		}
		
		return ret;
	}
	
	@Override
	public Object get(KP key, long timeout, TimeUnit unit) {
		Object ret;
		Object[] va = {null};
		readLock().lock();
		try {
			ret = get(tokenize(key), 0, va, timeout, unit);
		} finally {
			readLock().unlock();
		}
		
		// Chained version
		if (va[0]!=null) {
			set(key, va[0]);
		}
		
		return ret;
	}
	
	private static class CookEntry {
		
		CookEntry(Future<?> future, Callable<?> callable) {
			this.future = future;
			this.callable = callable;
		}
		
		Future<?> future;
		Callable<?> callable;
		Object value;
		
		synchronized Object get() throws Exception {
			if (future!=null) {
				value = future.get();	
				future = null;
			} else if (callable!=null) {
				value = callable.call();
				callable = null;
			}
			return value;
		}
		
		synchronized Object get(long timeout, TimeUnit unit) throws Exception {
			if (future!=null) {
				value = future.get(timeout, unit);	
				future = null;
			} else if (callable!=null) {
				value = callable.call();
				callable = null;
			}
			return value;
		}
		
		void cancel() {
			if (future!=null && callable!=null && !future.isDone()) {
				future.cancel(false);
				future = null;				
			}
		}
		
	}

	protected Object get(KE[] path, int idx, Object[] va) {
		if (path.length==idx+1) {
			if (getValueStore().containsKey(path[idx])) {
				Object ret = getValueStore().get(path[idx]);
				
				if (ret instanceof Invocable) {
					Invocable<PropertySet<KP>,?, KP, ?> i = (Invocable<PropertySet<KP>,?, KP, ?>) ret;
					if (i.getParameterTypes()==null || i.getParameterTypes().length==0) {
						InvocationImpl<KP> invocation = new InvocationImpl<KP>();
						invocation.setPropertySet(this);
						try {
							return i.invoke(this, invocation, executorService);
						} catch (Exception e) {
							throw new PropertySetException(e);
						}
					}
					return i;
				}
				
				if (ret instanceof CookEntry) {
					try {
						ret = ((CookEntry) ret).get();
						va[0] = ret; // To replace cook entry with value
						return ret;
					} catch (Exception e) {
						throw new PropertySetException(e);
					}
				}
							
				return ret;
			}
		} else {		
			PropertySet<KP> mount = getMounts().get(path[idx]);
			if (mount!=null) {
				return mount.get(buildPath(idx+1, path));
			}
			
			AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
			if (ss!=null) {
				Object ret = ss.get(path, idx+1, va);
				if (ret!=null) {
					return ret;
				}
			}
		}
		
		va[0] = version(shadowGet(buildPath(idx, path)));
		return va[0];
	}
	
	protected Object get(KE[] path, int idx, Object[] va, long timeout, TimeUnit unit) {
		if (path.length==idx+1) {
			if (getValueStore().containsKey(path[idx])) {
				Object ret = getValueStore().get(path[idx]);
				
				if (ret instanceof Invocable) {
					Invocable<PropertySet<KP>,?, KP, ?> i = (Invocable<PropertySet<KP>,?, KP, ?>) ret;
					if (i.getParameterTypes()==null || i.getParameterTypes().length==0) {
						InvocationImpl<KP> invocation = new InvocationImpl<KP>();
						invocation.setPropertySet(this);
						try {
							return i.invoke(this, invocation, executorService);
						} catch (Exception e) {
							throw new PropertySetException(e);
						}
					}
					return i;
				}
				
				if (ret instanceof CookEntry) {
					try {
						ret = ((CookEntry) ret).get(timeout, unit);
						va[0] = ret; // To replace cook entry with value
						return ret;
					} catch (Exception e) {
						throw new PropertySetException(e);
					}
				}
							
				return ret;
			}
		} else {		
			PropertySet<KP> mount = getMounts().get(path[idx]);
			if (mount!=null) {
				return mount.get(buildPath(idx+1, path), timeout, unit);
			}
			
			AbstractPropertySet<KP, KE> ss = getSubSetStore().get(path[idx]);
			if (ss!=null) {
				Object ret = ss.get(path, idx+1, va, timeout, unit);
				if (ret!=null) {
					return ret;
				}
			}
		}
		
		va[0] = version(shadowGet(buildPath(idx, path), timeout, unit));
		return va[0];
	}
	
	@Override
	public Object get(KP key, Object defaultValue) {
		Object ret = get(key);
		return ret == null ? defaultValue : ret;
	}

	@Override
	public Object get(KP key, Object defaultValue, long timeout, TimeUnit unit) {
		Object ret = get(key, timeout, unit);
		return ret == null ? defaultValue : ret;
	}

	@Override
	public <T> T get(KP key, Class<T> type) {
		return get(key, type, null);
	}

	@Override
	public <T> T get(KP key, Class<T> type, long timeout, TimeUnit unit) {
		return get(key, type, null, timeout, unit);
	}

	@Override
	public <T> T get(KP key, Class<T> type, T defaultValue) {
		Object ret = get(key);
		if (ret==null) {
			if (type!=null && type.isInterface()) {
				PropertySet<KP> toWrap = subset(tokenize(key), 0, false);
				if (toWrap!=null) {
					return toWrap.getAdapter(type, null);
				}
			}
			return defaultValue;
		}
		if (type.isInstance(ret)) {
			return (T) ret;
		}
		T converted = converter.convert(ret, type, context);
		if (converted==null) {
			throw new PropertySetException("Cannot convert "+ ret +" to "+type);
		}
		return converted;
	}

	@Override
	public <T> T get(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
		Object ret = get(key, timeout, unit);
		if (ret==null) {
			if (type!=null && type.isInterface()) {
				PropertySet<KP> toWrap = subset(tokenize(key), 0, false);
				if (toWrap!=null) {
					return toWrap.getAdapter(type, null);
				}
			}
			return defaultValue;
		}
		if (type.isInstance(ret)) {
			return (T) ret;
		}
		T converted = converter.convert(ret, type, context);
		if (converted==null) {
			throw new PropertySetException("Cannot convert "+ ret +" to "+type);
		}
		return converted;
	}

	@Override
	public void set(KP key, Object value) {
		writeLock().lock();
		try {
			set(tokenize(key), 0, value);
		} finally {
			writeLock().unlock();
		}
	}

	protected void set(KE[] path, int idx, Object value) {
		if (path.length==idx+1) {
			getValueStore().put(path[idx], value);
		} else {
			PropertySet<KP> mount = getMounts().get(path[idx]);
			if (mount!=null) {
				mount.set(buildPath(idx+1, path), value);
			} else {				
				AbstractPropertySet<KP, KE> ret = getSubSetStore().get(path[idx]);
				if (ret==null) {
					ret = newInstance(path[idx]);
					getSubSetStore().put(path[idx], ret);
				}
				
				ret.set(path, idx+1, value);
			}
		}
	}
	
	@Override
	public void setInvocable(KP key, Invocable<PropertySet<KP>, ?, KP, ?> invocable) {
		set(key, invocable);
	}

	@Override
	public void cook(KP key, Callable<?> callable) {
		cook(key, callable, 0, null);
	}

	protected void cook(KE[] path, int idx, Callable<?> callable, long delay, TimeUnit timeUnit) {
		if (path.length==idx+1) {
			Future<?> future = null;
			if (delay>0 && timeUnit!=null && executorService instanceof ScheduledExecutorService) {
				future = ((ScheduledExecutorService) executorService).schedule(callable, delay, timeUnit);
			} else if (executorService!=null) {
				future = executorService.submit(callable);
			}
			getValueStore().put(path[idx], new CookEntry(future, callable));
		} else {
			PropertySet<KP> mount = getMounts().get(path[idx]);
			if (mount!=null) {
				mount.cook(buildPath(idx+1, path), callable, delay, timeUnit);
			}
			
			AbstractPropertySet<KP, KE> ret = getSubSetStore().get(path[idx]);
			if (ret==null) {
				ret = newInstance(path[idx]);
				getSubSetStore().put(path[idx], ret);
			}
			
			ret.cook(path, idx+1, callable, delay, timeUnit);
		}
	}

	@Override
	public void cook(KP key, Callable<?> callable, long delay, TimeUnit timeUnit) {
		writeLock().lock();
		try {
			cook(tokenize(key), 0, callable, delay, timeUnit);
		} finally {
			writeLock().unlock();
		}
	}

	@Override
	public void cooking(KP key, Future<?> future) {
		set(key, new CookEntry(future, null));
	}

	@Override
	public void lazy(KP key, Callable<?> callable) {
		set(key, new CookEntry(null, callable));
	}

	@Override
	public void cancel(KP key) {
		readLock().lock();
		try {
			cancel(tokenize(key),0);
		} finally {
			readLock().unlock();
		}
	}

	protected void cancel(KE[] path, int idx) {
		if (path.length==idx+1) {
			Object v = getValueStore().get(path[idx]);
			if (v instanceof CookEntry) {
				((CookEntry) v).cancel();
			}
		} else {
			PropertySet<KP> mount = getMounts().get(path[idx]);
			if (mount!=null) {
				mount.cancel(buildPath(idx+1, path));
			}
			
			AbstractPropertySet<KP, KE> ret = getSubSetStore().get(path[idx]);
			if (ret!=null) {
				ret.cancel(path, idx+1);
			}
		}
	}
	
	@Override
	public void cancelAll() {
		readLock().lock();
		try {
			for (PropertySet<KP> mount: getMounts().values()) {
				mount.cancelAll();
			}
			for (PropertySet<KP> ss: getSubSetStore().values()) {
				ss.cancelAll();
			}
			for (Object v: getValueStore().values()) {
				if (v instanceof CookEntry) {
					((CookEntry) v).cancel();
				}				
			}
		} finally {
			readLock().unlock();
		}
	}

	@Override
	public Object invoke(KP key, Object... args) {
		return invoke(this, key, args);
	}
	
	private Object invoke(PropertySet<KP> contextPropertySet, KP key, Object... args) {
		writeLock().lock(); // To avoid deadlocks if invocable has side effects.
		try {
			Object i = contextPropertySet.get(key);
			if (i instanceof Invocable) {
				Invocable invocable = (Invocable) i;
				Class[] parameterTypes = invocable.getParameterTypes();
				if (args!=null && parameterTypes!=null) {
					if (parameterTypes.length!=args.length) {
						throw new PropertySetException("Cannot invoke "+key+" - invalid number of arguments");
					}
					args = Arrays.copyOf(args, args.length);
					for (int j=0; j<args.length; ++j) {
						if (args[j]!=null && !parameterTypes[j].isInstance(args[j])) {
							Object newVal = converter.convert(args[j], parameterTypes[j], context);
							if (args[j]==null) {
								throw new PropertySetException("Cannot covert "+args[j]+" to "+parameterTypes[j]);
							}
							args[j] = newVal;
						}
					}
				}
				InvocationImpl invocation = new InvocationImpl(args, contextPropertySet, null);
				try {
					return invocable.invoke(contextPropertySet, invocation, executorService);
				} catch (Exception e) {
					throw new PropertySetException(e);
				}
			}
			
			throw new PropertySetException("Not invocable: "+i);
		} finally {
			writeLock().unlock();
		}
	}

	@Override
	public Object shadowInvoke(KP key, Object... args) {
		return invoke(shadowPropertySet, key, args);
	}

	@Override
	public Object shadowGet(KP key) {
		return shadowPropertySet.get(key);
	}

	@Override
	public Object shadowGet(KP key, Object defaultValue) {
		return shadowPropertySet.get(key, defaultValue);
	}

	@Override
	public <T> T shadowGet(KP key, Class<T> type) {
		return shadowPropertySet.get(key, type);
	}

	@Override
	public <T> T shadowGet(KP key, Class<T> type, T defaultValue) {
		return shadowPropertySet.get(key, type, defaultValue);
	}
	
	@Override
	public Object shadowGet(KP key, long timeout, TimeUnit unit) {
		return shadowPropertySet.get(key, timeout, unit);
	}

	@Override
	public Object shadowGet(KP key, Object defaultValue, long timeout, TimeUnit unit) {
		return shadowPropertySet.get(key, defaultValue, timeout, unit);
	}

	@Override
	public <T> T shadowGet(KP key, Class<T> type, long timeout, TimeUnit unit) {
		return shadowPropertySet.get(key, type, timeout, unit);
	}

	@Override
	public <T> T shadowGet(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit) {
		return shadowPropertySet.get(key, type, defaultValue, timeout, unit);
	}
	
	
}
