import java.io.PrintWriter;
import java.io.BufferedOutputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

import java.io.File;
import java.io.FileInputStream;

import uws.UWSException;
import uws.job.ErrorType;
import uws.job.JobThread;
import uws.job.Result;
import uws.job.UWSJob;
import uws.service.UWSUrl;

import java.util.Map;
import java.util.HashMap;

import java.security.Principal;

public class UWSSodaWork extends JobThread
{
   private Settings     settings    = UWSSoda.settings;
   private Subsurvey[]  subsurveys  = UWSSoda.subsurveys;

   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;

   /* NOTE needed if cutouts dir served by vlkb-datasets */
   private String webappRootRequestUrl = null;



   public UWSSodaWork(UWSJob j) throws UWSException
   {
      super(j);
      UWSUrl url = j.getUrl();
      webappRootRequestUrl = url.getUrlHeader();
   }



   @Override
   protected void jobWork() throws UWSException, InterruptedException
   {
      long startTime_msec = System.currentTimeMillis();
      boolean showDuration = true;

      final String RESPONSE_ENCODING = "utf-8";

      try
      {
         Map<SodaParam, String[]> params = collectSodaParams(job);
         SodaParser parser = new SodaParser(params);

         String id   = null;
         Pos    pos  = null;
         Band   band = null;
         Time   time = null;
         Pol    pol  = null;

         if(parser.sodaReq_hasSodaId())
         {
            id   = parser.sodaReq_getId();
            pos  = parser.sodaReq_getPosCirclePolygon();
            band = parser.sodaReq_getBand();
            time = parser.sodaReq_getTime();
            pol  = parser.sodaReq_getPol();
         }
         else
         {
            id   = parser.vlkbReq_getPubdid();
            pos  = parser.vlkbReq_getCircleRect();
            band = parser.vlkbReq_getVelocity();
         }

         Coord coord = new Coord(DEFAULT_SKY_SYSTEM, pos, DEFAULT_SPEC_SYSTEM, band, time, pol);

         final String respContentType = "application/fits"; // FIXME parse this from RESPONSEFORMAT param

         // implementation

         Resolver rsl = new Resolver(settings);
         rsl.resolve(id);

         /* metadata/subsurveys */
         FitsCard[] extraCards = null;
         if(rsl.subsurveyId != null)
         {
            extraCards = Subsurvey.subsurveysFindCards(subsurveys, rsl.subsurveyId);
         }

         Datasets datasets = new DatasetsImpl(settings);

         final String DEFAULT_TIME_SYSTEM = "MJD_UTC"; // FIXME take from confif file

         if(pos  != null) pos.setSystem(Pos.System.valueOf(DEFAULT_SKY_SYSTEM));
         if(band != null) band.setSystem(Band.System.valueOf(DEFAULT_SPEC_SYSTEM));
         if(time != null) time.setSystem(Time.System.valueOf(DEFAULT_TIME_SYSTEM));

         CutResult cutResult = datasets.doCutoutFile(rsl.relPathname, rsl.hdunum, pos, band, time, pol, false, null);

         /* send Results */

         Result result = createResult("cutout");
         result.setMimeType(respContentType);
         OutputStream respOutputStream = getResultOutput(result);

         if(respContentType.equals("text/xml") || respContentType.equals("application/xml"))
         {
            PrintWriter writer = new PrintWriter(new OutputStreamWriter(respOutputStream, RESPONSE_ENCODING));

            String accessUrl = convertLocalPathnameToRemoteUrl(cutResult.fileName,
                  settings.fitsPaths.cutouts(),
                  settings.fitsPaths.cutoutsUrl());

            XmlSerializer.serializeToLegacyCutResult(writer, RESPONSE_ENCODING,
                  cutResult, accessUrl,
                  id, pos, band, time, pol, null, false,
                  showDuration, startTime_msec);

               writer.close();
         }
         else if(respContentType.equals("application/fits"))
         {
            String absCutPathname = cutResult.fileName;
            File downloadFile = new File(absCutPathname);
            FileInputStream input = new FileInputStream(downloadFile);

            input.transferTo(respOutputStream);

            downloadFile.delete();
         }
         else
         {
            throw new AssertionError("Unsupported contentType for response: " + respContentType);
         }

         respOutputStream.close();
         publishResult(result);
      }
      catch(IllegalArgumentException ex)
      {
         throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ex,
               "Illegal arg exception " + job.getJobId() + " !", ErrorType.TRANSIENT);
      }
      catch(IOException ex)
      {
         throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, ex,
               "IO exception " + job.getJobId() + " !", ErrorType.TRANSIENT);
      }
   }


   private String convertLocalPathnameToRemoteUrl(String localPathname,
         String FITScutpath, String FITSRemoteUrlCutouts)
   {
      LOGGER.info("trace " + localPathname);
      String fileName = localPathname.replaceAll(FITScutpath + "/", "");
      LOGGER.info("local filename: " + fileName);
      String remotefname = FITSRemoteUrlCutouts + "/" + fileName;
      LOGGER.info("remote url    : " + remotefname);
      return remotefname;
   }




   /* see UWSParameters::getAdditionalParameter -> UWSParameters.java.l465: ...returned value maybe an array... */
   /* Object getAdditionalParameterValue(String paramName) --> calls UWSParameters::getAdditionalParameters()   */
   private Map<SodaParam, String[]> collectSodaParams(UWSJob job)
   {
      Map<SodaParam, String[]> params = new HashMap<SodaParam, String[]>();
      for(SodaParam paramToken : SodaParam.values())
      {
         String[] paramValue = (String[])job.getAdditionalParameterValue(paramToken.toString());
         params.put(paramToken, paramValue);
      }
      return params;
   }

   /*
      Map<String, String[]> params = Map.of(
      "ID" , new String[]{pubdid},
      "CIRCLE", new String[]{circleStr},
      "BAND", new String[]{bandStr},
      "skysystem", new String[]{skysystemStr},
      "specsystem", new String[]{specsystemStr}
      );
      */
}
