/**
 * @file    smart.cpp
 * @brief   read S.M.A.R.T values
 * @author  hendrik schutter
 * @date    01.05.2020
 */

#include "../include/reHDD.h"

string SMART::modelFamily;
string SMART::modelName;
string SMART::serial;
uint64_t SMART::capacity = 0U;
uint32_t SMART::errorCount = 0U;
uint32_t SMART::powerOnHours = 0U;
uint32_t SMART::powerCycle = 0U;
uint32_t SMART::temperature = 0U;

/**
 * \brief   get and set S.M.A.R.T. values in Drive
 * \param	pointer of Drive instance
 * \return  void
 */
void SMART::readSMARTData(Drive *drive)
{
    modelFamily.clear();
    modelName.clear();
    serial.clear();
    capacity = 0U;
    errorCount = 0U;
    powerOnHours = 0U;
    powerCycle = 0U;
    temperature = 0U;

    string sSmartctlCommands[] = {" --json -a ", " --json -d sntjmicron -a ", " --json -d sntasmedia -a ", " --json -d sntrealtek -a "};

    for (string sSmartctlCommand : sSmartctlCommands)
    {
        string sCMD = ("smartctl");
        sCMD.append(sSmartctlCommand);
        sCMD.append(drive->getPath());
        const char *cpComand = sCMD.c_str();

        FILE *outputfileSmart = popen(cpComand, "r");
        size_t len = 0;     // length of found line
        char *cLine = NULL; // found line
        uint8_t status = 255;

        while ((getline(&cLine, &len, outputfileSmart)) != -1)
        {
            string sLine = string(cLine);

            status = SMART::parseExitStatus(sLine);
            SMART::parseModelFamily(sLine);
            SMART::parseModelName(sLine);
            SMART::parseSerial(sLine);
            SMART::parseCapacity(sLine);
            SMART::parseErrorCount(sLine);
            SMART::parsePowerOnHours(sLine);
            SMART::parsePowerCycle(sLine);
            SMART::parseTemperature(sLine);
        }

        pclose(outputfileSmart);

        if (status == 0U)
        {
            // Found S.M.A.R.T. data with this command
            break;
        }
    }

    drive->setDriveSMARTData(modelFamily, modelName, serial, capacity, errorCount, powerOnHours, powerCycle, temperature); // wirte data in drive
}

/**
 * \brief   parse ExitStatus
 * \param	string output line of smartctl
 * \return  uint_8 exit status
 */
uint8_t SMART::parseExitStatus(string sLine)
{
    uint8_t exitStatus = -1;
    string search("\"exit_status\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 1);
        exitStatus = stol(sLine);
    }
    return exitStatus;
}

/**
 * \brief   parse ModelFamiliy
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parseModelFamily(string sLine)
{
    string search("\"model_family\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 3);
        sLine.erase(sLine.length() - 3, 3);
        modelFamily = sLine;
    }
}

/**
 * \brief   parse ModelName
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parseModelName(string sLine)
{
    string search("\"model_name\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 3);
        sLine.erase(sLine.length() - 3, 3);
        modelName = sLine;
    }
}

/**
 * \brief   parse Serial
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parseSerial(string sLine)
{
    string search("\"serial_number\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 3);
        sLine.erase(sLine.length() - 3, 3);
        serial = sLine;
    }
}

/**
 * \brief   parse Capacity
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parseCapacity(string sLine)
{
    string search("\"bytes\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 2);
        sLine.erase(sLine.length() - 1, 1);
        capacity = stol(sLine);
    }
}

/**
 * \brief   parse ErrorCount
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parseErrorCount(string sLine)
{
    string search("\"error_count_total\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 2);
        sLine.erase(sLine.length() - 2, 2);
        errorCount = stol(sLine);
    }
}

/**
 * \brief   parse PowerOnHours
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parsePowerOnHours(string sLine)
{
    string search("\"hours\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 2);
        sLine.erase(sLine.length() - 1, 1);
        powerOnHours = stol(sLine);
    }
}

/**
 * \brief   parse PowerCycle
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parsePowerCycle(string sLine)
{
    string search("\"power_cycle_count\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 2);
        sLine.erase(sLine.length() - 2, 2);
        powerCycle = stol(sLine);
    }
}

/**
 * \brief   parse temperature
 * \param	string output line of smartctl
 * \return  void
 */
void SMART::parseTemperature(string sLine)
{
    string search("\"current\": ");
    size_t found = sLine.find(search);
    if (found != string::npos)
    {
        sLine.erase(0, sLine.find(": ") + 2);
        sLine.erase(sLine.length() - 1, 2);
        if (sLine == "{")
        {
            temperature = 0U; // this drive doesn't support temperature
        }
        else
        {
            temperature = stol(sLine);
        }
    }
}