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

#include "ir/IR.h"

#include <ostream>
#include <stdexcept>
#include <string>

#include "utils/Log.h"

namespace ir {

static const char* TypeToString(const Type& ty) {
  switch (ty.GetKind()) {
    case Type::Kind::Void:      return "void";
    case Type::Kind::Int32:     return "i32";
    case Type::Kind::Float:     return "float";
    case Type::Kind::PtrInt32:  return "i32*";
    case Type::Kind::PtrFloat:  return "float*";
    case Type::Kind::Label:     return "label";
    case Type::Kind::Array:     return "array";
    case Type::Kind::Function:  return "function";
    case Type::Kind::Int1:      return "i1";
    case Type::Kind::PtrInt1:   return "i1*";
    default:                    return "unknown";
  }
  throw std::runtime_error(FormatError("ir", "未知类型"));
}

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";
    case Opcode::Call:
      return "call";
    case Opcode::Br:
      return "br";
    case Opcode::CondBr:
      return "condbr";
    case Opcode::Icmp:
      return "icmp";
    case Opcode::Div:
      return "div";
    case Opcode::Mod:
      return "mod";
    case Opcode::ZExt:
      return "zext";
    case Opcode::Trunc:
      return "trunc";
    case Opcode::And: 
      return "and";
    case Opcode::Or: 
      return "or";
    case Opcode::Not: 
      return "not";
    case Opcode::GEP: 
      return "getelementptr";
    case Opcode::FAdd: return "fadd";
    case Opcode::FSub: return "fsub";
    case Opcode::FMul: return "fmul";
    case Opcode::FDiv: return "fdiv";
    case Opcode::FCmp: return "fcmp";
    case Opcode::SIToFP: return "sitofp";
    case Opcode::FPToSI: return "fptosi";
    case Opcode::FPExt: return "fpext";
    case Opcode::FPTrunc: return "fptrunc";
  }
  return "?";
}

static std::string ValueToString(const Value* v) {
  if (auto* ci = dynamic_cast<const ConstantInt*>(v)) {
    return std::to_string(ci->GetValue());
  }
  if (!v) {
    return "<null>";
  }
  const auto& name = v->GetName();
  if (name.empty()) {
    return "<unnamed>";
  }
  if (name[0] == '%' || name[0] == '@') {
    return name;
  }
  if (dynamic_cast<const GlobalValue*>(v)) {
    return "@" + name;
  }
  return "%" + name;
}

void IRPrinter::Print(const Module& module, std::ostream& os) {
  for (const auto& global : module.GetGlobals()) {
    if (!global) continue;
    os << "@" << global->GetName() << " = global ";
    if (global->GetType()->IsPtrInt32()) {
      os << "i32 0\n";
    } else if (global->GetType()->IsPtrFloat()) {
      os << "float 0.0\n";
    } else {
      os << TypeToString(*global->GetType()) << " zeroinitializer\n";
    }
  }
  for (const auto& func : module.GetFunctions()) {
    auto* func_ty = static_cast<const FunctionType*>(func->GetType().get());
    os << "define " << TypeToString(*func_ty->GetReturnType()) << " @" << func->GetName() << "(";
    bool first = true;
    for (const auto& arg : func->GetArguments()) {
      if (!first) os << ", ";
      first = false;
      os << TypeToString(*arg->GetType()) << " %" << arg->GetName();
    }
    os << ") {\n";
    for (const auto& bb : func->GetBlocks()) {
      if (!bb) {
        continue;
      }
      os << bb->GetName() << ":\n";
      for (const auto& instPtr : bb->GetInstructions()) {
        const auto* inst = instPtr.get();
        switch (inst->GetOpcode()) {
          case Opcode::Add:
          case Opcode::Sub:
          case Opcode::Mul:
          case Opcode::Div:       
          case Opcode::Mod:       
          case Opcode::And:
          case Opcode::Not:        
          case Opcode::Or:        
          {
            auto* bin = static_cast<const BinaryInst*>(inst);
            os << "  " << bin->GetName() << " = "
               << OpcodeToString(bin->GetOpcode()) << " "
               << TypeToString(*bin->GetLhs()->GetType()) << " "
               << ValueToString(bin->GetLhs()) << ", "
               << ValueToString(bin->GetRhs()) << "\n";
            break;
          }
          case Opcode::Alloca: {
            auto* alloca = static_cast<const AllocaInst*>(inst);
            std::string elem_ty_str;
            if (alloca->GetType()->IsPtrInt32()) {
              elem_ty_str = "i32";
            } else if (alloca->GetType()->IsPtrFloat()) {
              elem_ty_str = "float";
            } else {
              elem_ty_str = TypeToString(*alloca->GetType());
            }
            os << "  " << alloca->GetName() << " = alloca " << elem_ty_str << "\n";
            break;
          }
          case Opcode::Load: {
            auto* load = static_cast<const LoadInst*>(inst);
            os << "  " << load->GetName() << " = load "
               << TypeToString(*load->GetType()) << ", "
               << TypeToString(*load->GetPtr()->GetType()) << " "
               << ValueToString(load->GetPtr()) << "\n";
            break;
          }
          case Opcode::Store: {
            auto* store = static_cast<const StoreInst*>(inst);
            os << "  store " << TypeToString(*store->GetValue()->GetType()) << " "
               << ValueToString(store->GetValue())
               << ", " << TypeToString(*store->GetPtr()->GetType()) << " "
               << ValueToString(store->GetPtr()) << "\n";
            break;
          }
          case Opcode::Ret: {
            auto* ret = static_cast<const ReturnInst*>(inst);
            os << "  ret " << TypeToString(*ret->GetValue()->GetType()) << " "
               << ValueToString(ret->GetValue()) << "\n";
            break;
          }
          // CallInst类在 include/ir/IR.h 中定义
          case Opcode::Call: {
            auto* call = static_cast<const CallInst*>(inst);
            os << "  " << call->GetName() << " = call "
               << TypeToString(*call->GetType()) << " @" << call->GetCallee()->GetName() << "(";
            bool first = true;
            for (auto* arg : call->GetArgs()) {
              if (!first) os << ", ";
              first = false;
              os << TypeToString(*arg->GetType()) << " " << ValueToString(arg);
            }
            os << ")\n";
            break;
          }

          // 在 IRPrinter.cpp 的 switch 语句中添加
          case Opcode::Br:
          case Opcode::CondBr: {
              auto* br = static_cast<const BranchInst*>(inst);
              if (!br->IsConditional()) {
                  os << "  br label %" << br->GetTarget()->GetName() << "\n";
              } else {
                  os << "  br i1 " << ValueToString(br->GetCondition())
                    << ", label %" << br->GetTrueTarget()->GetName()
                    << ", label %" << br->GetFalseTarget()->GetName() << "\n";
              }
              break;
          }

          case Opcode::Icmp: {
              auto* icmp = static_cast<const IcmpInst*>(inst);
              os << "  " << icmp->GetName() << " = icmp ";
              switch (icmp->GetPredicate()) {
                  case IcmpInst::Predicate::EQ: os << "eq"; break;
                  case IcmpInst::Predicate::NE: os << "ne"; break;
                  case IcmpInst::Predicate::LT: os << "slt"; break;
                  case IcmpInst::Predicate::LE: os << "sle"; break;
                  case IcmpInst::Predicate::GT: os << "sgt"; break;
                  case IcmpInst::Predicate::GE: os << "sge"; break;
              }
              os << " " << TypeToString(*icmp->GetLhs()->GetType())
                << " " << ValueToString(icmp->GetLhs())
                << ", " << ValueToString(icmp->GetRhs()) << "\n";
              break;
          }

          case Opcode::ZExt: {
              auto* zext = static_cast<const ZExtInst*>(inst);
              os << "  " << zext->GetName() << " = zext "
                << TypeToString(*zext->GetSourceType()) << " "
                << ValueToString(zext->GetValue()) << " to "
                << TypeToString(*zext->GetTargetType()) << "\n";
              break;
          }

          case Opcode::Trunc: {
              auto* trunc = static_cast<const TruncInst*>(inst);
              os << "  " << trunc->GetName() << " = trunc "
                << TypeToString(*trunc->GetSourceType()) << " "
                << ValueToString(trunc->GetValue()) << " to "
                << TypeToString(*trunc->GetTargetType()) << "\n";
              break;
          }
          case Opcode::GEP:{
            // 简化打印：只打印基本信息和操作数数量
            os << "  " << inst->GetName() << " = getelementptr ";
            os << TypeToString(*inst->GetType()) << " (";
            for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
              if (i > 0) os << ", ";
              os << ValueToString(inst->GetOperand(i));
            }
            os << ")\n";
            break;
          }
          default: {
            // 处理未知操作码
            os << "  ; 未知指令: " << OpcodeToString(inst->GetOpcode()) << "\n";
            break;
          }
        }
      }
    }
    os << "}\n";
  }
}

void IRPrinter::PrintConstant(const ConstantValue* constant, std::ostream& os) {
  if (auto* const_int = dynamic_cast<const ConstantInt*>(constant)) {
    os << const_int->GetValue();
  } 
  else if (auto* const_float = dynamic_cast<const ConstantFloat*>(constant)) {
    os << const_float->GetValue();
  }
  else if (auto* const_array = dynamic_cast<const ConstantArray*>(constant)) {
    os << "[";
    auto& elements = const_array->GetElements();
    for (size_t i = 0; i < elements.size(); ++i) {
      if (i > 0) os << ", ";
      PrintConstant(elements[i], os);
    }
    os << "]";
  }
  else if (auto* zero = dynamic_cast<const ConstantZero*>(constant)) {
    os << "zero";
  }
  else if (auto* agg_zero = dynamic_cast<const ConstantAggregateZero*>(constant)) {
    os << "zeroinitializer";
  }
}

}  // namespace ir
