
#include "mcutout.hpp"

#include "m4vl.hpp"

#include "fitsfiles.hpp"
#include "io.hpp"

#include <linux/limits.h> // PATH_MAX NAME_MAX
#include <cstring>

#include <iterator>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <string>

// for timestamp
#include <iomanip>
#include <chrono>
#include <ctime>

#include <sys/stat.h> // for filesize

using namespace std;


string error_msg(const char * file, int line, string msg)
{
   return string{file} + ":" + to_string(line) + " " + msg;
}



unsigned long mergefilesize(const char * pathname)
{
   struct stat st; 
   if(stat(pathname, &st))
   {   
      return 0;
   };  
   return st.st_size;
}


char** malloc_fitsfiles_array(const vector<string> fitsfiles, char  * arg_c_fitsfiles[])
{
   arg_c_fitsfiles = (char**)malloc(fitsfiles.size() * sizeof(char*));

   int i=0;
   for(string fits : fitsfiles)
   {
      arg_c_fitsfiles[i] = (char*)malloc(fitsfiles.at(i).length() * (sizeof(char)+1));
      strcpy(arg_c_fitsfiles[i], fitsfiles.at(i).c_str());
      i++;
   }
   return arg_c_fitsfiles;
}

void free_fitsfiles_array(const size_t size, char  * arg_c_fitsfiles[])
{
   size_t i;
   for(i=0; i<size;i++)
   {
      free(arg_c_fitsfiles[i]);
   }

   free(arg_c_fitsfiles);
}


#define STACKSTRLEN (MAXPATHLEN + 16 + 512)


unsigned long xmergefiles(
      const vector<string> fitsfiles,
      const string dimensionality,
      const string merge_dir,
      const string result_dir,
      string& merged_file_pathname)
{
   LOG_trace(__func__);

   char ** c_fitsfiles = NULL;
   c_fitsfiles = malloc_fitsfiles_array(fitsfiles, c_fitsfiles);

   const char * mroot = merge_dir.c_str();
   const char * mergedpath = result_dir.c_str();
   const char * prefix = dimensionality.c_str();
   size_t nfiles = fitsfiles.size();

   struct merge_files pm = {mroot,mergedpath,prefix};
   const size_t merged_len = PATH_MAX + NAME_MAX;
   char merged[merged_len]; // FIXME const char* []

   int rc = M4VL_mergefiles(&pm, nfiles, (char**) c_fitsfiles, merged, merged_len);
   if(rc != 0)
   {
      throw runtime_error( error_msg(__FILE__,__LINE__, "M4VL_mergefiles() failed with rc=" + to_string(rc)) );
   }

   free_fitsfiles_array(fitsfiles.size(), c_fitsfiles);

   merged_file_pathname = merged;
   unsigned long mergefsize = mergefilesize(merged);
   return mergefsize;
}


// mergefiles split (enables to call reproject in parallel)


void xmergefiles_common_header(
      const string merge_id,
      const vector<string> fitsfiles,
      const string dimensionality,
      const string merge_dir,
      const string result_dir)
{
   LOG_trace(__func__);

   char ** c_fitsfiles = NULL;
   malloc_fitsfiles_array(fitsfiles, c_fitsfiles);

   const char * jobid = merge_id.c_str();
   const char * mroot = merge_dir.c_str();
   const char * mergedpath = result_dir.c_str();
   const char * prefix = dimensionality.c_str();
   unsigned long dim = strtol(prefix, NULL, 10);// FIXME prefix was (mis)used to carry dimensionality of FITS files
   size_t nfiles = fitsfiles.size();

   struct merge_config mconf;
   M4VL_merge_config_init(jobid, mroot, mergedpath, dim, &mconf);

   int rc = M4VL_mergefiles_common_header(&mconf, nfiles, (const char**) c_fitsfiles);

   free_fitsfiles_array(fitsfiles.size(), c_fitsfiles);

   if(rc == 0)
   {
      LOG_STREAM << "M4VL_mergefiles_common_header() succeeded for job: " + string{jobid} << endl;
   }
   else
   {
      throw runtime_error(
            error_msg(__FILE__,__LINE__, "M4VL_mergefiles_common_header() failed for job: "
               + string{jobid}  + " with rc=" + to_string(rc)) );
   }
}


void xmergefiles_reproject(
      const string merge_id,
      const string fitsfilename,
      const string dimensionality,
      const string merge_dir,
      const string result_dir)
{
   LOG_trace(__func__);

   const char * jobid = merge_id.c_str();
   const char * mroot = merge_dir.c_str();
   const char * mergedpath = result_dir.c_str();
   const char * prefix = dimensionality.c_str();
   unsigned long dim = strtol(prefix, NULL, 10);// FIXME prefix was (mis)used to carry dimensionality of FITS files

   struct merge_config mconf;
   M4VL_merge_config_init(jobid, mroot, mergedpath, dim, &mconf);

   int rc = M4VL_mergefiles_reproject(&mconf, fitsfilename.c_str());
   if(rc == 0)
   {
      LOG_STREAM << "M4VL_mergefiles_reproject() succeeded for job: " + string{jobid} << endl;
   }
   else
   {
      throw runtime_error(
            error_msg(__FILE__,__LINE__, "M4VL_mergefiles_reproject() failed for job: "
               + string{jobid}  + " with rc=" + to_string(rc)) );
   }
}



unsigned long xmergefiles_add_reprojected(
      const string merge_id,
      const string dimensionality,
      const string merge_dir,
      const string result_dir,
      string& merged_file_pathname)
{
   LOG_trace(__func__);

   const size_t merged_len = PATH_MAX + NAME_MAX;
   char merged[merged_len];

   const char * jobid = merge_id.c_str();
   const char * mroot = merge_dir.c_str();
   const char * mergedpath = result_dir.c_str();
   const char * prefix = dimensionality.c_str();
   unsigned long dim = strtol(prefix, NULL, 10);// FIXME prefix was (mis)used to carry dimensionality of FITS files

   struct merge_config mconf;
   M4VL_merge_config_init(jobid, mroot, mergedpath, dim, &mconf);

   int rc = M4VL_mergefiles_add_reprojected(&mconf, merged, merged_len);
   if(rc != 0)
   {
      throw runtime_error(
            error_msg(__FILE__,__LINE__, "M4VL_mergefiles_add_reprojected() failed for job: "
               + string{jobid}  + " with rc=" + to_string(rc)) );
   }

   merged_file_pathname = merged;
   unsigned long mergefsize = mergefilesize(merged);
   return mergefsize;
}


