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

import java.net.URI;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
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.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import cz.muni.fi.lasaris.sbms.api.entities.DataPoint;
import cz.muni.fi.lasaris.sbms.api.entities.Influence;
import cz.muni.fi.lasaris.sbms.api.entities.ObservedProperty;
import cz.muni.fi.lasaris.sbms.api.entities.Scope;
import cz.muni.fi.lasaris.sbms.api.entities.Sensing;
import cz.muni.fi.lasaris.sbms.api.entities.Source;
import cz.muni.fi.lasaris.sbms.api.request.DataPointInsert;
import cz.muni.fi.lasaris.sbms.api.request.DataPointsRequest;
import cz.muni.fi.lasaris.sbms.api.response.DataPointsResponse;
import cz.muni.fi.lasaris.sbms.model.ModelUpdater;
import cz.muni.fi.lasaris.sbms.model.QueryParser;

@Path("datapoints")
@PermitAll
public class DataPointsEndpoint {
	
	final static Logger logger = Logger.getLogger(DataPointsEndpoint.class);
	
	@Context
	private UriInfo uriInfo;
	
	@RolesAllowed({"user","admin"})
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public DataPointsResponse getDataPoints(
			@QueryParam("bmsId") String dpIdP,
			@QueryParam("type") String dptP,
			@QueryParam("source.bimId") String sourceIdP,
			@QueryParam("source.type") String sourceTypeP,
			@QueryParam("source.location") String sourceLocP,
			@QueryParam("scope.bimId") String scopeIdP,
			@QueryParam("scope.type") String scopeTypeP,
			//@QueryParam("scopeDevType") String scopeDevTypeP,
			@QueryParam("scope.location") String scopeLocP,
			@QueryParam("sensing.type") String sensingTypeP,
			@QueryParam("sensing.window") String sensingWindowP,
			@QueryParam("property.domain") String propDomP,
			@QueryParam("property.quality") String propQualityP,
			@QueryParam("influenced.scope.bimId") String inflScopeIdP,
			@QueryParam("influenced.property.quality") String inflQualityP,
			@QueryParam("influenced.property.domain") String inflDomainP,
			@QueryParam("fields") String fieldsP,
			@QueryParam("grouping") String groupingP
			) {
		DataPointsRequest dpr = new DataPointsRequest();
		DataPoint dp = new DataPoint();
		dpr.setDataPoint(dp);
		
		if(dpIdP != null) {
			dp.setBmsId(dpIdP);
		}
		
		if(dptP != null) {
			dp.setType(dptP);
		}
		
		if(sourceIdP != null || sourceTypeP != null || sourceLocP != null) {
			Source s = new Source(sourceIdP, sourceTypeP, sourceLocP);
			dp.setSource(s);
		}
		
		if(scopeIdP != null || scopeTypeP != null  || scopeLocP != null) {
			Scope s = new Scope(scopeIdP, scopeTypeP, scopeLocP);
			dp.setScope(s);
		}
		
		if(sensingTypeP != null || sensingWindowP != null) {
			Sensing s = new Sensing(sensingTypeP, sensingWindowP);
			dp.setSensing(s);
		}
		
		if(propDomP != null || propQualityP != null) {
			ObservedProperty p = new ObservedProperty(propQualityP, propDomP);
			dp.setProperty(p);
		}
		
		ObservedProperty ip = null;
		Scope is = null;
		if(inflScopeIdP != null) {
			is = new Scope(inflScopeIdP, null, null);
		}
		if(inflQualityP != null || inflDomainP != null) {
			ip = new ObservedProperty(inflQualityP, inflDomainP);
		}
		
		if(ip != null || is != null) {
			Influence i = new Influence(ip, is);
			dp.setInfluence(i);
		}
		
		dpr.setGrouping(groupingP);
		
		setResponseFields(fieldsP, dpr);
		try {
			return QueryParser.getDataPoints(dpr);
		} catch (Exception e){
			logger.info(e.toString());
			e.printStackTrace();
			DataPointsResponse r = new DataPointsResponse();
			r.setError(e.toString());
			return r;
		}
	}
	
	//@RolesAllowed({"user","admin"})
	@GET
	@Path("/{bmsId}")
	@Produces(MediaType.APPLICATION_JSON)
	public DataPointsResponse getDataPoint(
			@PathParam("bmsId") String bmsIdP, 
			@QueryParam("fields") String fieldsP) {
		
		DataPointsRequest dpr = new DataPointsRequest();
		DataPoint dp = new DataPoint();
		dp.setBmsId(bmsIdP);
		dpr.setDataPoint(dp);
		
		setResponseFields(fieldsP, dpr);
		
		try {
			return QueryParser.getDataPoints(dpr);
		} catch (Exception e){
			logger.info(e);
			DataPointsResponse r = new DataPointsResponse();
			r.setError(e.toString());
			return r;
		}
	}
	
	@RolesAllowed("admin")
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	public Response InsertDataPointInfo(DataPointInsert insert) {
		try {
			URI createdUri = new URI(uriInfo.getAbsolutePath().toString() + DataPoint.getResourceId(insert.getDataPoint().getBmsId()));
			if(ModelUpdater.insertDataPoint(insert)) {
				return Response.created(createdUri).build();
			} else {
				return Response.status(Response.Status.BAD_REQUEST).entity("Invalid JSON data - see server log").build();
			}
		} catch (Exception e) {
			logger.error(e);
			return Response.serverError().build();
		}
	}
	
	@RolesAllowed("admin")
	@DELETE
	@Path("/{datapoint.bmsId}")
	@Consumes(MediaType.APPLICATION_JSON)
	public Response RemoveDataPointInfo(@PathParam("datapoint.bmsId") String bmsId) {
		System.out.println(bmsId);
		try {
			if(ModelUpdater.removeDataPointAssertions(bmsId)) {
				return Response.ok().build();
			} else {
				return Response.status(Response.Status.BAD_REQUEST).entity("Invalid JSON data - see server log").build();
			}
		} catch (Exception e) {
			logger.error(e);
			return Response.serverError().build();
		}
	}
	
	private void setResponseFields(String fields, DataPointsRequest dpr) {
		if(fields != null) {
			for(String f : fields.split(",")) {
				if(StringUtils.isNotEmpty(f)) {
					dpr.getResponseFields().add(f);
				}
			}
		}
	}
	
	
}

	
	
