// 极简 IR 定义：当前只支撑 i32 和加法，演示用。
// 可在此基础上扩展更多类型/指令
#pragma once

#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

namespace ir {

class Type;
class ConstantInt;

// IR 上下文：集中管理类型、常量等共享资源，便于复用与扩展。
class Context {
 public:
  ~Context();
  const std::shared_ptr<Type>& Void();
  const std::shared_ptr<Type>& Int32();
  // 去重创建 i32 常量。
  ConstantInt* GetConstInt(int v);
  // 生成临时名称，如 %t0、%t1 ...
  std::string NextTemp();

 private:
  std::shared_ptr<Type> void_;
  std::shared_ptr<Type> int32_;
  std::unordered_map<int, std::unique_ptr<ConstantInt>> const_ints_;
  int temp_index_ = 0;
};

Context& DefaultContext();

class Type {
 public:
  enum class Kind { Void, Int32 };
  explicit Type(Kind k) : kind_(k) {}
  Kind kind() const { return kind_; }
  static std::shared_ptr<Type> Void();
  static std::shared_ptr<Type> Int32();

 private:
  Kind kind_;
};

class Value {
 public:
  Value(std::shared_ptr<Type> ty, std::string name)
      : type_(std::move(ty)), name_(std::move(name)) {}
  virtual ~Value() = default;
  const std::shared_ptr<Type>& type() const { return type_; }
  const std::string& name() const { return name_; }
  void set_name(std::string n) { name_ = std::move(n); }

 protected:
  std::shared_ptr<Type> type_;
  std::string name_;
};

class ConstantInt : public Value {
 public:
  explicit ConstantInt(int v);
  int value() const { return value_; }

 private:
  int value_{};
};

enum class Opcode { Add, Sub, Mul, Ret };

class Instruction : public Value {
 public:
  Instruction(Opcode op, std::shared_ptr<Type> ty, std::string name = "")
      : Value(std::move(ty), std::move(name)), opcode_(op) {}
  Opcode opcode() const { return opcode_; }

 private:
  Opcode opcode_;
};

class BinaryInst : public Instruction {
 public:
  BinaryInst(Opcode op, std::shared_ptr<Type> ty, Value* lhs, Value* rhs,
             std::string name);
  Value* lhs() const { return lhs_; }
  Value* rhs() const { return rhs_; }

 private:
  Value* lhs_;
  Value* rhs_;
};

class ReturnInst : public Instruction {
 public:
  explicit ReturnInst(Value* val);
  Value* value() const { return value_; }

 private:
  Value* value_;
};

class BasicBlock {
 public:
  explicit BasicBlock(std::string name) : name_(std::move(name)) {}
  const std::string& name() const { return name_; }
  const std::vector<std::unique_ptr<Instruction>>& instructions() const {
    return instructions_;
  }
  template <typename T, typename... Args>
  T* Append(Args&&... args) {
    auto inst = std::make_unique<T>(std::forward<Args>(args)...);
    auto* ptr = inst.get();
    instructions_.push_back(std::move(inst));
    return ptr;
  }

 private:
  std::string name_;
  std::vector<std::unique_ptr<Instruction>> instructions_;
};

class Function : public Value {
 public:
  // 允许显式指定返回类型，便于后续扩展多种函数签名。
  Function(std::string name, std::shared_ptr<Type> ret_type);
  BasicBlock* entry() { return entry_.get(); }
  const BasicBlock* entry() const { return entry_.get(); }

 private:
  std::unique_ptr<BasicBlock> entry_;
};

class Module {
 public:
  // 创建函数时显式传入返回类型，便于在 IRGen 中根据 AST 选择类型。
  Function* CreateFunction(const std::string& name,
                           std::shared_ptr<Type> ret_type);
  const std::vector<std::unique_ptr<Function>>& functions() const {
    return functions_;
  }

 private:
  std::vector<std::unique_ptr<Function>> functions_;
};

class IRBuilder {
 public:
  explicit IRBuilder(BasicBlock* bb) : insertBlock_(bb) {}
  void SetInsertPoint(BasicBlock* bb) { insertBlock_ = bb; }
  BasicBlock* GetInsertBlock() const { return insertBlock_; }

  // 构造常量、二元运算、返回指令的最小集合。
  ConstantInt* CreateConstInt(int v);
  BinaryInst* CreateBinary(Opcode op, Value* lhs, Value* rhs,
                           const std::string& name);
  BinaryInst* CreateAdd(Value* lhs, Value* rhs, const std::string& name) {
    return CreateBinary(Opcode::Add, lhs, rhs, name);
  }
  ReturnInst* CreateRet(Value* v);

 private:
  BasicBlock* insertBlock_;
};

class IRPrinter {
 public:
  void Print(const Module& module);
};

}  // namespace ir
