
#include "cutout.hpp"
#include "mcutout.hpp"
#include "fitsfiles.hpp"
#include "fits_header.hpp"
#include "ast4vl.hpp"
#include "mcutout_nljson.hpp"
#include "mcutout_ostream.hpp"
#include "json.hpp"
#include "io.hpp"
#include "my_assert.hpp"

#include <string.h>
#include <string>
#include <fstream> // ofstream for tgz-file
#include <sstream>
#include <stdexcept>
// for timestamp
#include <iomanip>
#include <chrono>
#include <ctime>

/* create_timestamp */
#include <time.h>
#include <sys/time.h>



using namespace std;
using json = nlohmann::json;



// mcutout



string exec_targz(std::vector<cut_resp_s> resps, string targzdir) // FIXME find suitable libs and replace cmd-exec
{
   LOG_trace(__func__);
   /* FIXME error check missing */

   /* encode responses to json and add to tgz file */

   json jjrr;
   try
   {
      jjrr = resps;
   }
   catch(json::exception& e)
   {
      LOG_STREAM << "mcutout_json() resp : " << e.what() << std::endl;
   }
   string resps_json_str =  jjrr.dump();

   /* add json string as file inside tar.gz */
   const string resps_json_name = "response.json";
   {
      const string resps_json_pathname = targzdir + "/" + resps_json_name;
      std::ofstream file_id;
      file_id.open(resps_json_pathname);
      file_id << resps_json_str;
      file_id.close();
   }
   /* check tar and gzip are installed */

   const string timestamp{create_timestamp()};

   const string tgz_filename = "mcutout_" + timestamp + ".tar.gz";
   const string tgzname = targzdir + "/" + tgz_filename;
   string cmd = "tar cfz " + tgzname;
   cmd.append(" -C " + targzdir);
   cmd.append(" " + resps_json_name);
   for(auto resp : resps)
   {
      if(resp.type == content_type::FILENAME)
         cmd.append(" " + resp.content);
   }

   LOG_STREAM << "compress: " << cmd << endl;
   system(cmd.c_str());

   return tgz_filename;
}


struct cut_resp_s one_cutout(const struct cut_param_s cut, const string fits_path, const string fits_cut_path)
{
   LOG_trace(__func__);

   LOG_STREAM << cut << endl;

   struct cut_resp_s resp;
   try
   {
      //uintmax_t filesize;
      const string relative_pathname = (cut.filename); // FIXME config wrong is http://...
      const string abs_fits_pathname = fits_path + '/' + relative_pathname;
      const string cutfitsname = fits_cut_path + '/' + generate_cut_fitsname(relative_pathname, cut.hdunum);
      /*filesize =*/ cutout_file(
            abs_fits_pathname, cut.hdunum,
            cut.coord,
            cutfitsname,
            cut.cards);

      if(cut.countNullVals)
      {
         unsigned long long null_cnt;
         unsigned long long total_cnt;
         double fill_ratio  = fitsfiles::calc_nullvals(fits_cut_path + "/" + cutfitsname, 1/*hdunum*/,
               null_cnt, total_cnt);
         LOG_STREAM << "nullvals: " << fill_ratio << " : " << null_cnt << " : " << total_cnt << endl;
      }

      resp.type = content_type::FILENAME;
      resp.content = cutfitsname;
   }
   catch (std::invalid_argument const& e)
   {
      resp.type = content_type::BAD_REQUEST;
      resp.content = string("Invalid argument error: ").append(e.what());
   }
   catch (std::exception const& e)
   {
      resp.type = content_type::SERVICE_ERROR;
      resp.content = string("System error: ").append(e.what());
   }

   return resp;
}



struct mcutout_res_s mcutout(vector<struct cut_param_s> cut_params, const string fits_path, const string fits_cut_path)
{
   LOG_trace(__func__);

   std::vector<cut_resp_s> resps(cut_params.size());

   unsigned int i = 0;
   for(auto cut : cut_params)
   {
      cut_resp_s resp;
      resp = one_cutout(cut, fits_path, fits_cut_path);
      resp.input = cut;
      resps[i++] = resp;
   }

   string tgzfilename = exec_targz(resps, fits_cut_path);

   struct mcutout_res_s mresult{0,tgzfilename, resps};

   return mresult;
}

