// IR 文本输出：
// - 将 IR 打印为 .ll 风格的文本
// - 支撑调试与测试对比（diff）

#include "ir/IR.h"

#include <iostream>
#include <stdexcept>

namespace ir {

static const char* TypeToString(const Type& ty) {
  switch (ty.kind()) {
    case Type::Kind::Void:
      return "void";
    case Type::Kind::Int32:
      return "i32";
    case Type::Kind::PtrInt32:
      return "i32*";
  }
  throw std::runtime_error("未知类型");
}

static const char* OpcodeToString(Opcode op) {
  switch (op) {
    case Opcode::Add:
      return "add";
    case Opcode::Sub:
      return "sub";
    case Opcode::Mul:
      return "mul";
    case Opcode::Alloca:
      return "alloca";
    case Opcode::Load:
      return "load";
    case Opcode::Store:
      return "store";
    case Opcode::Ret:
      return "ret";
  }
  return "?";
}

void IRPrinter::Print(const Module& module) {
  for (const auto& func : module.functions()) {
    std::cout << "define " << TypeToString(*func->type()) << " @"
              << func->name() << "() {\n";
    const auto* bb = func->entry();
    if (!bb) {
      std::cout << "}\n";
      continue;
    }
    std::cout << "entry:\n";
    for (const auto& instPtr : bb->instructions()) {
      const auto* inst = instPtr.get();
      switch (inst->opcode()) {
        case Opcode::Add:
        case Opcode::Sub:
        case Opcode::Mul: {
          auto* bin = static_cast<const BinaryInst*>(inst);
          std::cout << "  " << bin->name() << " = " << OpcodeToString(bin->opcode())
                    << " " << TypeToString(*bin->lhs()->type()) << " "
                    << bin->lhs()->name() << ", " << bin->rhs()->name() << "\n";
          break;
        }
        case Opcode::Alloca: {
          auto* alloca = static_cast<const AllocaInst*>(inst);
          std::cout << "  " << alloca->name() << " = alloca i32\n";
          break;
        }
        case Opcode::Load: {
          auto* load = static_cast<const LoadInst*>(inst);
          std::cout << "  " << load->name() << " = load i32, i32* "
                    << load->ptr()->name() << "\n";
          break;
        }
        case Opcode::Store: {
          auto* store = static_cast<const StoreInst*>(inst);
          std::cout << "  store i32 " << store->value()->name() << ", i32* "
                    << store->ptr()->name() << "\n";
          break;
        }
        case Opcode::Ret: {
          auto* ret = static_cast<const ReturnInst*>(inst);
          std::cout << "  ret " << TypeToString(*ret->value()->type()) << " "
                    << ret->value()->name() << "\n";
          break;
        }
      }
    }
    std::cout << "}\n";
  }
}

}  // namespace ir
