/*
 * Deskflow -- mouse and keyboard sharing utility
 * Copyright (C) 2012-2016 Symless Ltd.
 * Copyright (C) 2002 Chris Schoeneman
 *
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 *
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "base/log_outputters.h"
#include "arch/Arch.h"
#include "base/Path.h"
#include "base/TMethodJob.h"

#include <fstream>

enum EFileLogOutputter
{
  kFileSizeLimit = 1024 // kb
};

//
// StopLogOutputter
//

StopLogOutputter::StopLogOutputter()
{
  // do nothing
}

StopLogOutputter::~StopLogOutputter()
{
  // do nothing
}

void StopLogOutputter::open(const char *)
{
  // do nothing
}

void StopLogOutputter::close()
{
  // do nothing
}

void StopLogOutputter::show(bool)
{
  // do nothing
}

bool StopLogOutputter::write(ELevel, const char *)
{
  return false;
}

//
// ConsoleLogOutputter
//

ConsoleLogOutputter::ConsoleLogOutputter()
{
}

ConsoleLogOutputter::~ConsoleLogOutputter()
{
}

void ConsoleLogOutputter::open(const char *title)
{
  ARCH->openConsole(title);
}

void ConsoleLogOutputter::close()
{
  ARCH->closeConsole();
}

void ConsoleLogOutputter::show(bool showIfEmpty)
{
  ARCH->showConsole(showIfEmpty);
}

bool ConsoleLogOutputter::write(ELevel level, const char *msg)
{
  ARCH->writeConsole(level, msg);
  return true;
}

void ConsoleLogOutputter::flush()
{
}

//
// SystemLogOutputter
//

SystemLogOutputter::SystemLogOutputter()
{
  // do nothing
}

SystemLogOutputter::~SystemLogOutputter()
{
  // do nothing
}

void SystemLogOutputter::open(const char *title)
{
  ARCH->openLog(title);
}

void SystemLogOutputter::close()
{
  ARCH->closeLog();
}

void SystemLogOutputter::show(bool showIfEmpty)
{
  ARCH->showLog(showIfEmpty);
}

bool SystemLogOutputter::write(ELevel level, const char *msg)
{
  ARCH->writeLog(level, msg);
  return true;
}

//
// SystemLogger
//

SystemLogger::SystemLogger(const char *title, bool blockConsole) : m_stop(NULL)
{
  // redirect log messages
  if (blockConsole) {
    m_stop = new StopLogOutputter;
    CLOG->insert(m_stop);
  }
  m_syslog = new SystemLogOutputter;
  m_syslog->open(title);
  CLOG->insert(m_syslog);
}

SystemLogger::~SystemLogger()
{
  CLOG->remove(m_syslog);
  delete m_syslog;
  if (m_stop != NULL) {
    CLOG->remove(m_stop);
    delete m_stop;
  }
}

//
// BufferedLogOutputter
//

BufferedLogOutputter::BufferedLogOutputter(UInt32 maxBufferSize) : m_maxBufferSize(maxBufferSize)
{
  // do nothing
}

BufferedLogOutputter::~BufferedLogOutputter()
{
  // do nothing
}

BufferedLogOutputter::const_iterator BufferedLogOutputter::begin() const
{
  return m_buffer.begin();
}

BufferedLogOutputter::const_iterator BufferedLogOutputter::end() const
{
  return m_buffer.end();
}

void BufferedLogOutputter::open(const char *)
{
  // do nothing
}

void BufferedLogOutputter::close()
{
  // remove all elements from the buffer
  m_buffer.clear();
}

void BufferedLogOutputter::show(bool)
{
  // do nothing
}

bool BufferedLogOutputter::write(ELevel, const char *message)
{
  while (m_buffer.size() >= m_maxBufferSize) {
    m_buffer.pop_front();
  }
  m_buffer.push_back(String(message));
  return true;
}

//
// FileLogOutputter
//

FileLogOutputter::FileLogOutputter(const char *logFile)
{
  setLogFilename(logFile);
}

FileLogOutputter::~FileLogOutputter()
{
}

void FileLogOutputter::setLogFilename(const char *logFile)
{
  assert(logFile != NULL);
  m_fileName = logFile;
}

bool FileLogOutputter::write(ELevel level, const char *message)
{
  bool moveFile = false;

  std::ofstream m_handle;
  m_handle.open(deskflow::filesystem::path(m_fileName), std::fstream::app);
  if (m_handle.is_open() && m_handle.fail() != true) {
    m_handle << message << std::endl;

    // when file size exceeds limits, move to 'old log' filename.
    size_t p = m_handle.tellp();
    if (p > (kFileSizeLimit * 1024)) {
      moveFile = true;
    }
  }
  m_handle.close();

  if (moveFile) {
    String oldLogFilename = deskflow::string::sprintf("%s.1", m_fileName.c_str());
    remove(oldLogFilename.c_str());
    rename(m_fileName.c_str(), oldLogFilename.c_str());
  }

  return true;
}

void FileLogOutputter::open(const char *title)
{
}

void FileLogOutputter::close()
{
}

void FileLogOutputter::show(bool showIfEmpty)
{
}
