/*
 * Copyright (C) 2022 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.
 */

#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_DEOBFUSCATION_MAPPING_TABLE_H_
#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_DEOBFUSCATION_MAPPING_TABLE_H_

#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>

#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/murmur_hash.h"
#include "src/trace_processor/storage/trace_storage.h"

namespace perfetto::trace_processor {

// Constains deobfuscation for Java class names and its members per |PackageId|.
class DeobfuscationMappingTable {
 public:
  struct PackageId {
    std::string package_name;
    int64_t version_code;

    bool operator==(const PackageId& other) const {
      return package_name == other.package_name &&
             version_code == other.version_code;
    }
  };

  // For the given |package| and |obfuscated_class_name| adds translations of
  // the class and its members.
  //
  // Returns `true` if the translation for the given class
  // was inserted, `false` if there is already a translation for the given
  // class.
  bool AddClassTranslation(
      const PackageId& package,
      StringId obfuscated_class_name,
      StringId deobfuscated_class_name,
      base::FlatHashMap<StringId, StringId> obfuscated_to_deobfuscated_members);

  // These functions return the deobfuscated class/member name from an
  // obfuscated class/member name.
  // If a package is not provided, the |default_package_id_| is used.
  // If translation is not found, returns std::nullopt.

  std::optional<StringId> TranslateClass(StringId obfuscated_class_name) const;

  std::optional<StringId> TranslateClass(const PackageId& package,
                                         StringId obfuscated_class_name) const;

  std::optional<StringId> TranslateMember(const PackageId& package,
                                          StringId obfuscated_class_name,
                                          StringId obfuscated_member) const;

 private:
  struct PackageIdHash {
    std::size_t operator()(PackageId const& p) const noexcept {
      return static_cast<std::size_t>(
          base::MurmurHashCombine(p.package_name, p.version_code));
    }
  };

  using ObfuscatedToDeobfuscatedMembers = base::FlatHashMap<StringId, StringId>;
  struct ClassTranslation {
    StringId deobfuscated_class_name;
    ObfuscatedToDeobfuscatedMembers members;
  };

  using ObfuscatedClassesToMembers =
      base::FlatHashMap<StringId, ClassTranslation>;
  base::FlatHashMap<PackageId, ObfuscatedClassesToMembers, PackageIdHash>
      class_per_package_translation_;

  // To translate entities which don't have a package id, we will use
  // |default_package_id_|. |default_package_id_| is a package id of the first
  // inserted entity with a package id;
  // We need this because currently TraceProcessor doesn't use the package
  // version of the arguments.
  // TODO(b/244700870): start use the package version of arguments.
  std::optional<PackageId> default_package_id_;
};

}  // namespace perfetto::trace_processor

#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_DEOBFUSCATION_MAPPING_TABLE_H_
