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

#pragma once

#include <inttypes.h>

#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

#include "RegEx.h"
#include "dso.h"
#include "thread_tree.h"
#include "utils.h"

namespace simpleperf {

class ProguardMappingRetrace {
 public:
  // Add proguard mapping.txt to de-obfuscate minified symbols.
  bool AddProguardMappingFile(std::string_view mapping_file);

  bool DeObfuscateJavaMethods(std::string_view obfuscated_name, std::string* original_name,
                              bool* synthesized) const;

 private:
  struct MappingMethod {
    std::string original_name;
    bool contains_classname;
    bool synthesized;
  };

  struct MappingClass {
    std::string original_classname;
    bool synthesized = false;
    // Map from obfuscated method names to MappingMethod.
    std::unordered_map<std::string, MappingMethod> method_map;
  };

  enum LineType {
    SYNTHESIZED_COMMENT,
    CLASS_LINE,
    METHOD_LINE,
    LINE_EOF,
  };

  struct LineInfo {
    LineType type;
    std::string_view data;
  };

  void ParseMethod(MappingClass& mapping_class);
  void MoveToNextLine();

  // Map from obfuscated class names to ProguardMappingClass.
  std::unordered_map<std::string, MappingClass> class_map_;
  std::unique_ptr<LineReader> line_reader_;
  LineInfo cur_line_;
};

enum class CallChainExecutionType {
  NATIVE_METHOD,
  INTERPRETED_JVM_METHOD,
  JIT_JVM_METHOD,
  // ART methods near interpreted/JIT JVM methods. They're shown only when RemoveArtFrame = false.
  ART_METHOD,
};

struct CallChainReportEntry {
  uint64_t ip = 0;
  const Symbol* symbol = nullptr;
  Dso* dso = nullptr;
  const char* dso_name = nullptr;
  uint64_t vaddr_in_file = 0;
  const MapEntry* map = nullptr;
  CallChainExecutionType execution_type = CallChainExecutionType::NATIVE_METHOD;
};

// a base class for modifying callchain reports
class CallChainReportModifier {
 public:
  virtual ~CallChainReportModifier();

  virtual void Modify(std::vector<CallChainReportEntry>& callchain) = 0;
};

class CallChainReportBuilder {
 public:
  CallChainReportBuilder(ThreadTree& thread_tree);
  // If true, remove interpreter frames both before and after a Java frame.
  // Default is true.
  void SetRemoveArtFrame(bool enable);
  // If true, convert a JIT method into its corresponding interpreted Java method. So they can be
  // merged in reports like flamegraph. Default is true.
  void SetConvertJITFrame(bool enable);
  // Add proguard mapping.txt to de-obfuscate minified symbols.
  bool AddProguardMappingFile(std::string_view mapping_file);
  // Remove methods with name containing the given regular expression.
  bool RemoveMethod(std::string_view method_name_regex);
  std::vector<CallChainReportEntry> Build(const ThreadEntry* thread,
                                          const std::vector<uint64_t>& ips, size_t kernel_ip_count);

 private:
  void MarkArtFrame(std::vector<CallChainReportEntry>& callchain);

  ThreadTree& thread_tree_;
  bool remove_r8_synthesized_frame_ = false;
  std::unique_ptr<CallChainReportModifier> art_frame_remover_;
  std::unique_ptr<CallChainReportModifier> jit_frame_converter_;
  std::unique_ptr<CallChainReportModifier> java_method_deobfuscater_;
  std::unique_ptr<CallChainReportModifier> method_name_filter_;
};

struct ThreadReport {
  int pid;
  int tid;
  const char* thread_name;

  ThreadReport(int pid = 0, int tid = 0, const char* thread_name = nullptr)
      : pid(pid), tid(tid), thread_name(thread_name) {}
};

// Report thread info of a sample.
class ThreadReportBuilder {
 public:
  // Aggregate threads with names matching the same regex.
  bool AggregateThreads(const std::vector<std::string>& thread_name_regex);
  ThreadReport Build(const ThreadEntry& thread);

 private:
  void ModifyReportToAggregateThreads(ThreadReport& report);

  struct ThreadNameRegInfo {
    std::unique_ptr<RegEx> re;
    ThreadReport report;
  };

  std::vector<ThreadNameRegInfo> thread_regs_;
  // Map from thread name to the corresponding index in thread_regs_.
  // Return -1 if the thread name doesn't match any regular expression.
  std::unordered_map<std::string, int> thread_map_;
};

}  // namespace simpleperf
