#include "writer.h"
#include <nix.hpp>
#include <chrono>


void Writer::setVideoSpecs(VideoSpecs specs) {
  videoSpecs = specs;
  valid = true;
}

void Writer::run() {
  size_t count = 0;
  size_t chunksize = 256;

  if (valid) {
      stop_request = false;
      stopNow = false;
      Pylon::CVideoWriter videoWriter;
      videoWriter.SetParameter(videoSpecs.width, videoSpecs.height, videoSpecs.pixelType,
                               videoSpecs.fps, videoSpecs.quality);
      videoWriter.Open(videoSpecs.filename.c_str());
      nix::File nix_file =nix::File::open(videoSpecs.filename + ".nix", nix::FileMode::Overwrite, "hdf5", nix::Compression::DeflateNormal);
      nix::Block b = nix_file.createBlock("Recording", "nix.recording");
      nix::Section s = nix_file.createSection("Recording", "nix.recording");
      b.metadata(s);
      nix::Value v(nix::util::timeToStr(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())));
      s.createProperty("date", v);

      nix::Value fn(videoSpecs.filename);
      s.createProperty("moviefile", fn);
      nix::Section sw_sec = s.createSection("PylonRecorder", "nix.software");
      sw_sec.createProperty("version", nix::Value(1));

      nix::Section hw_sec = s.createSection("Basler ACA2040-120um", "nix.hardware.camera");
      hw_sec.createProperty("type", nix::Value("monochrome"));
      hw_sec.createProperty("manufacturer", nix::Value("Basler AG"));
      nix::Property p = hw_sec.createProperty("framerate", nix::Value(static_cast<int>(videoSpecs.fps)));
      p.unit("Hz");

      nix::NDSize initial_shape(1, chunksize);
      nix::DataArray frametimes = b.createDataArray("frametimes", "nix.imaging.frametimes", nix::DataType::String, initial_shape);
      frametimes.label("time");
      frametimes.appendSetDimension();
      nix::DataArray frameindices = b.createDataArray("frameindex", "nix.imaging.frameid", nix::DataType::Int64, initial_shape);
      frametimes.appendSetDimension();

      std::vector<std::string> stamps_buffer(chunksize);
      std::vector<int64_t> ids_buffer(chunksize);

      nix::NDSize offset(1, 0);
      nix::NDSize current_shape(initial_shape);
      nix::NDSize chunk_shape(1, chunksize);
      MyImage img;
      while ((!stop_request || buffer->bufferLoad() > 0) && !stopNow) {
          if (buffer->bufferLoad() > 0 ) {
              bool valid = buffer->pop(img);
              if (valid) {
                  Pylon::CPylonImage pyImage;
                  try {
                    pyImage.AttachUserBuffer(img.data(), videoSpecs.width * videoSpecs.height, videoSpecs.pixelType, videoSpecs.width, videoSpecs.height, 0, videoSpecs.orientation);
                    videoWriter.Add( pyImage );
                  } catch (const Pylon::GenericException &e) {
                    std::cerr << "Writer::run: An exception occurred." << std::endl << e.GetDescription() << std::endl;
                  }
                  if (count < chunksize) {
                      stamps_buffer[count] = nix::util::timeToStr(img.timestamp());
                      ids_buffer[count] = img.index();
                      count ++;
                  } else {
                      frametimes.setData(nix::DataType::String, stamps_buffer.data(), chunk_shape, offset);
                      frameindices.setData(nix::DataType::Int64, ids_buffer.data(), chunk_shape, offset);
                      current_shape += initial_shape;
                      frametimes.dataExtent(current_shape);
                      frameindices.dataExtent(current_shape);
                      offset[0] += chunksize;
                      count = 0;
                   }
                }
            } else {
              while (buffer->bufferLoad() < 1 && !stop_request) {
                  msleep(10);
                }
            }
        }
        if (count > 0) {
            chunk_shape[0] = count;
            frametimes.setData(nix::DataType::String, stamps_buffer.data(), chunk_shape, offset);
            frameindices.setData(nix::DataType::Int64, ids_buffer.data(), chunk_shape, offset);
        }
        videoWriter.Close();
        nix_file.close();
    } else {
      std::cerr << "Got no video specifications, not writing!" << std::endl;
    }
  emit writingDone();
}