
import java.util.logging.Logger;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.*;
 

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.*;
 
import java.nio.charset.Charset;


class PubdidResponseWrapper extends HttpServletResponseWrapper
{
   private SearchDescription search;

   public PubdidResponseWrapper(HttpServletResponse response)
   {
      super(response);
      search = null;
   }

   public void set(SearchDescription search) { this.search = search;}
   public SearchDescription get() { return this.search;}

   public void setPubdidArr(String[] pubdidArr) { this.search.pubdidArr = pubdidArr; }
   public String[] getPubdidArr() { return this.search.pubdidArr; }


   public void setAuth(AuthPolicy auth)
   {
      assert (this.search.inputs.auth != null);
      this.search.inputs.auth = auth;
   }
}





public class FormatResponseFilter implements Filter
{
   private static final Logger LOGGER = Logger.getLogger("FormatResponseFilter");
   private static final FormatResponseSettings settings = FormatResponseSettings.getInstance("formatresponsefilter.properties");

   final String RESPONSE_ENCODING = "UTF-8";
   protected Subsurvey[] dbSubsurveyArr  = null;


   @Override
   public void init(FilterConfig filterConfig) throws ServletException
   {
      LOGGER.info("trace");

//      DbPSearch.loadDriver();
/*
      FIXME DbPSearch is using Settings.DBConn from SearchServlet -> discovery.properties

      /* load Surveys table * /
      DbPSearch vlkbSql;
      synchronized(DbPSearch.class)
      {
         vlkbSql = new DbPSearch();
      }

      dbSubsurveyArr = vlkbSql.getSurveyTable();
*/
      String surveysAbsPathname = settings.serviceUrls.surveysAbsPathname();
      LOGGER.info("Loading metadata from: " + surveysAbsPathname);
      dbSubsurveyArr = Subsurvey.loadSubsurveys(surveysAbsPathname);
      LOGGER.info("Surveys: loaded metadata for " + dbSubsurveyArr.length + " known surveys");
      LOGGER.info("Default charset: " + Charset.defaultCharset());
   }



   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
   {
      LOGGER.info("trace");
      LOGGER.info("REQUEST START =============================================================================================");

      PubdidResponseWrapper pubdidWrapper = new PubdidResponseWrapper((HttpServletResponse) response);

      chain.doFilter(request, pubdidWrapper);

      long startTime_msec = System.currentTimeMillis();

      PrintWriter responseWriter = ((HttpServletResponse)response).getWriter();

      if (true)
         //if (pubdidWrapper.getContentType().contains("text/plain"))
      {

         SearchDescription search = pubdidWrapper.get();

         Dataset[] datasetArr = getOutputData(
               search.pubdidArr,
               search.inputs.coord,
               search.inputs.subsurveyId,
               search.inputs.queryString,
               settings.serviceUrls.cutoutUrl());

         SearchOutputData searchOutputData = SearchOutputData.marshall(
               datasetArr,
               search.inputs,
               settings.serviceUrls.mergeUrl(),
               dbSubsurveyArr);

         response.setCharacterEncoding(RESPONSE_ENCODING);
         final String respFormat = settings.serviceUrls.responseFormat();
         LOGGER.info("responseFormat: " + respFormat);

         if(respFormat.equals("application/vlkb+xml")) // FIXME hsould be application/x-vlkb+xml -> x- is for eXperimental a.k.a. not registered
         {
            response.setContentType("application/xml");
            boolean showDuration = true;
            XmlSerializer.serializeToLegacyResults(responseWriter, RESPONSE_ENCODING, searchOutputData,showDuration,startTime_msec);
         }
         else if(respFormat.equals("application/x-votable+xml"))
         {
            response.setContentType(respFormat);
            boolean showDuration = false;
            XmlSerializer.serializeToVoTable(responseWriter, RESPONSE_ENCODING, searchOutputData,showDuration,startTime_msec);
         }
         else
         {
            ; // FIXME throws wrong setting-file param: batter convert string to enum and throw 'unrecoginzed respFormat' then;
              // here use switch with enums and switch-default: say error: 'unsupported respFromat' type
         }

         responseWriter.close();
      }
      LOGGER.info("REQUEST END   =============================================================================================");
   }



   @Override
   public void destroy()
   {
      LOGGER.info("trace");
   }


   ///////////////////////////////////////////////////////////////////
   // collect output data from DB


   /* FIXME type needed in DbPSearch */
   static class ObsDataset
   {
      String data_type;
      String pubdid_str;
      String subsurvey_id;
      String vertices_str;
      String access_url;
      boolean inputInsideDb;
      boolean dbInsideInput;

      boolean em_valid;
      double em_min;
      double em_max;
   }



   private Dataset[] getOutputData(String[] pubdidArr, Coord coord,SubsurveyId subsurveyId, String queryString, String fitsRemotePath)
   {
      LOGGER.info("trace");

      DbPSearch dbps;
      synchronized(DbPSearch.class)
      {
         dbps = new DbPSearch();
      }

      FormatResponseFilter.ObsDataset[] obsDatasetArr = dbps.queryOutputData(pubdidArr, coord, subsurveyId);

      return convert(obsDatasetArr, coord, queryString, fitsRemotePath);
   }





   private Dataset[] convert(FormatResponseFilter.ObsDataset[] obsDatasetArr, Coord coord, String queryString, String fitsRemotePath)
   {
      List<Dataset> datasetList  = new ArrayList<Dataset>();
      for(FormatResponseFilter.ObsDataset obsDataset : obsDatasetArr)
      {
         Dataset dataset = new Dataset();

         dataset.subsurvey_id   = obsDataset.subsurvey_id;
         dataset.overlapCodeSky = convertToOverlapCodeSky(obsDataset.inputInsideDb, obsDataset.dbInsideInput);
         dataset.overlapCodeVel = convertToOverlapCodeVel(coord, obsDataset.em_valid, obsDataset.em_min, obsDataset.em_max);
         dataset.overlapCode    = convertToOverlapCode(dataset.overlapCodeSky, dataset.overlapCodeVel);
         dataset.dataType       = obsDataset.data_type;
         dataset.publisherDid   = obsDataset.pubdid_str;

         dataset.access.accessFileUrl   = obsDataset.access_url;
         dataset.access.accessCutoutUrl = fitsRemotePath + "?pubdid=" + dataset.publisherDid + "&amp;" + queryString;
         dataset.access.accessMosaicUrl  = null;

         dataset.vertices_deg = convertToVertices(obsDataset.vertices_str);

         datasetList.add(dataset);
      }
      return datasetList.toArray(new Dataset[0]);
   }


   private int convertToOverlapCodeSky(boolean inpInDb, boolean dbInInp)
   {
      if(!inpInDb && !dbInInp) return 4; // parial overlap
      else if( inpInDb && !dbInInp) return 3; // input region completely inside fits-datacube
      else if(!inpInDb &&  dbInInp) return 2; // datacube completely inside input-region
      else return 5; // exact match: both inpInDb dbInInp are true
   }


   private int convertToOverlapCodeVel(Coord coord, boolean v_valid, double v_min, double v_max)
   {
      if(coord.vel_valid && v_valid)
      {
         if(coord.vel_type.equals("1"))
         {
            // FIXME assert coord: vel_min <= vel_max
            // FIXME assert cube:  v_min   <= v_max

            boolean dbInInp = (coord.vel_low <= v_min) && (v_min <= coord.vel_up)
               && (coord.vel_low <= v_max) && (v_max <= coord.vel_up);

            boolean inpInDb = (v_min <= coord.vel_low) && (coord.vel_low <= v_max)
               && (v_min <= coord.vel_up ) && (coord.vel_up  <= v_max);

            return convertToOverlapCodeSky(inpInDb, dbInInp);

         }
         else ;// FIXME other v_type NotImplemented yet
      }

      return -1; // FIXME use enums; meaning: overlap code in velocity not applicable --> dont print in XML
   }



   private int convertToOverlapCode(int ovcSky, int ovcVel)
   {
      if(ovcVel == -1) return ovcSky; // 2D images
      else if(ovcSky == ovcVel) return ovcSky;
      else return 4;
   }


   private Dataset.Vertices convertToVertices(String polygon)
   {
      final double RAD2DEG = 180.0 / Math.PI;

      Dataset.Vertices vert = new Dataset.Vertices();

      polygon = polygon.replace("("," ");
      polygon = polygon.replace(")"," ");
      polygon = polygon.replace("{"," ");
      polygon = polygon.replace("}"," ");

      String[] verts = polygon.split(",");

      vert.lon[0] = RAD2DEG * Double.parseDouble(verts[0]);
      vert.lat[0] = RAD2DEG * Double.parseDouble(verts[1]);
      vert.lon[1] = RAD2DEG * Double.parseDouble(verts[2]);
      vert.lat[1] = RAD2DEG * Double.parseDouble(verts[3]);
      vert.lon[2] = RAD2DEG * Double.parseDouble(verts[4]);
      vert.lat[2] = RAD2DEG * Double.parseDouble(verts[5]);
      vert.lon[3] = RAD2DEG * Double.parseDouble(verts[6]);
      vert.lat[3] = RAD2DEG * Double.parseDouble(verts[7]);

      return vert;
   }


}
