// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_TOOLS_V8WINDBG_SRC_V8WINDBG_EXTENSION_H_
#define V8_TOOLS_V8WINDBG_SRC_V8WINDBG_EXTENSION_H_

#include <memory>
#include <unordered_map>
#include <vector>

#include "tools/v8windbg/base/utilities.h"

// Responsible for initializing and uninitializing the extension. Also provides
// various convenience functions.
class Extension {
 public:
  Extension();
  HRESULT Initialize();
  ~Extension();
  WRL::ComPtr<IDebugHostModule> GetV8Module(
      WRL::ComPtr<IDebugHostContext>& sp_ctx);
  WRL::ComPtr<IDebugHostType> GetTypeFromV8Module(
      WRL::ComPtr<IDebugHostContext>& sp_ctx, const char16_t* type_name);
  WRL::ComPtr<IDebugHostType> GetV8ObjectType(
      WRL::ComPtr<IDebugHostContext>& sp_ctx);
  void TryRegisterType(WRL::ComPtr<IDebugHostType>& sp_type,
                       std::u16string type_name);
  bool DoesTypeDeriveFromObject(const WRL::ComPtr<IDebugHostType>& sp_type);
  static Extension* Current() { return current_extension_.get(); }
  static void SetExtension(std::unique_ptr<Extension> new_extension) {
    current_extension_ = std::move(new_extension);
  }

  // Returns the parent model for instances of v8::internal::Object and similar
  // classes, which contain as their first and only field a tagged V8 value.
  IModelObject* GetObjectDataModel() { return sp_object_data_model_.Get(); }

  // Returns the parent model that provides indexing support for fields that
  // contain arrays of something more complicated than basic native types.
  IModelObject* GetIndexedFieldDataModel() {
    return sp_indexed_field_model_.Get();
  }

 private:
  HRESULT OverrideLocalsGetter(IModelObject* parent, const wchar_t* key_name,
                               bool is_parameters);

  template <class PropertyClass>
  HRESULT RegisterAndAddPropertyForClass(
      const wchar_t* class_name, const wchar_t* property_name,
      WRL::ComPtr<IModelObject> sp_data_model);

  // A property that has been overridden by this extension. The original value
  // must be put back in place during ~Extension.
  struct PropertyOverride {
    PropertyOverride();
    PropertyOverride(IModelObject* parent, std::u16string key_name,
                     IModelObject* original_value,
                     IKeyStore* original_metadata);
    ~PropertyOverride();
    PropertyOverride(const PropertyOverride&);
    PropertyOverride& operator=(const PropertyOverride&);
    WRL::ComPtr<IModelObject> parent;
    std::u16string key_name;
    WRL::ComPtr<IModelObject> original_value;
    WRL::ComPtr<IKeyStore> original_metadata;
  };

  struct RegistrationType {
    RegistrationType();
    RegistrationType(IDebugHostTypeSignature* sp_signature,
                     IModelObject* sp_data_model);
    ~RegistrationType();
    RegistrationType(const RegistrationType&);
    RegistrationType& operator=(const RegistrationType&);

    WRL::ComPtr<IDebugHostTypeSignature> sp_signature;
    WRL::ComPtr<IModelObject> sp_data_model;
  };

  static std::unique_ptr<Extension> current_extension_;

  WRL::ComPtr<IModelObject> sp_object_data_model_;
  WRL::ComPtr<IModelObject> sp_local_data_model_;
  WRL::ComPtr<IModelObject> sp_compiler_node_data_model_;
  WRL::ComPtr<IModelObject> sp_compiler_type_data_model_;
  WRL::ComPtr<IModelObject> sp_indexed_field_model_;

  WRL::ComPtr<IDebugHostModule> sp_v8_module_;
  std::unordered_map<std::u16string, WRL::ComPtr<IDebugHostType>>
      cached_v8_module_types_;
  std::vector<RegistrationType> registered_types_;
  std::vector<PropertyOverride> overridden_properties_;
  WRL::ComPtr<IDebugHostContext> sp_v8_module_ctx_;
  ULONG v8_module_proc_id_;
};
#endif  // V8_TOOLS_V8WINDBG_SRC_V8WINDBG_EXTENSION_H_