// Copyright (C) 2017 The Android Open Source Project
//
// 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 "statsd_test_util.h"

namespace android {
namespace os {
namespace statsd {


AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(atomId);
    return atom_matcher;
}

AtomMatcher CreateTemperatureAtomMatcher() {
    return CreateSimpleAtomMatcher("TemperatureMatcher", android::util::TEMPERATURE);
}

AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
                                                      ScheduledJobStateChanged::State state) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED);
    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
    field_value_matcher->set_field(3);  // State field.
    field_value_matcher->set_eq_int(state);
    return atom_matcher;
}

AtomMatcher CreateStartScheduledJobAtomMatcher() {
    return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart",
                                                     ScheduledJobStateChanged::STARTED);
}

AtomMatcher CreateFinishScheduledJobAtomMatcher() {
    return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish",
                                                     ScheduledJobStateChanged::FINISHED);
}

AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
    return atom_matcher;
}

AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId("UidProcessStateChanged"));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
    return atom_matcher;
}

AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
                                                  WakelockStateChanged::State state) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
    field_value_matcher->set_field(4);  // State field.
    field_value_matcher->set_eq_int(state);
    return atom_matcher;
}

AtomMatcher CreateAcquireWakelockAtomMatcher() {
    return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
}

AtomMatcher CreateReleaseWakelockAtomMatcher() {
    return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
}

AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher(
    const string& name, BatterySaverModeStateChanged::State state) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::BATTERY_SAVER_MODE_STATE_CHANGED);
    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
    field_value_matcher->set_field(1);  // State field.
    field_value_matcher->set_eq_int(state);
    return atom_matcher;
}

AtomMatcher CreateBatterySaverModeStartAtomMatcher() {
    return CreateBatterySaverModeStateChangedAtomMatcher(
        "BatterySaverModeStart", BatterySaverModeStateChanged::ON);
}


AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
    return CreateBatterySaverModeStateChangedAtomMatcher(
        "BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
}


AtomMatcher CreateScreenStateChangedAtomMatcher(
    const string& name, android::view::DisplayStateEnum state) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
    field_value_matcher->set_field(1);  // State field.
    field_value_matcher->set_eq_int(state);
    return atom_matcher;
}


AtomMatcher CreateScreenTurnedOnAtomMatcher() {
    return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
            android::view::DisplayStateEnum::DISPLAY_STATE_ON);
}

AtomMatcher CreateScreenTurnedOffAtomMatcher() {
    return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff",
            ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
}

AtomMatcher CreateSyncStateChangedAtomMatcher(
    const string& name, SyncStateChanged::State state) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
    field_value_matcher->set_field(3);  // State field.
    field_value_matcher->set_eq_int(state);
    return atom_matcher;
}

AtomMatcher CreateSyncStartAtomMatcher() {
    return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
}

AtomMatcher CreateSyncEndAtomMatcher() {
    return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
}

AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
    const string& name, ActivityForegroundStateChanged::State state) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
    field_value_matcher->set_field(4);  // Activity field.
    field_value_matcher->set_eq_int(state);
    return atom_matcher;
}

AtomMatcher CreateMoveToBackgroundAtomMatcher() {
    return CreateActivityForegroundStateChangedAtomMatcher(
        "Background", ActivityForegroundStateChanged::BACKGROUND);
}

AtomMatcher CreateMoveToForegroundAtomMatcher() {
    return CreateActivityForegroundStateChangedAtomMatcher(
        "Foreground", ActivityForegroundStateChanged::FOREGROUND);
}

AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher(
    const string& name, ProcessLifeCycleStateChanged::State state) {
    AtomMatcher atom_matcher;
    atom_matcher.set_id(StringToId(name));
    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
    simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
    field_value_matcher->set_field(3);  // Process state field.
    field_value_matcher->set_eq_int(state);
    return atom_matcher;
}

AtomMatcher CreateProcessCrashAtomMatcher() {
    return CreateProcessLifeCycleStateChangedAtomMatcher(
        "Crashed", ProcessLifeCycleStateChanged::CRASHED);
}

Predicate CreateScheduledJobPredicate() {
    Predicate predicate;
    predicate.set_id(StringToId("ScheduledJobRunningPredicate"));
    predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart"));
    predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish"));
    return predicate;
}

Predicate CreateBatterySaverModePredicate() {
    Predicate predicate;
    predicate.set_id(StringToId("BatterySaverIsOn"));
    predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
    predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
    return predicate;
}

Predicate CreateScreenIsOnPredicate() {
    Predicate predicate;
    predicate.set_id(StringToId("ScreenIsOn"));
    predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn"));
    predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff"));
    return predicate;
}

Predicate CreateScreenIsOffPredicate() {
    Predicate predicate;
    predicate.set_id(1111123);
    predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
    predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
    return predicate;
}

Predicate CreateHoldingWakelockPredicate() {
    Predicate predicate;
    predicate.set_id(StringToId("HoldingWakelock"));
    predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock"));
    predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock"));
    return predicate;
}

Predicate CreateIsSyncingPredicate() {
    Predicate predicate;
    predicate.set_id(33333333333333);
    predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
    predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
    return predicate;
}

Predicate CreateIsInBackgroundPredicate() {
    Predicate predicate;
    predicate.set_id(StringToId("IsInBackground"));
    predicate.mutable_simple_predicate()->set_start(StringToId("Background"));
    predicate.mutable_simple_predicate()->set_stop(StringToId("Foreground"));
    return predicate;
}

void addPredicateToPredicateCombination(const Predicate& predicate,
                                        Predicate* combinationPredicate) {
    combinationPredicate->mutable_combination()->add_predicate(predicate.id());
}

FieldMatcher CreateAttributionUidDimensions(const int atomId,
                                            const std::vector<Position>& positions) {
    FieldMatcher dimensions;
    dimensions.set_field(atomId);
    for (const auto position : positions) {
        auto child = dimensions.add_child();
        child->set_field(1);
        child->set_position(position);
        child->add_child()->set_field(1);
    }
    return dimensions;
}

FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
                                                 const std::vector<Position>& positions) {
    FieldMatcher dimensions;
    dimensions.set_field(atomId);
    for (const auto position : positions) {
        auto child = dimensions.add_child();
        child->set_field(1);
        child->set_position(position);
        child->add_child()->set_field(1);
        child->add_child()->set_field(2);
    }
    return dimensions;
}

FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
    FieldMatcher dimensions;
    dimensions.set_field(atomId);
    for (const int field : fields) {
        dimensions.add_child()->set_field(field);
    }
    return dimensions;
}

std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
    const android::view::DisplayStateEnum state, uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
    EXPECT_TRUE(event->write(state));
    event->init();
    return event;
}

std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(
        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
    event->init();
    return event;
}

std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(
        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
    event->init();
    return event;
}

std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
    int level, uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
    EXPECT_TRUE(event->write(level));
    event->init();
    return event;

}

std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
        const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
        const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
    event->write(attributions);
    event->write(jobName);
    event->write(state);
    event->init();
    return event;
}

std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
    const std::vector<AttributionNodeInternal>& attributions,
    const string& name, uint64_t timestampNs) {
    return CreateScheduledJobStateChangedEvent(
            attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
}

// Create log event when scheduled job finishes.
std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
    const std::vector<AttributionNodeInternal>& attributions,
    const string& name, uint64_t timestampNs) {
    return CreateScheduledJobStateChangedEvent(
            attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
}

std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
        const WakelockStateChanged::State state, uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
    event->write(attributions);
    event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
    event->write(wakelockName);
    event->write(state);
    event->init();
    return event;
}

std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
        uint64_t timestampNs) {
    return CreateWakelockStateChangedEvent(
        attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
}

std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
        uint64_t timestampNs) {
    return CreateWakelockStateChangedEvent(
        attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
}

std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
    const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(
        android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
    event->write(uid);
    event->write("pkg_name");
    event->write("class_name");
    event->write(state);
    event->init();
    return event;
}

std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
    return CreateActivityForegroundStateChangedEvent(
        uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
}

std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
    return CreateActivityForegroundStateChangedEvent(
        uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
}

std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
        const std::vector<AttributionNodeInternal>& attributions, const string& name,
        const SyncStateChanged::State state, uint64_t timestampNs) {
    auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
    event->write(attributions);
    event->write(name);
    event->write(state);
    event->init();
    return event;
}

std::unique_ptr<LogEvent> CreateSyncStartEvent(
        const std::vector<AttributionNodeInternal>& attributions, const string& name,
        uint64_t timestampNs) {
    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
}

std::unique_ptr<LogEvent> CreateSyncEndEvent(
        const std::vector<AttributionNodeInternal>& attributions, const string& name,
        uint64_t timestampNs) {
    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
}

std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
    const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) {
    auto logEvent = std::make_unique<LogEvent>(
        android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs);
    logEvent->write(uid);
    logEvent->write("");
    logEvent->write(state);
    logEvent->init();
    return logEvent;
}

std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) {
    return CreateProcessLifeCycleStateChangedEvent(
        uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs);
}

std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
    int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs) {
    auto logEvent = std::make_unique<LogEvent>(
        android::util::ISOLATED_UID_CHANGED, timestampNs);
    logEvent->write(hostUid);
    logEvent->write(isolatedUid);
    logEvent->write(is_create);
    logEvent->init();
    return logEvent;
}

sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                              const StatsdConfig& config, const ConfigKey& key) {
    sp<UidMap> uidMap = new UidMap();
    sp<AlarmMonitor> anomalyAlarmMonitor =
        new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
                [](const sp<IStatsCompanionService>&){});
    sp<AlarmMonitor> periodicAlarmMonitor =
        new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
                [](const sp<IStatsCompanionService>&){});
    sp<StatsLogProcessor> processor = new StatsLogProcessor(
        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, [](const ConfigKey&){return true;});
    processor->OnConfigUpdated(currentTimeNs, key, config);
    return processor;
}

AttributionNodeInternal CreateAttribution(const int& uid, const string& tag) {
    AttributionNodeInternal attribution;
    attribution.set_uid(uid);
    attribution.set_tag(tag);
    return attribution;
}

void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
  std::sort(events->begin(), events->end(),
            [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
              return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs();
            });
}

int64_t StringToId(const string& str) {
    return static_cast<int64_t>(std::hash<std::string>()(str));
}

void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
    EXPECT_EQ(value.field(), atomId);
    // Attribution field.
    EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
    // Uid only.
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value_size(), 1);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(0).field(), 1);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(0).value_int(), uid);
}

void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid) {
    EXPECT_EQ(value.field(), atomId);
    EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1);
    // Attribution field.
    EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
    // Uid only.
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value_size(), 1);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(0).field(), 1);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(0).value_int(), uid);
}

void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid) {
    EXPECT_EQ(value.field(), atomId);
    EXPECT_GT(value.value_tuple().dimensions_value_size(), node_idx);
    // Attribution field.
    EXPECT_EQ(value.value_tuple().dimensions_value(node_idx).field(), 1);
    EXPECT_EQ(value.value_tuple().dimensions_value(node_idx)
        .value_tuple().dimensions_value(0).field(), 1);
    EXPECT_EQ(value.value_tuple().dimensions_value(node_idx)
        .value_tuple().dimensions_value(0).value_int(), uid);
}

void ValidateAttributionUidAndTagDimension(
    const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag) {
    EXPECT_EQ(value.field(), atomId);
    EXPECT_GT(value.value_tuple().dimensions_value_size(), node_idx);
    // Attribution field.
    EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx).field());
    // Uid only.
    EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx)
        .value_tuple().dimensions_value_size());
    EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx)
        .value_tuple().dimensions_value(0).field());
    EXPECT_EQ(uid, value.value_tuple().dimensions_value(node_idx)
        .value_tuple().dimensions_value(0).value_int());
    EXPECT_EQ(2, value.value_tuple().dimensions_value(node_idx)
        .value_tuple().dimensions_value(1).field());
    EXPECT_EQ(tag, value.value_tuple().dimensions_value(node_idx)
        .value_tuple().dimensions_value(1).value_str());
}

void ValidateAttributionUidAndTagDimension(
    const DimensionsValue& value, int atomId, int uid, const std::string& tag) {
    EXPECT_EQ(value.field(), atomId);
    EXPECT_EQ(1, value.value_tuple().dimensions_value_size());
    // Attribution field.
    EXPECT_EQ(1, value.value_tuple().dimensions_value(0).field());
    // Uid only.
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value_size(), 2);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(0).field(), 1);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(0).value_int(), uid);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(1).field(), 2);
    EXPECT_EQ(value.value_tuple().dimensions_value(0)
        .value_tuple().dimensions_value(1).value_str(), tag);
}

bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
    if (s1.field() != s2.field()) {
        return false;
    }
    if (s1.value_case() != s2.value_case()) {
        return false;
    }
    switch (s1.value_case()) {
        case DimensionsValue::ValueCase::kValueStr:
            return (s1.value_str() == s2.value_str());
        case DimensionsValue::ValueCase::kValueInt:
            return s1.value_int() == s2.value_int();
        case DimensionsValue::ValueCase::kValueLong:
            return s1.value_long() == s2.value_long();
        case DimensionsValue::ValueCase::kValueBool:
            return s1.value_bool() == s2.value_bool();
        case DimensionsValue::ValueCase::kValueFloat:
            return s1.value_float() == s2.value_float();
        case DimensionsValue::ValueCase::kValueTuple: {
            if (s1.value_tuple().dimensions_value_size() !=
                s2.value_tuple().dimensions_value_size()) {
                return false;
            }
            bool allMatched = true;
            for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) {
                allMatched &= EqualsTo(s1.value_tuple().dimensions_value(i),
                                       s2.value_tuple().dimensions_value(i));
            }
            return allMatched;
        }
        case DimensionsValue::ValueCase::VALUE_NOT_SET:
        default:
            return true;
    }
}

bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) {
    if (s1.field() != s2.field()) {
        return s1.field() < s2.field();
    }
    if (s1.value_case() != s2.value_case()) {
        return s1.value_case() < s2.value_case();
    }
    switch (s1.value_case()) {
        case DimensionsValue::ValueCase::kValueStr:
            return s1.value_str() < s2.value_str();
        case DimensionsValue::ValueCase::kValueInt:
            return s1.value_int() < s2.value_int();
        case DimensionsValue::ValueCase::kValueLong:
            return s1.value_long() < s2.value_long();
        case DimensionsValue::ValueCase::kValueBool:
            return (int)s1.value_bool() < (int)s2.value_bool();
        case DimensionsValue::ValueCase::kValueFloat:
            return s1.value_float() < s2.value_float();
        case DimensionsValue::ValueCase::kValueTuple: {
            if (s1.value_tuple().dimensions_value_size() !=
                s2.value_tuple().dimensions_value_size()) {
                return s1.value_tuple().dimensions_value_size() <
                       s2.value_tuple().dimensions_value_size();
            }
            for (int i = 0; i < s1.value_tuple().dimensions_value_size(); ++i) {
                if (EqualsTo(s1.value_tuple().dimensions_value(i),
                             s2.value_tuple().dimensions_value(i))) {
                    continue;
                } else {
                    return LessThan(s1.value_tuple().dimensions_value(i),
                                    s2.value_tuple().dimensions_value(i));
                }
            }
            return false;
        }
        case DimensionsValue::ValueCase::VALUE_NOT_SET:
        default:
            return false;
    }
}

bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2) {
    if (LessThan(s1.dimInWhat, s2.dimInWhat)) {
        return true;
    } else if (LessThan(s2.dimInWhat, s1.dimInWhat)) {
        return false;
    }

    return LessThan(s1.dimInCondition, s2.dimInCondition);
}

void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
                               DimensionsValue* dimension) {
    if (dimension->has_value_str_hash()) {
        auto it = str_map.find((uint64_t)(dimension->value_str_hash()));
        if (it != str_map.end()) {
            dimension->clear_value_str_hash();
            dimension->set_value_str(it->second);
        } else {
            ALOGE("Can not find the string hash: %llu",
                (unsigned long long)dimension->value_str_hash());
        }
    } else if (dimension->has_value_tuple()) {
        auto value_tuple = dimension->mutable_value_tuple();
        for (int i = 0; i < value_tuple->dimensions_value_size(); ++i) {
            backfillStringInDimension(str_map, value_tuple->mutable_dimensions_value(i));
        }
    }
}

void backfillStringInReport(ConfigMetricsReport *config_report) {
    std::map<uint64_t, string> str_map;
    for (const auto& str : config_report->strings()) {
        uint64_t hash = Hash64(str);
        if (str_map.find(hash) != str_map.end()) {
            ALOGE("String hash conflicts: %s %s", str.c_str(), str_map[hash].c_str());
        }
        str_map[hash] = str;
    }
    for (int i = 0; i < config_report->metrics_size(); ++i) {
        auto metric_report = config_report->mutable_metrics(i);
        if (metric_report->has_count_metrics()) {
            backfillStringInDimension(str_map, metric_report->mutable_count_metrics());
        } else if (metric_report->has_duration_metrics()) {
            backfillStringInDimension(str_map, metric_report->mutable_duration_metrics());
        } else if (metric_report->has_gauge_metrics()) {
            backfillStringInDimension(str_map, metric_report->mutable_gauge_metrics());
        } else if (metric_report->has_value_metrics()) {
            backfillStringInDimension(str_map, metric_report->mutable_value_metrics());
        }
    }
    // Backfill the package names.
    for (int i = 0 ; i < config_report->uid_map().snapshots_size(); ++i) {
        auto snapshot = config_report->mutable_uid_map()->mutable_snapshots(i);
        for (int j = 0 ; j < snapshot->package_info_size(); ++j) {
            auto package_info = snapshot->mutable_package_info(j);
            if (package_info->has_name_hash()) {
                auto it = str_map.find((uint64_t)(package_info->name_hash()));
                if (it != str_map.end()) {
                    package_info->clear_name_hash();
                    package_info->set_name(it->second);
                } else {
                    ALOGE("Can not find the string package name hash: %llu",
                        (unsigned long long)package_info->name_hash());
                }

            }
        }
    }
    // Backfill the app name in app changes.
    for (int i = 0 ; i < config_report->uid_map().changes_size(); ++i) {
        auto change = config_report->mutable_uid_map()->mutable_changes(i);
        if (change->has_app_hash()) {
            auto it = str_map.find((uint64_t)(change->app_hash()));
            if (it != str_map.end()) {
                change->clear_app_hash();
                change->set_app(it->second);
            } else {
                ALOGE("Can not find the string change app name hash: %llu",
                    (unsigned long long)change->app_hash());
            }
        }
    }
}

void backfillStringInReport(ConfigMetricsReportList *config_report_list) {
    for (int i = 0; i < config_report_list->reports_size(); ++i) {
        backfillStringInReport(config_report_list->mutable_reports(i));
    }
}

bool backfillDimensionPath(const DimensionsValue& path,
                           const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
                           int* leafIndex,
                           DimensionsValue* dimension) {
    dimension->set_field(path.field());
    if (path.has_value_tuple()) {
        for (int i = 0; i < path.value_tuple().dimensions_value_size(); ++i) {
            if (!backfillDimensionPath(
                path.value_tuple().dimensions_value(i), leafValues, leafIndex,
                dimension->mutable_value_tuple()->add_dimensions_value())) {
                return false;
            }
        }
    } else {
        if (*leafIndex < 0 || *leafIndex >= leafValues.size()) {
            return false;
        }
        dimension->MergeFrom(leafValues.Get(*leafIndex));
        (*leafIndex)++;
    }
    return true;
}

bool backfillDimensionPath(const DimensionsValue& path,
                           const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
                           DimensionsValue* dimension) {
    int leafIndex = 0;
    return backfillDimensionPath(path, leafValues, &leafIndex, dimension);
}

void backfillDimensionPath(ConfigMetricsReportList *config_report_list) {
    for (int i = 0; i < config_report_list->reports_size(); ++i) {
        auto report = config_report_list->mutable_reports(i);
        for (int j = 0; j < report->metrics_size(); ++j) {
            auto metric_report = report->mutable_metrics(j);
            if (metric_report->has_dimensions_path_in_what() ||
                metric_report->has_dimensions_path_in_condition()) {
                auto whatPath = metric_report->dimensions_path_in_what();
                auto conditionPath = metric_report->dimensions_path_in_condition();
                if (metric_report->has_count_metrics()) {
                    backfillDimensionPath(whatPath, conditionPath,
                                          metric_report->mutable_count_metrics());
                } else if (metric_report->has_duration_metrics()) {
                    backfillDimensionPath(whatPath, conditionPath,
                                          metric_report->mutable_duration_metrics());
                } else if (metric_report->has_gauge_metrics()) {
                    backfillDimensionPath(whatPath, conditionPath,
                                          metric_report->mutable_gauge_metrics());
                } else if (metric_report->has_value_metrics()) {
                    backfillDimensionPath(whatPath, conditionPath,
                                          metric_report->mutable_value_metrics());
                }
                metric_report->clear_dimensions_path_in_what();
                metric_report->clear_dimensions_path_in_condition();
            }
        }
    }
}

void backfillStartEndTimestamp(StatsLogReport *report) {
    const int64_t timeBaseNs = report->time_base_elapsed_nano_seconds();
    const int64_t bucketSizeNs = report->bucket_size_nano_seconds();
    if (report->has_count_metrics()) {
        backfillStartEndTimestampForMetrics(
            timeBaseNs, bucketSizeNs, report->mutable_count_metrics());
    } else if (report->has_duration_metrics()) {
        backfillStartEndTimestampForMetrics(
            timeBaseNs, bucketSizeNs, report->mutable_duration_metrics());
    } else if (report->has_gauge_metrics()) {
        backfillStartEndTimestampForMetrics(
            timeBaseNs, bucketSizeNs, report->mutable_gauge_metrics());
        if (report->gauge_metrics().skipped_size() > 0) {
            backfillStartEndTimestampForSkippedBuckets(
                timeBaseNs, report->mutable_gauge_metrics());
        }
    } else if (report->has_value_metrics()) {
        backfillStartEndTimestampForMetrics(
            timeBaseNs, bucketSizeNs, report->mutable_value_metrics());
        if (report->value_metrics().skipped_size() > 0) {
            backfillStartEndTimestampForSkippedBuckets(
                timeBaseNs, report->mutable_value_metrics());
        }
    }
}

void backfillStartEndTimestamp(ConfigMetricsReport *config_report) {
    for (int j = 0; j < config_report->metrics_size(); ++j) {
        backfillStartEndTimestamp(config_report->mutable_metrics(j));
    }
}

void backfillStartEndTimestamp(ConfigMetricsReportList *config_report_list) {
    for (int i = 0; i < config_report_list->reports_size(); ++i) {
        backfillStartEndTimestamp(config_report_list->mutable_reports(i));
    }
}

}  // namespace statsd
}  // namespace os
}  // namespace android