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

import java.io.IOException;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;

import javax.annotation.security.RolesAllowed;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import org.apache.log4j.Logger;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import cz.muni.fi.lasaris.sbms.data.api.request.GroupedAddressRequest;
import cz.muni.fi.lasaris.sbms.data.api.request.TrendsRequest;
import cz.muni.fi.lasaris.sbms.data.api.request.AddressRequest;
import cz.muni.fi.lasaris.sbms.data.api.response.AggregateResponse;
import cz.muni.fi.lasaris.sbms.data.api.response.SeriesOfValuesResponse;
import cz.muni.fi.lasaris.sbms.data.api.response.SliceResponse;
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.AggregationWindow;
import cz.muni.fi.lasaris.sbms.data.entities.Interpolation;
import cz.muni.fi.lasaris.sbms.data.entities.SeriesSpecs;
import cz.muni.fi.lasaris.sbms.data.entities.containers.Trend;
import cz.muni.fi.lasaris.sbms.data.logic.TrendsDataCollector;

public class TrendsEndpoint {
	final static Logger logger = Logger.getLogger(TrendsEndpoint.class);
	
	private TrendsDataCollector dc;
	
	private ObjectMapper m;
	
	
	public TrendsEndpoint() {
		dc = new TrendsDataCollector();
		m = new ObjectMapper();
		m.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
	}
	
	// http://stackoverflow.com/questions/14202257/design-restful-query-api-with-a-long-list-of-query-parameters
	
	/*
	// Methods 4, 6
	@RolesAllowed({"user","admin"})
	@GET
	@Path("/serie/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public SeriesOfValuesResponse getFunc(
			@QueryParam("id") String id,
			@QueryParam("start") String start,
			@QueryParam("end") String end,
			@QueryParam("sampling.cron") String samplingCron,
			@QueryParam("sampling.duration") Long samplingDur,
			@QueryParam("sampling.interpolation") Interpolation interpolation,
			@QueryParam("aggregation.function") String aggregation,
			@QueryParam("aggregation.window") String window,
			) {
		Address a = new Address(id);
		Trend v = ProviderManager.getTrendsProvider(a.getProtocol()).getTrend(a, getSeriesSpecs(start, end, samplingCron, samplingDur, interpolation));
		return new SeriesOfValuesResponse(a, v);
	}
	*/
	
	// Method 4
	@RolesAllowed({"user","admin"})
	@POST
	@Path("/si")
	@Produces(MediaType.APPLICATION_JSON)
	public SeriesOfValuesResponse getSI(
			@QueryParam("id") String id,
			@QueryParam("start") String start,
			@QueryParam("end") String end
			) {

		TrendsRequest r = getReadSpecsSingle(id, start, end, null, null, null, null, null);
		Trend v = dc.getTrend(r);
		return new SeriesOfValuesResponse(r.getAddress(), v);
	}
	
	// Method 16
		@RolesAllowed({"user","admin"})
		@POST
		@Path("/mias")
		@Produces(MediaType.APPLICATION_JSON)
		public SeriesOfValuesResponse getMIAS(
				@QueryParam("ids") String ids,
				@QueryParam("grouping") boolean grouping,
				@QueryParam("start") String start,
				@QueryParam("end") String end,
				@QueryParam("sampling.cron") String samplingCron,
				@QueryParam("sampling.duration") Long samplingDur,
				@QueryParam("sampling.interpolation") Interpolation interpolation,
				@QueryParam("aggregation.function") AggregationFunction aggregation
				) {
			try {
				TrendsRequest r = getReadSpecsMulti(ids, grouping, start, end, samplingCron, samplingDur, interpolation, aggregation, null);
				Trend v = dc.getTrend(r);
				return new SeriesOfValuesResponse(r.getAddress(), v);
			} catch(Exception e) {
				return SeriesOfValuesResponse.getErrorResponse(e.getMessage());
			}
		}
	
	private TrendsRequest getReadSpecsSingle(String id, String start, String end, String samplingCron, 
			Long samplingDur, Interpolation interpolation, AggregationFunction aggregation, AggregationWindow window) {
		
		TrendsRequest readSpecs = getReadSpecsBase(start, end, samplingCron, samplingDur, interpolation, aggregation, window);
		readSpecs.setAddressSpecs(new Address(id));
		return readSpecs;
	}
	
	private TrendsRequest getReadSpecsMulti(String json, boolean grouping, String start, String end, String samplingCron, 
			Long samplingDur, Interpolation interpolation, AggregationFunction aggregation, AggregationWindow window) throws JsonParseException, JsonMappingException, IOException {	
		TrendsRequest readSpecs = getReadSpecsBase(start, end, samplingCron, samplingDur, interpolation, aggregation, window);
		if(grouping) {
			readSpecs.setAddressSpecs(m.readValue(json, AddressRequest.class));
		} else {
			readSpecs.setAddressSpecs(m.readValue(json, GroupedAddressRequest.class));
		}
		return readSpecs;
	}
	
	
	private TrendsRequest getReadSpecsBase(String start, String end, String samplingCron, 
			Long samplingDur, Interpolation interpolation, AggregationFunction aggregation, AggregationWindow window) {
		TrendsRequest readSpecs = new TrendsRequest();
		SeriesSpecs s = getSeriesSpecs(start, end, samplingCron, samplingDur, interpolation);
		readSpecs.setSeriesSpecs(s);
		readSpecs.setAggregation(aggregation, window);
		return readSpecs;
	}
	
	private SeriesSpecs getSeriesSpecs(String start, String end, String samplingCron, 
			Long samplingDur, Interpolation interpolation) {
		SeriesSpecs s = null;
		if(samplingCron != null) {
			s = new SeriesSpecs(getDateTime(start), getDateTime(end), samplingCron, interpolation);
		} else if (samplingDur != null) {
			s = new SeriesSpecs(getDateTime(start), getDateTime(end), Duration.of(samplingDur, ChronoUnit.SECONDS), interpolation);  
		} else {
			s = new SeriesSpecs(getDateTime(start), getDateTime(end));
		}
		return s;
	}
	
	private ZonedDateTime getDateTime(String s) {
		return ZonedDateTime.now();
	}
}
