#include "PixelStats.h"

#define LOG_TAG "pixelstats-system"
#include <log/log.h>
#include <metricslogger/metrics_logger.h>

namespace hardware {
namespace google {
namespace pixelstats {
namespace V1_0 {
namespace implementation {

using namespace android::metricslogger;

PixelStats::PixelStats()
    :limiter_(kDailyRatelimit) {}

void loggerAddFields(ComplexEventLogger* logger) {
    logger->Record();
}

template<typename... Args>
void loggerAddFields(ComplexEventLogger* logger, int32_t field, int32_t value, Args... args) {
    logger->AddTaggedData(LOGBUILDER_TYPE, TYPE_ACTION);
    logger->AddTaggedData(field, value);
    loggerAddFields(logger, args...);
}

template<typename... Args>
void logIntAction(int32_t category, Args... args) {
    ComplexEventLogger logger(category);
    logger.AddTaggedData(LOGBUILDER_TYPE, TYPE_ACTION);
    loggerAddFields(&logger, args...);
}

// Methods from ::hardware::google::pixelstats::V1_0::IPixelStats follow.
Return<void> PixelStats::reportUsbConnectorConnected() {
    // Ratelimit to max 20 / 24hrs (expected 0/24hrs)
    if (rateLimit(android::metricslogger::ACTION_USB_CONNECTOR_CONNECTED, 20))
        return Void();
    logIntAction(android::metricslogger::ACTION_USB_CONNECTOR_CONNECTED);
    return Void();
}

Return<void> PixelStats::reportUsbConnectorDisconnected(int32_t durationMillis) {
    // Ratelimit to max 20 / 24hrs (expected 0/24hrs)
    if (rateLimit(android::metricslogger::ACTION_USB_CONNECTOR_DISCONNECTED, 20))
        return Void();
    logIntAction(android::metricslogger::ACTION_USB_CONNECTOR_DISCONNECTED,
                 android::metricslogger::FIELD_DURATION_MILLIS, durationMillis);
    return Void();
}

Return<void> PixelStats::reportUsbAudioConnected(int32_t vid, int32_t pid) {
    // Ratelimit to max 20 / 24hrs (expected 0/24hrs)
    if (rateLimit(android::metricslogger::ACTION_USB_AUDIO_CONNECTED, 20))
        return Void();
    logIntAction(android::metricslogger::ACTION_USB_AUDIO_CONNECTED,
                 android::metricslogger::FIELD_USB_AUDIO_VIDPID, (vid << 16) | pid);
    return Void();
}

Return<void> PixelStats::reportUsbAudioDisconnected(int32_t vid, int32_t pid,
                                                    int32_t durationMillis) {
    // Ratelimit to max 20 / 24hrs (expected 0/24hrs)
    if (rateLimit(android::metricslogger::ACTION_USB_AUDIO_DISCONNECTED, 20))
        return Void();
    logIntAction(android::metricslogger::ACTION_USB_AUDIO_DISCONNECTED, FIELD_USB_AUDIO_VIDPID,
                    (vid << 16) | pid, android::metricslogger::FIELD_DURATION_MILLIS,
                    durationMillis);
    return Void();
}

Return<void> PixelStats::reportSpeakerImpedance(int32_t speakerLocation, int32_t milliOhms) {
    // Ratelimit to max 2 / 24hrs (expected 1/24hrs)
    if (rateLimit(android::metricslogger::ACTION_SPEAKER_IMPEDANCE, 2))
        return Void();

    logIntAction(android::metricslogger::ACTION_SPEAKER_IMPEDANCE, FIELD_SPEAKER_LOCATION,
                    speakerLocation, FIELD_SPEAKER_IMPEDANCE_MILLIOHMS, milliOhms);
    return Void();
}

static android::metricslogger::HardwareType toMetricsLoggerHardwareType(
    IPixelStats::HardwareType pixelstatsType) {
    switch (pixelstatsType) {
        case IPixelStats::HardwareType::MICROPHONE:
            return android::metricslogger::HardwareType::HARDWARE_MICROPHONE;
        case IPixelStats::HardwareType::CODEC:
            return android::metricslogger::HardwareType::HARDWARE_CODEC;
        case IPixelStats::HardwareType::SPEAKER:
            return android::metricslogger::HardwareType::HARDWARE_SPEAKER;
        case IPixelStats::HardwareType::FINGERPRINT:
            return android::metricslogger::HardwareType::HARDWARE_FINGERPRINT;
        case IPixelStats::HardwareType::UNKNOWN:
        default:
            return android::metricslogger::HardwareType::HARDWARE_UNKNOWN;

    }
}

static android::metricslogger::HardwareFailureCode toMetricsLoggerHardwareFailure(
    IPixelStats::HardwareErrorCode pixelstatsError) {
    switch (pixelstatsError) {
        case IPixelStats::HardwareErrorCode::COMPLETE:
            return HARDWARE_FAILURE_COMPLETE;
        case IPixelStats::HardwareErrorCode::SPEAKER_HIGH_Z:
            return HARDWARE_FAILURE_SPEAKER_HIGH_Z;
        case IPixelStats::HardwareErrorCode::SPEAKER_SHORT:
            return HARDWARE_FAILURE_SPEAKER_SHORT;
        case IPixelStats::HardwareErrorCode::FINGERPRINT_SENSOR_BROKEN:
            return HARDWARE_FAILURE_FINGERPRINT_SENSOR_BROKEN;
        case IPixelStats::HardwareErrorCode::FINGERPRINT_TOO_MANY_DEAD_PIXELS:
            return HARDWARE_FAILURE_FINGERPRINT_TOO_MANY_DEAD_PIXELS;
        case IPixelStats::HardwareErrorCode::UNKNOWN:
        default:
            return HARDWARE_FAILURE_UNKNOWN;
    }
}

Return<void> PixelStats::reportHardwareFailed(HardwareType hardwareType, int32_t hardwareLocation,
                                              HardwareErrorCode errorCode) {
    // Ratelimit to max 15 / 24hrs (expected 0/24hrs)
    if (rateLimit(android::metricslogger::ACTION_HARDWARE_FAILED, 15))
        return Void();

    logIntAction(ACTION_HARDWARE_FAILED,
                 FIELD_HARDWARE_TYPE, toMetricsLoggerHardwareType(hardwareType),
                 FIELD_HARDWARE_LOCATION, hardwareLocation,
                 FIELD_HARDWARE_FAILURE_CODE, toMetricsLoggerHardwareFailure(errorCode));
    return Void();
}

Return<void> PixelStats::reportPhysicalDropDetected(int32_t confidencePctg, int32_t accelPeak,
                                                    int32_t freefallDurationMs) {
    // Ratelimit to max 10 / 24hrs (expected 0/24hrs)
    if (rateLimit(android::metricslogger::ACTION_PHYSICAL_DROP, 10))
        return Void();

    logIntAction(ACTION_PHYSICAL_DROP, FIELD_CONFIDENCE_PERCENT, confidencePctg,
                 FIELD_ACCEL_MILLI_G, accelPeak,
                 FIELD_DURATION_MILLIS, freefallDurationMs);
    return Void();
}

Return<void> PixelStats::reportChargeCycles(const hidl_string& buckets) {
    // Ratelimit to max 2 / 24hrs (expected 1/24hrs)
    if (rateLimit(android::metricslogger::ACTION_BATTERY_CHARGE_CYCLES, 2))
        return Void();
    LogMultiAction(ACTION_BATTERY_CHARGE_CYCLES, FIELD_BATTERY_CHARGE_CYCLES, buckets);
    return Void();
}

static android::metricslogger::IoOperation toMetricsLoggerIoOperation(IPixelStats::IoOperation op) {
    switch (op) {
        default:
        case IPixelStats::IoOperation::UNKNOWN:
            return android::metricslogger::IoOperation::IOOP_UNKNOWN;
        case IPixelStats::IoOperation::READ:
            return android::metricslogger::IoOperation::IOOP_READ;
        case IPixelStats::IoOperation::WRITE:
            return android::metricslogger::IoOperation::IOOP_WRITE;
        case IPixelStats::IoOperation::UNMAP:
            return android::metricslogger::IoOperation::IOOP_UNMAP;
        case IPixelStats::IoOperation::SYNC:
            return android::metricslogger::IoOperation::IOOP_SYNC;
    }
}

Return<void> PixelStats::reportSlowIo(IoOperation operation, int32_t count) {
    // Ratelimit to max 2 per 24hrs
    if (rateLimit(android::metricslogger::ACTION_SLOW_IO, 2))
        return Void();
    logIntAction(ACTION_SLOW_IO, FIELD_IO_OPERATION_TYPE, toMetricsLoggerIoOperation(operation),
                    FIELD_IO_OPERATION_COUNT, count);
    return Void();
}

Return<void> PixelStats::reportBatteryHealthSnapshot(const BatteryHealthSnapshotArgs& args) {
    // Ratelimit to max 2 per 24hrs
    if (rateLimit(android::metricslogger::ACTION_BATTERY_HEALTH, 2))
        return Void();
    logIntAction(ACTION_BATTERY_HEALTH,
                 FIELD_BATTERY_HEALTH_SNAPSHOT_TYPE, (int32_t)args.type,
                 FIELD_BATTERY_TEMPERATURE_DECI_C, args.temperatureDeciC,
                 FIELD_BATTERY_VOLTAGE_UV, args.voltageMicroV,
                 FIELD_BATTERY_CURRENT_UA, args.currentMicroA,
                 FIELD_BATTERY_OPEN_CIRCUIT_VOLTAGE_UV, args.openCircuitVoltageMicroV,
                 FIELD_BATTERY_RESISTANCE_UOHMS, args.resistanceMicroOhm,
                 FIELD_END_BATTERY_PERCENT, args.levelPercent);
    return Void();
}

Return<void> PixelStats::reportBatteryCausedShutdown(int32_t voltageMicroV) {
    // Ratelimit to max 5 per 24hrs
    if (rateLimit(android::metricslogger::ACTION_BATTERY_CAUSED_SHUTDOWN, 5))
        return Void();
    logIntAction(ACTION_BATTERY_CAUSED_SHUTDOWN, FIELD_BATTERY_VOLTAGE_UV, voltageMicroV);
    return Void();
}

bool PixelStats::rateLimit(int action, int limit) {
    if (limiter_.RateLimit(action, limit)) {
        ALOGE("Rate limited action %d\n", action);
        return true;
    }
    return false;
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace pixelstats
}  // namespace google
}  // namespace hardware