void SimulationAnimationPatch::update(set<Device *> devices, 
	IsUpdateRequiredFunction isUpdateRequired,
	InterruptFunction interruptRender,
	ClearUpdateFlagsFunction clearUpdateFlags) {
	// Doesn't respond if it's stopped.
    if (m_mode == SimulationAnimationMode::STOPPED)
        return ;
    
    FrameDeviceInfo frame;
	
	// Fulls time and mode for frame info.
	createFrameInfoHeader(frame);

    // Checks if an update is needed.
    bool rerender_req = isUpdateRequired(devices);

    // There's no need to send this frame
    if (!rerender_req)
        return ;
    
	createFrameInfoBody(devices, frame);

    std::stringstream ss;
    ss << "Sent new frame: " << frame.time << "(" << frame.mode << ")";
    Logger::log(LDEBUG, ss.str());

	// Interrupts the current rendering if in the interactive mode.
    if (m_mode == SimulationAnimationMode::INTERACTIVE ||
		m_mode == SimulationAnimationMode::RECORDING) {
		interruptRender();
    }
    
	// Enqueues.
	enqueueFrameInfo(frame);

	// A flag cleared means the task has been indeed inserted
	clearUpdateFlags();
}
void ArnoldAnimationPatch::renderSingleFrameToBuffer(const set<Device*>& devices, unsigned char* buff, int w, int h) {
  if (m_mode != SimulationAnimationMode::STOPPED)
    disableContinuousRenderMode();

  FrameDeviceInfo frame;

  // Fulls time and mode for frame info.
  createFrameInfoHeader(frame);
  createFrameInfoBody(devices, frame, true);

  std::stringstream ss;
  ss << "Rendering single frame: " << frame.time;
  Logger::log(LDEBUG, ss.str());

  // Interrupts the current rendering if in the interactive mode.
  if (m_mode == SimulationAnimationMode::INTERACTIVE ||
    m_mode == SimulationAnimationMode::RECORDING) {
    interruptRender();
  }

  // Render immediately.
  if (!m_interface->isDistributedOpen()) {
	  m_interface->init(this->toJSON());

	  // Check if we were able to open a connection
	  if (!m_interface->isDistributedOpen()) {
		  std::cerr << "Connection could not be established. " <<
			  "Check to make sure your host and port " <<
			  "parameters are correct and that nobody " <<
			  "else is currently using the remote renderer" << std::endl;

		  return;
	  }
  }

  bool success = false;
  float* bp = nullptr;
  int cid;

  if (CachingArnoldInterface* ci = dynamic_cast<CachingArnoldInterface*>(m_interface)) {
#ifdef USE_ARNOLD
    success = ci->render(devices, w, h, cid) == AI_SUCCESS;
#else
    success = ci->render(devices, w, h, cid) == 0;
#endif
    // we need to pull the proper buffer from the right context
    bp = ci->getBufferForContext(cid);
    frame.clear();
  }
  else {
    updateLight(devices);
    success = ArnoldPatch::renderLoop(devices);
    frame.clear();
    bp = getBufferPointer();
  }

  if (success) {
    for (int j = 0; j < h; j++) {
      for (int i = 0; i < w; i++) {
        int offset = (j * w + i) * 4;

        // convert to bytes
        buff[offset] = static_cast<unsigned char>(bp[offset + 2] * 0xff);
        buff[offset + 1] = static_cast<unsigned char>(bp[offset + 1] * 0xff);
        buff[offset + 2] = static_cast<unsigned char>(bp[offset + 0] * 0xff);
        buff[offset + 3] = static_cast<unsigned char>(bp[offset + 3] * 0xff);
      }
    }
  }

  if (CachingArnoldInterface* ci = dynamic_cast<CachingArnoldInterface*>(m_interface)) {
    ci->closeContext(cid);
  }
}
void ArnoldAnimationPatch::renderSingleFrame(const set<Device*>& devices, string basepath, string filename) {
  if (m_mode != SimulationAnimationMode::STOPPED)
    disableContinuousRenderMode();

  FrameDeviceInfo frame;

  // Fulls time and mode for frame info.
  createFrameInfoHeader(frame);
  createFrameInfoBody(devices, frame);

  std::stringstream ss;
  ss << "Rendering single frame: " << frame.time;
  Logger::log(LDEBUG, ss.str());

  // Interrupts the current rendering if in the interactive mode.
  if (m_mode == SimulationAnimationMode::INTERACTIVE ||
    m_mode == SimulationAnimationMode::RECORDING) {
    interruptRender();
  }

  // Render immediately.
  if (!m_interface->isDistributedOpen()) {
	  m_interface->init(this->toJSON());
	  
	  // Check if we were able to open a connection
	  if (!m_interface->isDistributedOpen()) {
		  std::cerr << "Connection could not be established. " << 
			  "Check to make sure your host and port " << 
			  "parameters are correct and that nobody " << 
			  "else is currently using the remote renderer" << std::endl;

		  return;
	  }
  }

  bool success = false;
  float* bp = nullptr;
  int cid;
  int w = getWidth();
  int h = getHeight();

  if (CachingArnoldInterface* ci = dynamic_cast<CachingArnoldInterface*>(m_interface)) {
#ifdef USE_ARNOLD
    success = ci->render(devices, w, h, cid) == AI_SUCCESS;
#else
    success = ci->render(devices, w, h, cid) == 0;
#endif
    // we need to pull the proper buffer from the right context
    bp = ci->getBufferForContext(cid);
    frame.clear();
  }
  else {
    updateLight(devices);
    success = ArnoldPatch::renderLoop(devices);
    frame.clear();
    bp = getBufferPointer();
  }

  if (success) {
    unsigned char *bytes = new unsigned char[w * h * 4];
    floats_to_bytes(bytes, bp, w, h);

    string file = basepath + "/" + filename + ".png";

    if (!imageio_save_image(file.c_str(), bytes, getWidth(), getHeight())) {
      std::stringstream err_ss;
      err_ss << "Error to write png: " << filename;
      Logger::log(ERR, err_ss.str());
    }
  }

  if (CachingArnoldInterface* ci = dynamic_cast<CachingArnoldInterface*>(m_interface)) {
    ci->closeContext(cid);
  }
}