/*
*/

#include <RTSMonFrame.h>
#include <json.hpp>
#include <fstream>
#include <string>
#include <iostream>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>


static const std::string RTSMONFID("id");
static const std::string RTSMONID("monid");
static const std::string RTSMONNAME("name");
static const std::string RTSMONSRATE("samplerate");
static const std::string RTSMONSPERIOD("sampleperiod");
static const std::string RTSMONDTIME("downtime");
static const std::string RTSMONTIMESTAMP("timestamp");
static const std::string RTSMONSAMPLES("samples");
static const std::string RTSMONHW("hwdescription");

using namespace std::chrono;


RTSMonFrame::RTSMonFrame(std::string id, std::string name, int samplerate, int sampleperiod, int downtime, const std::list<std::shared_ptr<ISensorDesc>>& sensDesc, std::string path) { 
   this->id = id;
   this->name = name;
   this->samplerate = samplerate;
   this->sampleperiod = sampleperiod;
   this->downtime = downtime;
   this->mSenorDesriptions.insert(this->mSenorDesriptions.end(), sensDesc.begin(), sensDesc.end());
   this->path = path;
}

RTSMonFrame::~RTSMonFrame(){}



std::string RTSMonFrame::GetId() {
   return this->id;
}

void RTSMonFrame::SetName(const std::string& name){
   this->name = name;
}


const std::string& RTSMonFrame::GetName() {
   return this->name;
}


void RTSMonFrame::SetSampleRate(int samplerate){
   this->samplerate = samplerate;   
}

int RTSMonFrame::GetSampleRate() {
   return this->samplerate;
}


void RTSMonFrame::SetSamplePeriod(int sampleperiod){
   this->sampleperiod= sampleperiod;
}   

int RTSMonFrame::GetSamplePeriod(){
   return this->sampleperiod;
}


void RTSMonFrame::SetDownTime(int downtime){
   this->downtime= downtime;
}


int RTSMonFrame::GetDownTime(){
   return this->downtime;
}


bool RTSMonFrame::generate([[maybe_unused]] u_int32_t* values, [[maybe_unused]] u_int32_t length){
   // Example of the very popular RFC 3339 format UTC time
   std::stringstream ss;
   //build path
   ss << this->path << this->id;
   std::string monpath = ss.str();
   boost::filesystem::path directory(monpath.c_str());

   //check for directory
   if (!boost::filesystem::exists(directory)){
      boost::filesystem::create_directories(directory);
      printf("CA7 : created dir %s.\n", monpath.c_str());
   }

   std::time_t time = std::time({});
   char timeString[std::size("yyyy-mm-ddThh:mm:ss")];
   std::strftime(std::data(timeString), std::size(timeString),"%FT%TZ", std::gmtime(&time));

   ss << "/" << timeString << ".json";
   std::string filename = ss.str();
   printf("CA7 : filename %s.\n", filename.c_str());

   auto unix_timestamp = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();


   //generate header id, name, timestamp,samplerate, sampleperiod, downtime
   json j;
   json jsamples = json::array();

   boost::uuids::random_generator gen;
   boost::uuids::uuid u = gen();
   std::string sampleid = to_string(u);
   printf("CA7 : uuid %s.\n", sampleid.c_str());

   j[RTSMONFID] = sampleid;
   j[RTSMONID] = this->id;
   j[RTSMONNAME] = this->name;
   j[RTSMONSRATE] = this->samplerate;
   j[RTSMONSPERIOD] = this->sampleperiod;
   j[RTSMONDTIME] = this->downtime;
   j[RTSMONTIMESTAMP] = unix_timestamp;


   vRCMDeviceData* pDeviceData = (vRCMDeviceData*) values;
   uint32_t pacCount = pDeviceData->packageCount;
   uint32_t* packData = pDeviceData->data;
   uint32_t actNo = 0;

   json jsensors = json::array();
   for (auto &&sensDesc : this->mSenorDesriptions)
   {                     
      actNo = sensDesc->AddHW2Json(jsensors);
   }
   j[RTSMONHW] = jsensors;

   json jvalues = json::array();
   
   printf("CA7 : start writing data %s.\n", monpath.c_str());
   for (size_t actPacNo = 0; actPacNo <= pacCount; actPacNo++)
   {
      printf("CA7 : writing packing %d.\n", actPacNo);      
      //TODO ML: check that the maxNo is smaller than i
      while (actNo < pDeviceData->dataQuantity)
      {  
         //json jsample = json::object();
         
         if (this->mSenorDesriptions.empty()) {
            printf("CA7 : no sensor description.\n");
            actPacNo = pacCount;            
            break;
         }


         for (auto &&sensDesc : this->mSenorDesriptions)
         {                     
            //actNo = sensDesc->Add2Json(jsample, values, actNo, pDeviceData->dataQuantity);   
            actNo = sensDesc->AddValues2Json(jvalues, packData, actNo, pDeviceData->dataQuantity);
                                                   
         }
         // jsample[RTSMONVALUES] = jvalues;
         // jsamples.push_back(jsample);
         printf("CA7 : jsamples.push_back - actno[%d].\n", actNo);                  
      }
      //printf("CA7 : datpac written %d.\n", actPacNo);      
      //set the point to the new package
      int dlength = actPacNo * (3 + pDeviceData->dataQuantity);
      printf("CA7 : dlength = %d  - actPacNo = %d - dataQuantity = %d.\n", dlength, actPacNo, pDeviceData->dataQuantity);      
      pDeviceData = (vRCMDeviceData*) (values + dlength);
      packData = pDeviceData->data;
      actNo = 0;
   }


   //j[RTSMONSAMPLES] = jsamples;
   j[RTSMONVALUES] = jvalues;

   printf("CA7 : end writing data %s.\n", monpath.c_str());
   // write prettified JSON to another file
   std::ofstream o(filename);
   o << std::setw(4) << j << std::endl;
   return true;
}


/*
uint64_t timeSinceEpochMillisec() {
  using namespace std::chrono;
  return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
*/

/* 
                // Example of the very popular RFC 3339 format UTC time
                std::stringstream ss;
                std::time_t time = std::time({});
                char timeString[std::size("yyyy-mm-ddThh:mm:ssZ")];
                std::strftime(std::data(timeString), std::size(timeString),"%FT%TZ", std::gmtime(&time));

                ss << "/home/root/monfiles/" << timeString << ".txt";
                std::string filename = ss.str();
                printf("CA7 : filename %s.\n", filename.c_str());

                //save to file
                std::ofstream outfile (filename,std::ofstream::binary);
                // if (outfile.write (mByteBuffCpy,this->q_get_data_size.size)) 
                // {
                //     printf("CA7 : write file %s succesfully.\n", filename.c_str());                
                //     result = std::string("CA7 : write file %s succesfully.\n") + filename;
                // }
                // else {
                //     result = std::string("CA7 : file could not be written.\n") + filename;                
                //     printf("CA7 : file %s could not be written.\n", filename.c_str());                                
                // }
                outfile.close();
                printf("CA7 : file %s closed.\n", filename.c_str());

*/


/** old impl */
                //unsigned char* pData = (unsigned char*) this->mmappedData[i];
                // save a copy of 1st data
                //mByteBuffCpy[0] = *pData;

                // printf("CA7 : filename pointer:%p , pno:%d, pcount:%d, dataCount:%d .\n"
                //                                                                    , pData
                //                                                                    , pData[0].packageNo 
                //                                                                    , pData[0].packageCount
                //                                                                    , pData[0].dataQuantity);  

                // printf("CA7 : data[0]:%d , data[1]:%d data[2]:%d \n data[115]:%d , data[116]:%d data[117]:%d.\n"
                //                                                                    , pData[0].data[0]
                //                                                                    , pData[0].data[1]
                //                                                                    , pData[0].data[2]
                //                                                                    , pData[0].data[115]
                //                                                                    , pData[0].data[116]
                //                                                                    , pData[0].data[117]);  


                // int dlength = 1* (3 + pData->dataQuantity);
                // pData = (vRCMDeviceData*) (u32Buff + dlength);
                // printf("CA7 : u32Buff[%p]   - pData[%p] - dlength=%d \n", u32Buff , pData, dlength);                
                // printf("CA7 : filename pointer:%p , pno:%d, pcount:%d, dataCount:%d, data[0]:%d , data[1]:%d data[2]:%d , data[3]:%d , data[4]:%d data[5]:%d.\n"
                //                                                                    , pData
                //                                                                    , pData->packageNo 
                //                                                                    , pData->packageCount
                //                                                                    , pData->dataQuantity
                //                                                                    , pData->data[0]
                //                                                                    , pData->data[1]
                //                                                                    , pData->data[2]             
                //                                                                    , pData->data[3]
                //                                                                    , pData->data[4]
                //                                                                    , pData->data[5]);  


                // Example of the very popular RFC 3339 format UTC time
                // std::stringstream ss;
                // std::time_t time = std::time({});
                // char timeString[std::size("yyyy-mm-ddThh:mm:ssZ")];
                // std::strftime(std::data(timeString), std::size(timeString),"%FT%TZ", std::gmtime(&time));

                // ss << "/home/root/monfiles/" << timeString << ".txt";
                // std::string filename = ss.str();
                // printf("CA7 : filename %s.\n", filename.c_str());

                // //save to file
                // std::ofstream outfile (filename,std::ofstream::binary);
                // // if (outfile.write (mByteBuffCpy,this->q_get_data_size.size)) 
                // // {
                // //     printf("CA7 : write file %s succesfully.\n", filename.c_str());                
                // //     result = std::string("CA7 : write file %s succesfully.\n") + filename;
                // // }
                // // else {
                // //     result = std::string("CA7 : file could not be written.\n") + filename;                
                // //     printf("CA7 : file %s could not be written.\n", filename.c_str());                                
                // // }
                // outfile.close();
                //printf("CA7 : file %s closed.\n", filename.c_str());
