/*
 * Copyright (C) 2014 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 ART_COMPILER_DEX_PASS_DRIVER_H_
#define ART_COMPILER_DEX_PASS_DRIVER_H_

#include <vector>
#include "pass.h"
#include "safe_map.h"

// Forward Declarations.
class Pass;
class PassDriver;
namespace art {
/**
 * @brief Helper function to create a single instance of a given Pass and can be shared across
 * the threads.
 */
template <typename PassType>
const Pass* GetPassInstance() {
  static const PassType pass;
  return &pass;
}

// Empty holder for the constructor.
class PassDriverDataHolder {
};

/**
 * @class PassDriver
 * @brief PassDriver is the wrapper around all Pass instances in order to execute them
 */
template <typename PassDriverType>
class PassDriver {
 public:
  explicit PassDriver() {
    InitializePasses();
  }

  virtual ~PassDriver() {
  }

  /**
   * @brief Insert a Pass: can warn if multiple passes have the same name.
   */
  void InsertPass(const Pass* new_pass) {
    DCHECK(new_pass != nullptr);
    DCHECK(new_pass->GetName() != nullptr && new_pass->GetName()[0] != 0);

    // It is an error to override an existing pass.
    DCHECK(GetPass(new_pass->GetName()) == nullptr)
        << "Pass name " << new_pass->GetName() << " already used.";

    // Now add to the list.
    pass_list_.push_back(new_pass);
  }

  /**
   * @brief Run a pass using the name as key.
   * @return whether the pass was applied.
   */
  virtual bool RunPass(const char* pass_name) {
    // Paranoid: c_unit cannot be nullptr and we need a pass name.
    DCHECK(pass_name != nullptr && pass_name[0] != 0);

    const Pass* cur_pass = GetPass(pass_name);

    if (cur_pass != nullptr) {
      return RunPass(cur_pass);
    }

    // Return false, we did not find the pass.
    return false;
  }

  /**
   * @brief Runs all the passes with the pass_list_.
   */
  void Launch() {
    for (const Pass* cur_pass : pass_list_) {
      RunPass(cur_pass);
    }
  }

  /**
   * @brief Searches for a particular pass.
   * @param the name of the pass to be searched for.
   */
  const Pass* GetPass(const char* name) const {
    for (const Pass* cur_pass : pass_list_) {
      if (strcmp(name, cur_pass->GetName()) == 0) {
        return cur_pass;
      }
    }
    return nullptr;
  }

  static void CreateDefaultPassList(const std::string& disable_passes) {
    // Insert each pass from g_passes into g_default_pass_list.
    PassDriverType::g_default_pass_list.clear();
    PassDriverType::g_default_pass_list.reserve(PassDriver<PassDriverType>::g_passes_size);
    for (uint16_t i = 0; i < PassDriver<PassDriverType>::g_passes_size; ++i) {
      const Pass* pass = PassDriver<PassDriverType>::g_passes[i];
      // Check if we should disable this pass.
      if (disable_passes.find(pass->GetName()) != std::string::npos) {
        LOG(INFO) << "Skipping " << pass->GetName();
      } else {
        PassDriver<PassDriverType>::g_default_pass_list.push_back(pass);
      }
    }
  }

  /**
   * @brief Run a pass using the Pass itself.
   * @param time_split do we want a time split request(default: false)?
   * @return whether the pass was applied.
   */
  virtual bool RunPass(const Pass* pass, bool time_split = false) = 0;

  /**
   * @brief Print the pass names of all the passes available.
   */
  static void PrintPassNames() {
    LOG(INFO) << "Loop Passes are:";

    for (const Pass* cur_pass : PassDriver<PassDriverType>::g_default_pass_list) {
      LOG(INFO) << "\t-" << cur_pass->GetName();
    }
  }

  /**
   * @brief Gets the list of passes currently schedule to execute.
   * @return pass_list_
   */
  std::vector<const Pass*>& GetPasses() {
    return pass_list_;
  }

  static void SetPrintAllPasses() {
    default_print_passes_ = true;
  }

  static void SetDumpPassList(const std::string& list) {
    dump_pass_list_ = list;
  }

  static void SetPrintPassList(const std::string& list) {
    print_pass_list_ = list;
  }

  void SetDefaultPasses() {
    pass_list_ = PassDriver<PassDriverType>::g_default_pass_list;
  }

 protected:
  virtual void InitializePasses() {
    SetDefaultPasses();
  }

  /**
   * @brief Apply a patch: perform start/work/end functions.
   */
  virtual void ApplyPass(PassDataHolder* data, const Pass* pass) {
    pass->Start(data);
    DispatchPass(pass);
    pass->End(data);
  }
  /**
   * @brief Dispatch a patch.
   * Gives the ability to add logic when running the patch.
   */
  virtual void DispatchPass(const Pass* pass) {
    UNUSED(pass);
  }

  /** @brief List of passes: provides the order to execute the passes. */
  std::vector<const Pass*> pass_list_;

  /** @brief The number of passes within g_passes.  */
  static const uint16_t g_passes_size;

  /** @brief The number of passes within g_passes.  */
  static const Pass* const g_passes[];

  /** @brief The default pass list is used to initialize pass_list_. */
  static std::vector<const Pass*> g_default_pass_list;

  /** @brief Do we, by default, want to be printing the log messages? */
  static bool default_print_passes_;

  /** @brief What are the passes we want to be printing the log messages? */
  static std::string print_pass_list_;

  /** @brief What are the passes we want to be dumping the CFG? */
  static std::string dump_pass_list_;
};

}  // namespace art
#endif  // ART_COMPILER_DEX_PASS_DRIVER_H_