/* Blinker
   Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
   a blinkenarea.org project */

#include <list>
#include <string>

#include <BlinkenLib/BlinkenFrame.h>

#include "Canvas.h"
#include "CanvasInput.h"
#include "Directory.h"
#include "File.h"
#include "Format.h"
#include "FormatFile.h"
#include "Mgrs.h"
#include "Module.h"
#include "ListTracker.h"
#include "ListTracker_impl.h"
#include "OutStreamFile.h"
#include "StreamRecv.h"

namespace Blinker {

/**
 * @brief constructor
 * @param[in] name module name
 * @param[in] mgrs managers
 * @param[in] dirBase base directory
 */
Canvas::Canvas(const std::string &name, Mgrs &mgrs, const Directory &dirBase):
  Module(name, mgrs, dirBase),
  m_fileFormat(dirBase.getFile("format")),
  m_fileOutStream(dirBase.getFile("outstream"), mgrs.m_streamMgr),
  m_pCanvas(NULL),
  m_canvasHasFrame(false),
  m_inListTracker(*this, dirBase.getSubdir("inputs"))
{
  // set up
  createCanvas();
  m_inListTracker.init();
}

/// virtual destructor
Canvas::~Canvas()
{
  // clean up
  m_inListTracker.clear();
  destroyCanvas();
}

/// check for update of configuration
void Canvas::updateConfig()
{
  // format file was modified -> re-create canvas
  if (m_fileFormat.checkModified()) {
    createCanvas();
  }

  // input list update
  m_inListTracker.updateConfig();

  // output stream name file was modified -> re-get output stream
  if (m_fileOutStream.checkModified())
    m_fileOutStream.update();
}

/// (re-)create canvas
void Canvas::createCanvas()
{
  // get rid of old canvas
  destroyCanvas();

  // read format from format file
  m_fileFormat.update();
  if (!m_fileFormat.m_valid)
    return;

  // create frame
  m_pCanvas = BlinkenFrameNew(m_fileFormat.m_obj.m_height,
                              m_fileFormat.m_obj.m_width,
                              m_fileFormat.m_obj.m_channels,
                              m_fileFormat.m_obj.m_maxval, 1);
  m_canvasHasFrame = false;
}

/// tear down canvas
void Canvas::destroyCanvas()
{
  if (m_pCanvas) {
    BlinkenFrameFree(m_pCanvas);
    m_pCanvas = NULL;
    m_canvasHasFrame = false;
  }
}

/// notfication to redraw (called by inputs)
void Canvas::redraw()
{
  // do nothing if there is no canvas
  if (!m_pCanvas)
    return;

  // black background
  BlinkenFrameClear(m_pCanvas);
  m_canvasHasFrame = false; // no frame on canvas yet

  // tell all inputs to draw on canvas
  InListTracker::ListIt itIn;
  for (itIn = m_inListTracker.m_list.begin();
       itIn != m_inListTracker.m_list.end(); ++itIn)
    if (itIn->m_pObj->draw())
      m_canvasHasFrame = true; // drawing successful -> there is a frame now

  // send current frame to stream
  sendFrame();
}

/// send current frame to output stream
void Canvas::sendFrame()
{
  // frame available -> send it
  if (m_canvasHasFrame)
    m_fileOutStream.setFrame(m_pCanvas);
  // no frame available -> send this information
  else
    m_fileOutStream.setFrame(NULL);
}

} // namespace Blinker

