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

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import vo.parameter.*;
import vo.error.*;

public class SearchServlet extends javax.servlet.http.HttpServlet
{
   private static final Logger         LOGGER   = Logger.getLogger("PSearch");
   private static final SearchSettings settings = SearchSettings.getInstance("search.properties");

   final String RESPONSE_ENCODING = "utf-8";
   final String DEFAULT_RESPONSEFORMAT = settings.defaults.responseFormat;
   final String DEFAULT_SKY_SYSTEM     = settings.defaults.skySystem;
   final String DEFAULT_SPEC_SYSTEM    = settings.defaults.specSystem;
   final String DEFAULT_TIME_SYSTEM = "MJD_UTC"; // FIXME take from config file


   public void init() throws ServletException
   {
      super.init();

      LOGGER.config("DB: " + settings.dbConn.toString());
   }

   protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException, UnsupportedEncodingException
   {
      LOGGER.fine("trace");
      legacyLogEntry(request);

      try
      {
         Map<String, String[]> params = request.getParameterMap();

         Coord coord = new Coord();
         coord.pos  = Pos.parsePos(params, DEFAULT_SKY_SYSTEM);
         coord.band = Band.parseBand(params, DEFAULT_SPEC_SYSTEM);
         coord.time = Time.parseTime(params, DEFAULT_TIME_SYSTEM);
         coord.pol  = Pol.parsePol(params);

         coord.fov     = Interval.parseInterval(params, "FOV");
         coord.spatres = Interval.parseInterval(params, "SPATRES");
         coord.specrp  = Interval.parseInterval(params, "SPECRP");
         coord.exptime = Interval.parseInterval(params, "EXPTIME");
         coord.timeres = Interval.parseInterval(params, "TIMERES");

         coord.id = SingleStringParam.parseSingleStringParam(params, "ID");

         coord.collection = SingleStringParam.parseSingleStringParam(params, "COLLECTION");
         coord.facility   = SingleStringParam.parseSingleStringParam(params, "FACILITY");
         coord.instrument = SingleStringParam.parseSingleStringParam(params, "INSTRUMENT");
         coord.dptype     = SingleStringParam.parseSingleStringParam(params, "DPTYPE");

         coord.target = SingleStringParam.parseSingleStringParam(params, "TARGET");
         coord.format = SingleStringParam.parseSingleStringParam(params, "FORMAT");

         String str   = SingleStringParam.parseSingleStringParam(params, "CALIB");
         coord.calib  = (str == null) ? null : Integer.parseInt(str);
         if((coord.calib != null) && ((coord.calib < 0) || (coord.calib > 4)))
            throw new IllegalArgumentException("CALIB out of range: " + coord.calib + " but allowed is 0..4");

         str = SingleStringParam.parseSingleStringParam(params, "MAXREC");
         coord.maxrec  = (str == null) ? null : Integer.parseInt(str);
         if((coord.maxrec != null) && (coord.maxrec < 0))
            throw new IllegalArgumentException("MAXREC must not be negative: " + coord.maxrec);

         /* query Obscore table */

         DbPSearch dbps;
         synchronized(DbPSearch.class)
         {
            dbps = new DbPSearch(settings.dbConn);
         }
         String[] pubdidArr = dbps.queryOverlapingPubdid(coord);

         LOGGER.info("Found " + pubdidArr.length + " records");

         final String RESPONSE_ENCODING = "UTF-8";

         /* if filters installed response will be wrapped */

         if(response instanceof FormatResponseWrapper)
         {
            LOGGER.finest("response-type is FormatResponseWrapper");

            response.setContentType("text/plain");
            response.setCharacterEncoding(RESPONSE_ENCODING);

            /* collect all search description and set to wrapped response */

            FormatResponseWrapper responseWrapper = (FormatResponseWrapper) response;
            responseWrapper.setPubdidArr(pubdidArr);
         }
         else
         {
            LOGGER.finest("response-type is HttpServletResponse");

            response.setContentType("text/plain");
            response.setCharacterEncoding(RESPONSE_ENCODING);

            PrintWriter writer = response.getWriter();
            for(String pubdid : pubdidArr)
            {
               writer.println(pubdid);
            }
            writer.close();
         }
      }
      catch(MultiValuedParamNotSupported ex)
      {
         LOGGER.warning("MultiValuedParamNotSupported: " + ex.getMessage());

         response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
         response.setContentType("text/plain");

         PrintWriter writer = response.getWriter();
         Lib.doMultiValuedParamNotSupported(ex.getMessage(), writer);
         writer.close();
      }
      catch(IllegalArgumentException ex)
      {
         LOGGER.warning("IllegalArgumentException: " + ex.getMessage());

         response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
         response.setContentType("text/plain");

         PrintWriter writer = response.getWriter();
         Lib.doUsageError(ex.getMessage(), writer);
         writer.close();
      }
      catch(Exception ex)
      {
         LOGGER.warning("Exception: " + ex.getMessage());
         // ex.printStackTrace();

         response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
         response.setContentType("text/plain");

         PrintWriter writer = response.getWriter();
         Lib.doError(ex.toString(), writer);
         writer.close();
      }

      return;
   }


   private void legacyLogEntry(HttpServletRequest request) throws UnsupportedEncodingException
   {
      StringBuffer requestURL = request.getRequestURL();
      if (request.getQueryString() != null)
      {
         requestURL.append("?").append(request.getQueryString());
         String completeURL = requestURL.toString();
         String className = this.getClass().getSimpleName();
         LOGGER.finest(className + " vlkb req from: "
               + request.getRemoteAddr()
               + " doGet: " + URLDecoder.decode(completeURL, "UTF-8"));
      }
   }


}