import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedOutputStream;
import javax.servlet.ServletOutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
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;

/* for datasets::doAction */
import java.security.Principal;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;

import vo.parameter.*;

public class UWSMergeWork extends JobThread
{
   private Settings settings = UWSMerge.settings;

   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";

   Datasets datasets = new DatasetsImpl(settings);

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

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


   /* FIXME in UWS howto result.setSize(size); */

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

         Map<String, String[]> params = collectSodaParams(job);

         String id   = SingleStringParam.parseSingleStringParam(params, "ID");
         Pos    pos  = Pos.parsePos(params, DEFAULT_SKY_SYSTEM);
         Band   band = Band.parseBand(params, DEFAULT_SPEC_SYSTEM);
         Time   time = Time.parseTime(params, DEFAULT_TIME_SYSTEM);
         Pol    pol  = Pol.parsePol(params);
         String pixels = SingleStringParam.parseSingleStringParam(params, "PIXELS");

         /*         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(pos, band, time, pol, pixels);

         CutResult cutResult  = datasets.doMerge(parseLegacyPubdidArr(id), coord, false);//countNullValues);

         final String respFormat = "text/xml";// FIXME read from param RESPONSEFORMAT ?


         String contentType = respFormat;
         String respEncoding = RESPONSE_ENCODING;
         Result result = createResult("Report");
         result.setMimeType(respFormat);
         OutputStream respOutputStream = getResultOutput(result);

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

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

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

            writer.close();
         }
         else if(contentType.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 output: " + contentType);
         }

         /* publishResult(result);*/
         respOutputStream.close();
      }
      catch(IllegalArgumentException ex)
      {
         throw new UWSException(UWSException.BAD_REQUEST, ex.getMessage());
      }
      catch(FileNotFoundException ex)
      {
         throw new UWSException(UWSException.BAD_REQUEST, ex.getMessage());
      }
      catch(IOException ex)
      {
         throw new UWSException(UWSException.BAD_REQUEST, ex.getMessage());
      }
   }





   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;
   }




   /* semi-colon separated list of pudids convert to arra */
   private String[] parseLegacyPubdidArr(String pubdids)
   {
      List<String> pubdidList = new ArrayList<String>();
      String[] pdArr = pubdids.split(";");
      for(String pd : pdArr)
         if(pd.length() > 0) pubdidList.add(pd);

      String[] pubdidArr = new String[pubdidList.size()];

      return pubdidList.toArray(pubdidArr);
   }

   private Map<String, String[]> collectSodaParams(UWSJob job)
   {
      Map<String, String[]> params = new HashMap<String, String[]>();
      String[] paraTokens = {"POS", "BAND", "TIME", "POL", "PIXELS"};
      for(String paramToken : paraTokens)
      {
         String[] paramValue = (String[])job.getAdditionalParameterValue(paramToken);
         params.put(paramToken, paramValue);
      }
      return params;
   }

}

