/*
 * testLogging.cc
 *
 *  Created on: Apr 11, 2018
 *      Author: zenker
 */

//#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE ProcessModuleTest

#include <fstream>

#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/test/included/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/thread.hpp>

#include "Logging.h"
using namespace logging;

#include "ChimeraTK/ApplicationCore/TestFacility.h"

#include <boost/test/unit_test.hpp>
using namespace boost::unit_test_framework;

/**
 * Define a test app to test the SoftwareMasterModule.
 */
struct testApp : public ChimeraTK::Application {
  testApp() : Application("test"), fileCreated(false){ }
  ~testApp() {
    shutdown();
    if(fileCreated){
      BOOST_CHECK_EQUAL(boost::filesystem::remove("/tmp/testLogging/test.log"), true);
      BOOST_CHECK_EQUAL(boost::filesystem::remove("/tmp/testLogging/"), true);
    }
  }

  LoggingModule log { this, "LoggingModule",
      "LoggingModule test" };

  Logger logger{&log};

  ChimeraTK::ControlSystemModule cs;

  void defineConnections() override{
    cs("targetStream") >> log.targetStream;
    cs("logLevel") >> log.logLevel;
    cs("logFile") >> log.logFile;
    cs("tailLength") >> log.tailLength;

    log.addSource(&logger);
    log.findTag("CS").connectTo(cs);

   }

  bool fileCreated;
};

BOOST_AUTO_TEST_CASE( testLogMsg) {
  testApp app;
  ChimeraTK::TestFacility tf;
  tf.runApplication();
  auto tailLength = tf.getScalar<uint>("tailLength");
  app.logger.sendMessage("test", LogLevel::DEBUG);
  tailLength = 1;
  tailLength.write();
  tf.stepApplication();
  std::string ss  = (std::string)tf.readScalar<std::string>("LogTail");
  BOOST_CHECK_EQUAL(ss.substr(ss.find("->")+3), std::string("test\n"));
}

BOOST_AUTO_TEST_CASE( testLogfileFails) {
  testApp app;
  ChimeraTK::TestFacility tf;

  auto logFile = tf.getScalar<std::string>("logFile");
  tf.runApplication();
  logFile = std::string("/tmp/testLogging/test.log");
  logFile.write();
  // message not considered here but used to step through the application
  app.logger.sendMessage("test", LogLevel::DEBUG);
  tf.stepApplication();
  std::string ss  = (std::string)tf.readScalar<std::string>("LogTail");
  std::vector<std::string> strs;
  boost::split(strs, ss, boost::is_any_of("\n"), boost::token_compress_on);
  BOOST_CHECK_EQUAL(strs.at(2).substr(strs.at(2).find("->")+3), std::string("Failed to open log file for writing: /tmp/testLogging/test.log"));
}

BOOST_AUTO_TEST_CASE( testLogfile) {
  testApp app;
  ChimeraTK::TestFacility tf;

  auto logFile = tf.getScalar<std::string>("logFile");

  if(!boost::filesystem::is_directory("/tmp/testLogging/"))
    boost::filesystem::create_directory("/tmp/testLogging/");
  tf.runApplication();
  logFile = std::string("/tmp/testLogging/test.log");
  logFile.write();
  // message not considered here but used to step through the application
  app.logger.sendMessage("test", LogLevel::DEBUG);
  tf.stepApplication();
  std::fstream file;
  file.open("/tmp/testLogging/test.log");
  BOOST_CHECK_EQUAL(file.good(), true);
  if(file.good())
    app.fileCreated = true;
  std::string line;

  std::getline(file, line);
  BOOST_CHECK_EQUAL(line.substr(line.find("->")+3), std::string("Opened log file for writing: /tmp/testLogging/test.log"));
  std::getline(file, line);
  BOOST_CHECK_EQUAL(line.substr(line.find("->")+3), std::string("test"));
}

BOOST_AUTO_TEST_CASE( testLogging) {
  testApp app;
  ChimeraTK::TestFacility tf;

  auto logLevel = tf.getScalar<uint>("logLevel");
  auto tailLength = tf.getScalar<uint>("tailLength");

  logLevel = 0;
  logLevel.write();
  tailLength = 2;
  tailLength.write();

  tf.runApplication();
  app.logger.sendMessage("1st test message", LogLevel::DEBUG);
  tf.stepApplication();
  app.logger.sendMessage("2nd test message", LogLevel::DEBUG);
  tf.stepApplication();
  auto tail = tf.readScalar<std::string>("LogTail");
  std::vector< std::string > result;
  boost::algorithm::split(result, tail, boost::is_any_of("\n"));
  // result length should be 3 not 2, because new line is used to split, which results in 3 items although there are only two messages.
  BOOST_CHECK_EQUAL(result.size(), 3);

  /**** Test log level ****/
  logLevel = 2;
  logLevel.write();
  app.logger.sendMessage("3rd test message", LogLevel::DEBUG);
  tf.stepApplication();
  tail = tf.readScalar<std::string>("LogTail");
  boost::algorithm::split(result, tail, boost::is_any_of("\n"));
  // should still be 3 because log level was too low!
  BOOST_CHECK_EQUAL(result.size(), 3);

  /**** Test tail length ****/
  app.logger.sendMessage("4th test message", LogLevel::ERROR);
  tailLength = 3;
  tailLength.write();
  tf.stepApplication();
  tail = tf.readScalar<std::string>("LogTail");
  boost::algorithm::split(result, tail, boost::is_any_of("\n"));
  BOOST_CHECK_EQUAL(result.size(), 4);

  app.logger.sendMessage("5th test message", LogLevel::ERROR);
  tf.stepApplication();
  tail = tf.readScalar<std::string>("LogTail");
  boost::algorithm::split(result, tail, boost::is_any_of("\n"));
  // should still be 4 because tailLength is 3!
  BOOST_CHECK_EQUAL(result.size(), 4);
}