/**
   @file log.cpp

   @brief log library

   @author Miroslav Pivovarsky
   Contact: miroslav.pivovarsky@gmail.com

   @bug: no know bug
*/
#include "log.h"

Logs SystemLog(LOGS_FILE_PATH, LOGS_FILE_NAME, LOGS_FILE_MAX_SIZE);

/**
   @info Constructor
   @param none
   @return none
*/
Logs::Logs() {
  FileName = F("log.txt");
  FilePath = F("/");
  LogLevel = LogLevel_Verbose;
  FileMaxSize = 1024;
  NtpTimeSynced = false;
  LogMsg = "";
  LogMutex = xSemaphoreCreateMutex();
}

/**
 * @brief Construct a new Logs:: Logs object  with file path and file name
 * 
 * @param String - file path
 * @param String - file name
 */
Logs::Logs(String i_FilePath, String i_FileName) {
  FileName = i_FileName;
  FilePath = i_FilePath;
  LogLevel = LogLevel_Verbose;
  FileMaxSize = 1024;
  NtpTimeSynced = false;
  LogMsg = "";
  LogMutex = xSemaphoreCreateMutex();
}

/**
 * @brief Construct a new Logs:: Logs object with log level, file path and file name
 * 
 * @param LogLevel_enum - log level
 * @param String - file path
 * @param String - file name
 */
Logs::Logs(LogLevel_enum i_LogLevel, String i_FilePath, String i_FileName) {
  FileName = i_FileName;
  FilePath = i_FilePath;
  LogLevel = i_LogLevel;
  FileMaxSize = 1024;
  NtpTimeSynced = false;
  LogMsg = "";
  LogMutex = xSemaphoreCreateMutex();
}

/**
 * @brief Construct a new Logs:: Logs object with file path, file name and file size
 * 
 * @param String - file path
 * @param String - file name
 * @param uint16_t - file size
 */
Logs::Logs(String i_FilePath, String i_FileName, uint16_t i_FileSize) {
  FileName = i_FileName;
  FilePath = i_FilePath;
  LogLevel = LogLevel_Verbose;
  FileMaxSize = i_FileSize;
  NtpTimeSynced = false;
  LogMsg = "";
  LogMutex = xSemaphoreCreateMutex();
}

/**
 * @brief Construct a new Logs:: Logs object with log level, file path, file name and file size
 * 
 * @param LogLevel_enum - log level
 * @param String - file path
 * @param String - file name
 * @param uint16_t - file size
 */
Logs::Logs(LogLevel_enum i_LogLevel, String i_FilePath, String i_FileName, uint16_t i_FileSize) {
  FileName = i_FileName;
  FilePath = i_FilePath;
  LogLevel = i_LogLevel;
  FileMaxSize = i_FileSize;
  NtpTimeSynced = false;
  LogMsg = "";
  LogMutex = xSemaphoreCreateMutex();
}

/**
   @info Init library for logs
   @param LogLevel_enum - log level
   @param String - file path
   @param String - file name
   @return none
*/
void Logs::Init() {
  Serial.println(F("----------------------------------------------------------------"));
  Serial.println(F("Init Logs library"));

  /* init micro SD card */
#if (true == ENABLE_SD_CARD)
  InitSdCard();
  LogFileOpened = OpenFile(&LogFile, FilePath + FileName);

  if (true == GetCardDetectedStatus()) {
    /* check maximum log file size */
    CheckMaxLogFileSize();

    /* added first message to log file after start MCU */
    LogMsg = F("----------------------------------------------------------------\n");
    LogMsg += F("Start MCU!\nSW Version: ");
    LogMsg += String(SW_VERSION);
    LogMsg += F(" ,Build: ");
    LogMsg += String(SW_BUILD);
    LogMsg += "\n";
    LogMsg += F("Verbose mode: ");
    LogMsg += (true == CONSOLE_VERBOSE_DEBUG) ? "true" : "false";
    LogMsg += "\n";
    LogMsg += F("Log level: ");
    LogMsg += String(LogLevel);
    LogMsg += "\n";
    AppendFile(&LogFile, &LogMsg);

  } else {
    Serial.println(F("Micro-SD card not found! Disable logs"));
  }
#else
  Serial.println(F("Micro-SD card not enabled! Disable logs to card"));
  LogFileOpened = false;
#endif
}

/**
 * @brief Open log file
 * 
 */
void Logs::LogOpenFile() {
#if (true == ENABLE_SD_CARD)
  LogFileOpened = OpenFile(&LogFile, FilePath + FileName);
#endif
}

/**
 * @brief Close log file
 * 
 */
void Logs::LogCloseFile() {
#if (true == ENABLE_SD_CARD)
  CloseFile(&LogFile);
#endif
}

/**
 * @brief Function for check opened log file
 * 
 */
void Logs::LogCheckOpenedFile() { 
#if (true == ENABLE_SD_CARD)
  LogFileOpened = CheckOpenFile(&LogFile);
#endif
}

/**
   @info set log level
   @param LogLevel_enum - log level
   @return none
*/
void Logs::SetLogLevel(LogLevel_enum level) {
  LogLevel = level;
}

/**
   @info Add new log event
   @param LogLevel_enum - log level
   @param String - log message
   @param bool - new line
   @param bool - date
   @return none
*/
void Logs::AddEvent(LogLevel_enum level, String msg, bool newLine, bool date) {
  /* mutex for log */
  xSemaphoreTake(LogMutex, portMAX_DELAY);
  /* check log level */
  if (LogLevel >= level) {

    /* create log message */
    LogMsg = "";
    if (true == date) {
      LogMsg += GetSystemTime();
      LogMsg += " - ";
    }
    LogMsg += msg;
    if (true == newLine) {
      LogMsg += "\n";
    }

    /* print log message to console */
    Serial.print(LogMsg);
#if (true == ENABLE_SD_CARD)
    /* append log message to log file */
    if (true == LogFileOpened) {
      LogFileOpened = AppendFile(&LogFile, &LogMsg);
      if ((false == LogFileOpened) && (true == GetCardDetectedStatus())){
        LogCloseFile();
        LogOpenFile();
        if (true == LogFileOpened) {
          LogFileOpened = AppendFile(&LogFile, &LogMsg);
        } 
      }
    }
#endif
  }
#if (true == CONSOLE_VERBOSE_DEBUG)
  else {
    Serial.println(msg);
  }
#endif
  xSemaphoreGive(LogMutex);
}

/**
   @info Add new log event
   @param LogLevel_enum - log level
   @param const __FlashStringHelper - log message
   @param String - parameters
   @param bool - new line
   @param bool - date
   @return none
*/
void Logs::AddEvent(LogLevel_enum level, const __FlashStringHelper *msg, String parameters, bool newLine, bool date) {
  /* mutex for log */
  xSemaphoreTake(LogMutex, portMAX_DELAY);

  /* check log level */
  if (LogLevel >= level) {

    /* create log message */
    LogMsg = "";
    if (true == date) {
      LogMsg += GetSystemTime();
      LogMsg += " - ";
    }
    LogMsg += msg;
    LogMsg += parameters;
    if (true == newLine) {
      LogMsg += "\n";
    }

    /* print log message to console */
    Serial.print(LogMsg);

#if (true == ENABLE_SD_CARD)
    /* append log message to log file */
    if (true == LogFileOpened) {
      LogFileOpened = AppendFile(&LogFile, &LogMsg);
      if ((false == LogFileOpened) && (true == GetCardDetectedStatus())){
        LogCloseFile();
        LogOpenFile();
        if (true == LogFileOpened) {
          AppendFile(&LogFile, &LogMsg);
        } 
      }
    }
#endif
  }
#if (true == CONSOLE_VERBOSE_DEBUG)
  else {
    Serial.println(msg);
  }
#endif

  /* release mutex */
  xSemaphoreGive(LogMutex);
 }

/**
   @info Set file name
   @param String - file name
   @return none
*/
void Logs::SetFileName(String i_data) {
  FileName = i_data;
}

/**
   @info Set file path
   @param String - file path
   @return none
*/
void Logs::SetFilePath(String i_data) {
  FilePath = i_data;
}

/**
   @info Set file max size
   @param uint16_t - file max size
   @return none
*/
void Logs::SetFileMaxSize(uint16_t i_data) {
  FileMaxSize = i_data;
}

/**
   @info Set NTP time synced
   @param bool - NTP time synced
   @return none
*/
void Logs::SetNtpTimeSynced(bool i_data) {
  NtpTimeSynced = i_data;
  AddEvent(LogLevel_Info, F("System time: "), GetSystemTime());
}

/**
   @info Get file name
   @param none
   @return String - file name
*/
String Logs::GetFileName() {
  return FileName;
}

/**
   @info Get file path
   @param none
   @return String - file path
*/
String Logs::GetFilePath() {
  return FilePath;
}

/**
   @info Get log level
   @param none
   @return LogLevel_enum - log level
*/
LogLevel_enum Logs::GetLogLevel() {
  return LogLevel;
}

/**
   @info Get NTP time synced
   @param none
   @return bool - NTP time synced
*/
bool Logs::GetNtpTimeSynced() {
  return NtpTimeSynced;
}

/**
   @info Check maximum log file size
   @param none
   @return none
*/
void Logs::CheckMaxLogFileSize() {
#if (true == ENABLE_SD_CARD)
  uint32_t FileSize = GetFileSize(SD_MMC, FilePath + FileName);
  AddEvent(LogLevel_Verbose, F("Log file size: "), String(FileSize) + "/" + String(LOGS_FILE_MAX_SIZE) + " B");

  if (FileSize >= LOGS_FILE_MAX_SIZE) {
    uint16_t file_count = FileCount(SD_MMC, FilePath, FileName);
    AddEvent(LogLevel_Info, F("Maximum log file size. File count: "), String(file_count));
    LogCloseFile();
    RenameFile(SD_MMC, FilePath + FileName, FilePath + FileName + String(file_count));
    LogOpenFile();
  }
#endif
}

void Logs::CheckCardSpace() {
#if (true == ENABLE_SD_CARD)
  CheckCardUsedStatus();
  AddEvent(LogLevel_Verbose, "Card size: " + String(GetCardSizeMB()), + " MB, Used: " + String(GetCardUsedMB()) + " MB, Free: " + String(GetCardUsedMB()) + " MB");
#endif
}

/**
   @info Get log file opened
   @param none
   @return bool - log file opened
*/
bool Logs::GetLogFileOpened() {
  return LogFileOpened;
}

/**
   @info Get system time
   @param none
   @return String - time
*/
String Logs::GetSystemTime() {
  String ret = "0000-00-00_00-00-00";
  if (true == NtpTimeSynced) {
    struct tm timeinfo;
    if (!getLocalTime(&timeinfo)) {
#if (true == CONSOLE_VERBOSE_DEBUG)
      Serial.println(F("Failed to obtain time"));
#endif
      return ret;
    }

    char timeString[20];
    strftime(timeString, sizeof(timeString), "%Y-%m-%d_%H-%M-%S", &timeinfo);
#if (true == CONSOLE_VERBOSE_DEBUG)
    Serial.println(timeString);
#endif
    ret = String(timeString);
  }
  return ret;
}

/* EOF */