/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package eu.dnetlib.espas.spatial.impl;

import eu.dnetlib.espas.spatial.QShape;
import eu.dnetlib.espas.spatial.QueryCRS;
import eu.dnetlib.espas.spatial.SpatialQueryIF;
import eu.dnetlib.espas.spatial.shared.SpatialQueryStatus;
import eu.dnetlib.espas.spatial.TimePeriodConstraint;
import eu.dnetlib.espas.spatial.shared.SpatialQueryStatus.QueryStatus;
import eu.dnetlib.espas.spatial.utils.QueryDBUtils;
import eu.dnetlib.espas.spatial.utils.SQDiskMonitor;
import eu.dnetlib.espas.util.GeometryMetadataHandler;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.log4j.Logger;

/**
 *
 * @author gathanas
 */
public class QueryEngineImpl implements SpatialQueryIF{
   private static final Logger _logger = Logger.getLogger(QueryEngineImpl.class);
   
   private ExecutorService qExecutor;
   
   private int queryEnginePoolSize= 10;
   private QueryDBUtils dBUtils;
   private SQDiskMonitor diskMonitor;
   private Boolean enableSatellite = true;
   private Map<String,QueryHandler> queryHandlerMap= new HashMap<String, QueryHandler>(); 

   public QueryEngineImpl() {
   }

    @Override
    public void cancelLocationQuery(String queryId) {
        if (queryHandlerMap.containsKey(queryId)) {
            dBUtils.updateQueryStatus(queryId, SpatialQueryStatus.QueryStatus.CANCELED, "Query cancelled upon user request.");
            queryHandlerMap.get(queryId).cancelQuery();
            queryHandlerMap.remove(queryId);
        }
        else{
            dBUtils.updateQueryStatus(queryId, SpatialQueryStatus.QueryStatus.CANCELED, "Query cancelled upon user request.");
        }
    }
   
   
   @Override
   public synchronized String findObservationsByLocation(QShape querySpace, QueryCRS crs, TimePeriodConstraint timeConstraint, String userid) {
      QueryHandler qHandler = new QueryHandler(querySpace,crs,timeConstraint,userid, dBUtils, diskMonitor);
      String queryId = qHandler.getQueryId();
      queryHandlerMap.put(queryId, qHandler);
      dBUtils.updateQueryStatus(queryId, SpatialQueryStatus.QueryStatus.PENDING, "");
      qExecutor.execute(qHandler);
      return queryId;
   }

   @Override
   public synchronized SpatialQueryStatus getQueryStatus(String queryId, String userid) {
       SpatialQueryStatus status = null;
       try {
           status = dBUtils.getQueryStatus(queryId,userid);
           if(status.getStatus()==QueryStatus.COMPLETED||status.getStatus()==QueryStatus.CANCELED||status.getStatus()==QueryStatus.FAILED||status.getStatus()==QueryStatus.EXPIRED)
               if(queryHandlerMap.containsKey(queryId))
                   queryHandlerMap.remove(queryId);
           
       } catch (SQLException ex) {
           _logger.debug("Exception occured while retrieving query status", ex);
           status=  new SpatialQueryStatus();
           status.setStatus(SpatialQueryStatus.QueryStatus.FAILED);
           status.setDescription("Failed while trying to retrieve query status.");
       }
      return status;
   }

   //////////////////////////////////////////////////////////////
   //          Spring initialization code

   public void init(){
      qExecutor = Executors.newFixedThreadPool(queryEnginePoolSize);
      GeometryMetadataHandler.initHandler(dBUtils.getSpDBSource());
   }
   
   public int getQueryEnginePoolSize() {
      return queryEnginePoolSize;
   }

   public void setQueryEnginePoolSize(int queryEnginePoolSize) {
      this.queryEnginePoolSize = queryEnginePoolSize;
   }

   public QueryDBUtils getdBUtils() {
      return dBUtils;
   }

   public void setdBUtils(QueryDBUtils dBUtils) {
      this.dBUtils = dBUtils.getQueryDBInstance();
   }

   public SQDiskMonitor getDiskMonitor() {
      return diskMonitor;
   }

   public void setDiskMonitor(SQDiskMonitor diskMonitor) {
      this.diskMonitor = diskMonitor;
   }

   public Boolean getEnableSatellite() {
      return enableSatellite;
   }

   public void setEnableSatellite(Boolean enableSatellite) {
      this.enableSatellite = enableSatellite;
   }
}
