package cz.muni.fi.lasaris.sbms.data.logic;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;

import cz.muni.fi.lasaris.sbms.data.api.request.GroupedAddressRequest;
import cz.muni.fi.lasaris.sbms.data.api.request.AddressRequest;
import cz.muni.fi.lasaris.sbms.data.entities.Address;
import cz.muni.fi.lasaris.sbms.data.entities.AggregationFunction;
import cz.muni.fi.lasaris.sbms.data.entities.containers.Snapshot;
import cz.muni.fi.lasaris.sbms.data.entities.values.AggregatedValue;
import cz.muni.fi.lasaris.sbms.data.entities.values.RawValue;
import cz.muni.fi.lasaris.sbms.data.providers.AbstractSimpleDataPointsProvider;
import cz.muni.fi.lasaris.sbms.data.providers.DataPointsProvider;



public class DPDataCollector {

	final static Logger logger = Logger.getLogger(DPDataCollector.class);

	public Snapshot getDataPointValues(AddressRequest readSpecs, boolean allowCache) {
		DataPointsProvider p = new MultiProtocolProvider(new ProtocolParser(readSpecs));
		Snapshot result = p.getDataPointValues(readSpecs.getAddresses(), allowCache);
		return result;
	}

	public AggregatedValue getDataPointsAggregation(AddressRequest readSpecs, AggregationFunction aggregation,
			boolean allowCache) {

		DataPointsProvider p = new MultiProtocolProvider(new ProtocolParser(readSpecs));
		AggregatedValue result = p.getDataPointsAggregation(readSpecs.getAddresses(), aggregation, allowCache);
		return result;

	}

	public Map<String, Snapshot> getDataPointGroupedValues(GroupedAddressRequest readSpecs,
			boolean allowCache) {
		
		DataPointsProvider p = new MultiProtocolProvider(new ProtocolParser(readSpecs));
		Map<String, Snapshot> result = p.getDataPointGroupedValues(readSpecs.getGroups(), allowCache);
		return result;
	}

	public Map<String,AggregatedValue> getDataPointGroupAggregations(GroupedAddressRequest readSpecs,
			AggregationFunction aggregation, boolean allowCache) {

		DataPointsProvider p = new MultiProtocolProvider(new ProtocolParser(readSpecs));
		Map<String,AggregatedValue> result = p.getDataPointGroupAggregations(readSpecs.getGroups(), aggregation, allowCache);
		return result;
	}

	private class MultiProtocolProvider extends AbstractSimpleDataPointsProvider {
		
		ProtocolParser p;
		
		public MultiProtocolProvider(ProtocolParser parser) {
			p = parser;
		}
		
		@Override
		protected Map<Address, RawValue> getValues(List<Address> dataPoints, boolean allowCache) {
			NavigableSet<String> protocols = p.getProtocolsSet();
			

			if(protocols.size() == 1) {
				return ProviderManager.getDataPointsProvider(protocols.first())
						.getDataPointValues(p.getAddresses(protocols.first()), allowCache).getData();
			}

			AtomicInteger counter = new AtomicInteger(0);
			int count = protocols.size();

			List<Snapshot> results = Collections.synchronizedList(new LinkedList<Snapshot>());

			for(String protocol : protocols) {
				new Thread(new Runnable() {
					@Override
					public void run() {
						Snapshot rs = (ProviderManager.getDataPointsProvider(protocol)
								.getDataPointValues(p.getAddresses(protocol), allowCache)); 
						synchronized(results) {
							results.add(rs);
							counter.incrementAndGet();
							results.notifyAll();
						}

					}}).start();
			}
			synchronized(results) {
				while(counter.get() < count) {
					try {
						results.wait();
					} catch (InterruptedException e) {
						logger.error("Exception while waiting for completion.", e);
					}
				}
			}

			Snapshot result = new Snapshot();


			synchronized(results) {
				for(Snapshot r : results) {
					result.addAll(r.getData());
				}
			}

			return result.getData();
		}

		@Override
		public void init(Properties props, String name, String propertiesPrefix) {
		}

		@Override
		public void close() {
		}
	}

}
