/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "print_sensor_data.h"

#ifdef HIVIEWDFX_HISYSEVENT_ENABLE
#include "hisysevent.h"
#endif // HIVIEWDFX_HISYSEVENT_ENABLE

#include "sensor_errors.h"

#undef LOG_TAG
#define LOG_TAG "PrintSensorData"

namespace OHOS {
namespace Sensors {
using namespace OHOS::HiviewDFX;
namespace {
enum {
    ONE_DIMENSION = 1,
    TWO_DIMENSION = 2,
    THREE_DIMENSION = 3,
    FOUR_DIMENSION = 4,
    SEVEN_DIMENSION = 7,
    EIGHT_DIMENSION = 8,
    THIRTEEN_DIMENSION = 13,
    DEFAULT_DIMENSION = 16
};
constexpr int64_t LOG_INTERVAL = 60000000000L;
constexpr int32_t FIRST_PRINT_TIMES = 10;
constexpr float LOG_FORMAT_DIVIDER = 1e9f;

const std::vector<int32_t> g_triggerSensorType = {
    SENSOR_TYPE_ID_DROP_DETECTION,
    SENSOR_TYPE_ID_HALL,
    SENSOR_TYPE_ID_HALL_EXT,
    SENSOR_TYPE_ID_PROXIMITY,
    SENSOR_TYPE_ID_PROXIMITY1,
    SENSOR_TYPE_ID_WEAR_DETECTION,
};
const std::vector<int32_t> g_continuousSensorType = {
    SENSOR_TYPE_ID_ACCELEROMETER,
    SENSOR_TYPE_ID_AMBIENT_LIGHT,
    SENSOR_TYPE_ID_AMBIENT_LIGHT1,
    SENSOR_TYPE_ID_GRAVITY,
    SENSOR_TYPE_ID_GYROSCOPE,
    SENSOR_TYPE_ID_MAGNETIC_FIELD,
    SENSOR_TYPE_ID_ORIENTATION,
    SENSOR_TYPE_ID_POSTURE,
    SENSOR_TYPE_ID_ROTATION_VECTOR,
};
}

void PrintSensorData::ControlSensorHdiPrint(const SensorData &sensorData)
{ // LCOV_EXCL_START
    auto triggerIt = std::find(g_triggerSensorType.begin(), g_triggerSensorType.end(), sensorData.sensorTypeId);
    if (triggerIt != g_triggerSensorType.end()) {
        PrintHdiData(sensorData);
        ProcessHdiDFX(sensorData);
    }
    std::lock_guard<std::mutex> hdiLoginfoLock(hdiLoginfoMutex_);
    auto it = hdiLoginfo_.find(sensorData.sensorTypeId);
    if (it == hdiLoginfo_.end()) {
        return;
    }
    it->second.hdiTimes++;
    if (it->second.count < FIRST_PRINT_TIMES) {
        PrintHdiData(sensorData);
        if (it->second.count == FIRST_PRINT_TIMES - 1) {
            it->second.lastTime = sensorData.timestamp;
        }
        it->second.count++;
    } else {
        if (sensorData.timestamp - it->second.lastTime >= LOG_INTERVAL) {
            PrintHdiData(sensorData);
            it->second.lastTime = sensorData.timestamp;
        }
    }
    if (it->second.hdiTimesFlag == 0) {
        it->second.hdiTimesFlag = sensorData.timestamp;
    }
    if (sensorData.timestamp - it->second.hdiTimesFlag >= LOG_INTERVAL) {
        SEN_HILOGI("sensorType:%{public}d, hdiTimes:%{public}s", sensorData.sensorTypeId,
            std::to_string(it->second.hdiTimes).c_str());
        it->second.hdiTimesFlag = sensorData.timestamp;
        it->second.hdiTimes = 0;
    }
} // LCOV_EXCL_STOP

void PrintSensorData::PrintHdiData(const SensorData &sensorData)
{ // LCOV_EXCL_START
    std::string str;
    str += std::to_string(sensorData.deviceId) + " ";
    str += std::to_string(sensorData.sensorTypeId) + " ";
    str += std::to_string(sensorData.sensorId) + " ";
    str += std::to_string(sensorData.location) + " ";
    str += " ts:" + std::to_string(sensorData.timestamp / LOG_FORMAT_DIVIDER) + ", ";
    int32_t dataDim = GetDataDimension(sensorData.sensorTypeId);
    auto data = reinterpret_cast<const float *>(sensorData.data);
    for (int32_t i = 0; i < dataDim; ++i) {
        CHKPV(data);
        str.append(std::to_string(*data));
        if (i != dataDim - 1) {
            str.append(", ");
        }
        ++data;
    }
    str.append("\n");
    SEN_HILOGI("%{public}s", str.c_str());
} // LCOV_EXCL_STOP

void PrintSensorData::ProcessHdiDFX(const SensorData &sensorData)
{ // LCOV_EXCL_START
    std::string strData;
    auto data = reinterpret_cast<const float *>(sensorData.data);
    int32_t dataDim = GetDataDimension(sensorData.sensorTypeId);
    for (int32_t i = 0; i < dataDim; i++) {
        CHKPV(data);
        strData.append(std::to_string(*data));
        if (i != dataDim - 1) {
            strData.append(", ");
        }
        ++data;
    }
#ifdef HIVIEWDFX_HISYSEVENT_ENABLE
    HiSysEventWrite(HiSysEvent::Domain::SENSOR, "EVENT_REPORT",
        HiSysEvent::EventType::BEHAVIOR, "SENSOR_ID", sensorData.sensorTypeId, "TIMESTAMP",
        std::to_string(sensorData.timestamp), "DATA", strData);
#endif // HIVIEWDFX_HISYSEVENT_ENABLE
} // LCOV_EXCL_STOP

int32_t PrintSensorData::GetDataDimension(int32_t sensorType)
{ // LCOV_EXCL_START
    switch (sensorType) {
        case SENSOR_TYPE_ID_DROP_DETECTION:
        case SENSOR_TYPE_ID_HALL:
        case SENSOR_TYPE_ID_PROXIMITY:
        case SENSOR_TYPE_ID_WEAR_DETECTION:
            return ONE_DIMENSION;
        case SENSOR_TYPE_ID_AMBIENT_LIGHT:
        case SENSOR_TYPE_ID_AMBIENT_LIGHT1:
        case SENSOR_TYPE_ID_ACCELEROMETER:
        case SENSOR_TYPE_ID_GRAVITY:
        case SENSOR_TYPE_ID_GYROSCOPE:
        case SENSOR_TYPE_ID_MAGNETIC_FIELD:
        case SENSOR_TYPE_ID_ORIENTATION:
            return THREE_DIMENSION;
        case SENSOR_TYPE_ID_ROTATION_VECTOR:
            return FOUR_DIMENSION;
        case SENSOR_TYPE_ID_HALL_EXT:
            return EIGHT_DIMENSION;
        case SENSOR_TYPE_ID_POSTURE:
            return THIRTEEN_DIMENSION;
        default:
            SEN_HILOGW("Unknown sensorType:%{public}d, size:%{public}d", sensorType, DEFAULT_DIMENSION);
            return DEFAULT_DIMENSION;
    }
} // LCOV_EXCL_STOP

void PrintSensorData::ControlSensorClientPrint(const RecordSensorCallback callback, const SensorEvent &event)
{ // LCOV_EXCL_START
    auto triggerIt = std::find(g_triggerSensorType.begin(), g_triggerSensorType.end(), event.sensorTypeId);
    if (triggerIt != g_triggerSensorType.end()) {
        PrintClientData(event);
        ProcessClientDFX(event);
    }

    auto continuosIt = std::find(g_continuousSensorType.begin(), g_continuousSensorType.end(), event.sensorTypeId);
    if (continuosIt == g_continuousSensorType.end()) {
        return;
    }
    std::lock_guard<std::mutex> clientLoginfoLock(clientLoginfoMutex_);
    auto it = clientLoginfo_.find(callback);
    if (it == clientLoginfo_.end()) {
        return;
    }
    it->second.clientTimes++;
    if (it->second.count < FIRST_PRINT_TIMES) {
        PrintClientData(event);
        if (it->second.count == FIRST_PRINT_TIMES - 1) {
            it->second.lastTime = event.timestamp;
        }
        it->second.count++;
    } else {
        if (event.timestamp - it->second.lastTime >= LOG_INTERVAL) {
            PrintClientData(event);
            it->second.lastTime = event.timestamp;
        }
    }
    if (it->second.clientTimesFlag == 0) {
        it->second.clientTimesFlag = event.timestamp;
    }
    if (event.timestamp - it->second.clientTimesFlag >= LOG_INTERVAL) {
        SEN_HILOGI("sensorType:%{public}d, clientTimes:%{public}s", event.sensorTypeId,
            std::to_string(it->second.clientTimes).c_str());
        it->second.clientTimesFlag = event.timestamp;
        it->second.clientTimes = 0;
    }
} // LCOV_EXCL_STOP

void PrintSensorData::PrintClientData(const SensorEvent &event)
{
    std::string str;
    str += std::to_string(event.deviceId) + " ";
    str += std::to_string(event.sensorTypeId) + " ";
    str += std::to_string(event.sensorId) + " ";
    str += std::to_string(event.location) + " ";
    str += " ts:" + std::to_string(event.timestamp / LOG_FORMAT_DIVIDER) + ", ";
    int32_t dataDim = GetDataDimension(event.sensorTypeId);
    auto data = reinterpret_cast<const float *>(event.data);
    for (int32_t i = 0; i < dataDim; ++i) {
        CHKPV(data);
        str.append(std::to_string(*data));
        if (i != dataDim - 1) {
            str.append(", ");
        }
        ++data;
    }
    str.append("\n");
    SEN_HILOGI("%{public}s", str.c_str());
}

void PrintSensorData::ProcessClientDFX(const SensorEvent &event)
{
    std::string strData;
    auto data = reinterpret_cast<const float *>(event.data);
    int32_t dataDim = GetDataDimension(event.sensorTypeId);
    for (int32_t i = 0; i < dataDim; i++) {
        CHKPV(data);
        strData.append(std::to_string(*data));
        if (i != dataDim - 1) {
            strData.append(", ");
        }
        ++data;
    }
#ifdef HIVIEWDFX_HISYSEVENT_ENABLE
    HiSysEventWrite(HiSysEvent::Domain::SENSOR, "EVENT_REPORT",
        HiSysEvent::EventType::BEHAVIOR, "SENSOR_ID", event.sensorTypeId, "TIMESTAMP",
        std::to_string(event.timestamp), "DATA", strData);
#endif // HIVIEWDFX_HISYSEVENT_ENABLE
}

bool PrintSensorData::IsContinuousType(int32_t sensorType)
{
    return std::find(g_continuousSensorType.begin(), g_continuousSensorType.end(),
        sensorType) != g_continuousSensorType.end();
}

void PrintSensorData::SavePrintUserInfo(const RecordSensorCallback callback)
{ // LCOV_EXCL_START
    CHKPV(callback);
    std::lock_guard<std::mutex> clientLoginfoLock(clientLoginfoMutex_);
    if (clientLoginfo_.find(callback) != clientLoginfo_.end()) {
        return;
    }
    LogPrintInfo info;
    auto status = clientLoginfo_.insert(std::make_pair(callback, info));
    if (!status.second) {
        SEN_HILOGD("callback has been saved");
    }
} // LCOV_EXCL_STOP

void PrintSensorData::RemovePrintUserInfo(const RecordSensorCallback callback)
{ // LCOV_EXCL_START
    CHKPV(callback);
    std::lock_guard<std::mutex> clientLoginfoLock(clientLoginfoMutex_);
    if (clientLoginfo_.find(callback) == clientLoginfo_.end()) {
        return;
    }
    clientLoginfo_.erase(callback);
} // LCOV_EXCL_STOP

void PrintSensorData::ResetHdiCounter(int32_t sensorType)
{ // LCOV_EXCL_START
    std::lock_guard<std::mutex> hdiLoginfoLock(hdiLoginfoMutex_);
    auto it = hdiLoginfo_.find(sensorType);
    if (it == hdiLoginfo_.end()) {
        return;
    }
    it->second.count = 0;
    it->second.lastTime = 0;
} // LCOV_EXCL_STOP

void PrintSensorData::ResetHdiTimes(int32_t sensorType)
{ // LCOV_EXCL_START
    std::lock_guard<std::mutex> hdiLoginfoLock(hdiLoginfoMutex_);
    auto it = hdiLoginfo_.find(sensorType);
    if (it == hdiLoginfo_.end()) {
        return;
    }
    it->second.hdiTimes = 0;
    it->second.hdiTimesFlag = 0;
} // LCOV_EXCL_STOP

void PrintSensorData::PrintSensorDataLog(const std::string &name, const SensorData &data)
{ // LCOV_EXCL_START
    std::string str;
    str += std::to_string(data.deviceId) + ", ";
    str += std::to_string(data.sensorTypeId) + ", ";
    str += std::to_string(data.sensorId) + ", ";
    str += std::to_string(data.location) + ", ";
    str += " ts:" + std::to_string(data.timestamp / LOG_FORMAT_DIVIDER) + ", ";
    int32_t dataDim = GetDataDimension(data.sensorTypeId);
    auto tempData = reinterpret_cast<const float *>(data.data);
    for (int32_t i = 0; i < dataDim; ++i) {
        CHKPV(tempData);
        str.append(std::to_string(*tempData));
        if (i != dataDim - 1) {
            str.append(", ");
        }
        ++tempData;
    }
    str.append("\n");
    SEN_HILOGI("%{public}s SensorData:%{public}s", name.c_str(), str.c_str());
} // LCOV_EXCL_STOP

void PrintSensorData::PrintSensorInfo(SensorInfo *sensorInfos, int32_t sensorInfoCount)
{
    std::string combineSensorIds = "";
    for (int32_t i = 0; i < sensorInfoCount; ++i) {
        combineSensorIds = combineSensorIds + std::to_string(sensorInfos[i].sensorTypeId) + " ";
    }
    SEN_HILOGD("PrintSensorInfo success, sensorIds:%{public}s, sensorInfoCount:%{public}d", combineSensorIds.c_str(),
        sensorInfoCount);
}
} // namespace Sensors
} // namespace OHOS