/* 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 "Lock.h"
#include "LockMgr.h"
#include "LockNameFile.h"
#include "NameFile.h"

namespace Blinker {

/**
 * @brief constructor
 * @param[in] file basic file object to treat as lock name file
 * @param[in] lockMgr lock manager
 */
LockNameFile::LockNameFile(const File &file, LockMgr &lockMgr):
  m_nameFile(file),
  m_lockMgr(lockMgr),
  m_refLock(nullptr),
  m_takenLock(nullptr)
{
  update();
}

/// destructor
LockNameFile::~LockNameFile()
{
  release();
}

/// update lock name if file has been modified
void LockNameFile::updateIfModified()
{
  if (m_nameFile.checkModified()) {
    update();
  }
}

/**
 * @brief check if lock is free,
 *        i.e., no lock configured (i.e. no lock file),
 *        lock not locked or taken by this object
 * @return whether lock is free
 */
bool LockNameFile::isfree() const
{
  return m_takenLock != nullptr ||
         m_refLock == nullptr ||
         ! m_refLock->islocked();
}

/**
 * @brief take the lock,
 *        success if no lock configured (i.e. no lock file),
 *        lock was not locked and could be taken by this object
 *        or lock was already taken by this object
 * @return whether lock could be taken
 */
bool LockNameFile::take()
{
  if (m_takenLock != nullptr) {
    return true; // already taken
  }
  if (m_refLock == nullptr) {
    return true; // no lock configured
  }
  if (! m_refLock->trylock()) {
    return false; // did not get lock
  }
  // remember pointer to taken lock
  // (important if lock name changes while taken)
  m_takenLock = m_refLock;
  return true; // lock taken
}

/// release taken lock
void LockNameFile::release()
{
  if (m_takenLock != nullptr) {
    m_takenLock->unlock();
    m_takenLock = nullptr;
  }
}

/// update pointer to referenced lock based on name file contents
void LockNameFile::update()
{
  m_nameFile.update();
  m_refLock = nullptr; // forget old referenced lock
  if (! m_nameFile.m_valid) {
    return; // no lock referenced
  }
  // get new referenced lock
  m_refLock = &m_lockMgr.getLock(m_nameFile.m_obj.m_str);
}

} // namespace Blinker

