/* 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 "Directory.h"
#include "File.h"
#include "ListTracker.h"
#include "ListTracker_impl.h"
#include "Mgrs.h"
#include "Module.h"
#include "Priority.h"
#include "PriorityInput.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
 */
Priority::Priority(const std::string &name, Mgrs &mgrs,
                   const Directory &dirBase):
  Module(name, mgrs, dirBase),
  m_fileOutStream(dirBase.getFile("outstream"), mgrs.m_streamMgr),
  m_inListTracker(*this, dirBase.getSubdir("inputs")),
  m_itCurIn(m_inListTracker.m_list.rend()),
  m_updateNeeded(false)
{
  m_inListTracker.init();
  updateSelection();
}

/// virtual destructor
Priority::~Priority()
{
  m_inListTracker.clear();
}

/// check for update of configuration
void Priority::updateConfig()
{
  // input list update
  m_inListTracker.updateConfig();
  updateSelection();

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

/**
 * @brief select specific input (called by inputs)
 * @param[in] input input to select
 */
void Priority::select(const Input *pInput)
{
  // search inputs for passed input
  m_itCurIn = m_inListTracker.m_list.rbegin();
  while (m_itCurIn != m_inListTracker.m_list.rend() &&
         m_itCurIn->m_pObj != pInput)
    ++m_itCurIn;

  // send current frame
  curFrame();

  // return if required stream was successfully selected
  if (m_itCurIn == m_inListTracker.m_list.rend() ||
      m_itCurIn->m_pObj != pInput)
    m_updateNeeded = true;
}

/// select lower priority input (called by inputs)
void Priority::selectLower()
{
  stBlinkenFrame *pFrame;

  // search inputs with lower priority until a frame is found
  while (m_itCurIn != m_inListTracker.m_list.rend()) {
    // check for frame
    if ((pFrame = m_itCurIn->m_pObj->getCurFrame())) {
      // frame found -> keep this input as current, send frame
      m_fileOutStream.setFrame(pFrame);
      return;
    }
    // try next input
    ++m_itCurIn;
  }

  // no input has got a frame -> send no frame
  m_fileOutStream.setFrame(NULL);
}

/// update current selection
void Priority::updateSelection()
{
  if (m_updateNeeded) {
    m_updateNeeded = false;

    // search for highest priority input with a frame
    for (m_itCurIn = m_inListTracker.m_list.rbegin();
         m_itCurIn != m_inListTracker.m_list.rend(); ++m_itCurIn)
      if (m_itCurIn->m_pObj->getCurFrame())
        break;

    // send new frame to output stream
    curFrame();

  } // if m_updateNeeded
}

/// send current frame to output stream
void Priority::curFrame()
{
  stBlinkenFrame *pFrame;

  // current input available and frame it it available -> send frame
  if (m_itCurIn != m_inListTracker.m_list.rend() &&
      (pFrame = m_itCurIn->m_pObj->getCurFrame()))
    m_fileOutStream.setFrame(pFrame);
  // no frame available -> send this information
  else
    m_fileOutStream.setFrame(NULL);
}

} // namespace Blinker

