#include "irgen/IRGen.h"

#include <cmath>
#include <stdexcept>
#include <type_traits>
#include <vector>

#include "SysYParser.h"
#include "ir/IR.h"
#include "utils/Log.h"

namespace {

bool IsZero(const ir::ConstantValue* value) {
  if (auto* ci = dynamic_cast<const ir::ConstantInt*>(value)) {
    return ci->GetValue() == 0;
  }
  if (auto* cf = dynamic_cast<const ir::ConstantFloat*>(value)) {
    return cf->GetValue() == 0.0f;
  }
  return false;
}

bool IsTruthy(const ir::ConstantValue* value) {
  return !IsZero(value);
}

int AsInt(const ir::ConstantValue* value) {
  if (auto* ci = dynamic_cast<const ir::ConstantInt*>(value)) {
    return ci->GetValue();
  }
  if (auto* cf = dynamic_cast<const ir::ConstantFloat*>(value)) {
    return static_cast<int>(cf->GetValue());
  }
  throw std::runtime_error(FormatError("irgen", "无法将常量转换为 int"));
}

float AsFloat(const ir::ConstantValue* value) {
  if (auto* cf = dynamic_cast<const ir::ConstantFloat*>(value)) {
    return cf->GetValue();
  }
  if (auto* ci = dynamic_cast<const ir::ConstantInt*>(value)) {
    return static_cast<float>(ci->GetValue());
  }
  throw std::runtime_error(FormatError("irgen", "无法将常量转换为 float"));
}

std::shared_ptr<ir::Type> ScalarPointerType(std::shared_ptr<ir::Type> ty) {
  if (ty->IsInt32()) return ir::Type::GetPtrInt32Type();
  if (ty->IsFloat()) return ir::Type::GetPtrFloatType();
  return ty;
}

std::shared_ptr<ir::Type> CommonArithType(ir::Value* lhs, ir::Value* rhs) {
  if (lhs->GetType()->IsFloat() || rhs->GetType()->IsFloat()) {
    return ir::Type::GetFloatType();
  }
  return ir::Type::GetInt32Type();
}

}  // namespace

ir::Value* IRGenImpl::EvalExpr(SysYParser::ExpContext& expr) {
  return std::any_cast<ir::Value*>(expr.accept(this));
}

ir::ConstantValue* IRGenImpl::EvalConstExpr(SysYParser::ExpContext& expr) {
  class ConstExprVisitor final : public SysYBaseVisitor {
   public:
    ConstExprVisitor(ir::Module& module, const SemanticContext& sema)
        : module_(module), sema_(sema) {}

    std::any visitParenExp(SysYParser::ParenExpContext* ctx) override {
      return Eval(*ctx->exp());
    }

    std::any visitNumberExp(SysYParser::NumberExpContext* ctx) override {
      if (ctx->number()->ILITERAL()) {
        const std::string text = ctx->number()->ILITERAL()->getText();
        int value = 0;
        if (text.size() > 2 && (text[1] == 'x' || text[1] == 'X')) {
          value = std::stoi(text, nullptr, 16);
        } else if (text.size() > 1 && text[0] == '0') {
          value = std::stoi(text, nullptr, 8);
        } else {
          value = std::stoi(text, nullptr, 10);
        }
        return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstInt(value));
      }
      return static_cast<ir::ConstantValue*>(
          module_.GetContext().GetConstFloat(static_cast<float>(std::stod(ctx->number()->FLITERAL()->getText()))));
    }

    std::any visitLValueExp(SysYParser::LValueExpContext* ctx) override {
      auto* def = sema_.ResolveLValue(ctx->lValue());
      if (!def) {
        throw std::runtime_error(FormatError("irgen", "常量表达式引用了未绑定左值"));
      }
      if (!ctx->lValue()->exp().empty()) {
        throw std::runtime_error(
            FormatError("irgen", "暂不支持在常量表达式中访问数组元素"));
      }
      if (auto* const_def = dynamic_cast<SysYParser::ConstDefContext*>(def)) {
        if (!const_def->initValue() || !const_def->initValue()->exp()) {
          throw std::runtime_error(
              FormatError("irgen", "常量缺少标量初始化表达式"));
        }
        auto* init = Eval(*const_def->initValue()->exp());
        auto* decl = dynamic_cast<SysYParser::ConstDeclContext*>(const_def->parent);
        bool is_float = (decl && decl->btype() && decl->btype()->FLOAT());
        if (!is_float && init->GetType()->IsFloat()) {
          init = module_.GetContext().GetConstInt(
              static_cast<int>(static_cast<ir::ConstantFloat*>(init)->GetValue()));
        } else if (is_float && init->GetType()->IsInt32()) {
          init = module_.GetContext().GetConstFloat(
              static_cast<float>(static_cast<ir::ConstantInt*>(init)->GetValue()));
        }
        return init;
      }
      throw std::runtime_error(
          FormatError("irgen", "全局/常量表达式必须是编译期常量"));
    }

    std::any visitUnaryAddExp(SysYParser::UnaryAddExpContext* ctx) override {
      return Eval(*ctx->exp());
    }

    std::any visitUnarySubExp(SysYParser::UnarySubExpContext* ctx) override {
      auto* value = Eval(*ctx->exp());
      if (value->GetType()->IsFloat()) {
        return static_cast<ir::ConstantValue*>(
            module_.GetContext().GetConstFloat(-AsFloat(value)));
      }
      return static_cast<ir::ConstantValue*>(
          module_.GetContext().GetConstInt(-AsInt(value)));
    }

    std::any visitNotExp(SysYParser::NotExpContext* ctx) override {
      return static_cast<ir::ConstantValue*>(
          module_.GetContext().GetConstInt(IsTruthy(Eval(*ctx->exp())) ? 0 : 1));
    }

    std::any visitMulDivModExp(SysYParser::MulDivModExpContext* ctx) override {
      auto* lhs = Eval(*ctx->exp(0));
      auto* rhs = Eval(*ctx->exp(1));
      
      bool is_mul = ctx->MUL() != nullptr;
      bool is_div = ctx->DIV() != nullptr;
      bool is_mod = ctx->MOD() != nullptr;

      if (is_mod) {
        return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstInt(
            AsInt(rhs) == 0 ? 0 : AsInt(lhs) % AsInt(rhs)));
      }

      if (lhs->GetType()->IsFloat() || rhs->GetType()->IsFloat()) {
        const float lv = AsFloat(lhs);
        const float rv = AsFloat(rhs);
        if (is_mul) return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstFloat(lv * rv));
        else return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstFloat(rv == 0.0f ? 0.0f : lv / rv));
      }
      const int lv = AsInt(lhs);
      const int rv = AsInt(rhs);
      if (is_mul) return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstInt(lv * rv));
      else return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstInt(rv == 0 ? 0 : lv / rv));
    }

    std::any visitAddSubExp(SysYParser::AddSubExpContext* ctx) override {
      auto* lhs = Eval(*ctx->exp(0));
      auto* rhs = Eval(*ctx->exp(1));
      bool is_sub = ctx->SUB() != nullptr;
      if (lhs->GetType()->IsFloat() || rhs->GetType()->IsFloat()) {
        const float lv = AsFloat(lhs);
        const float rv = AsFloat(rhs);
        return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstFloat(is_sub ? lv - rv : lv + rv));
      }
      const int lv = AsInt(lhs);
      const int rv = AsInt(rhs);
      return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstInt(is_sub ? lv - rv : lv + rv));
    }

    std::any visitRelExp(SysYParser::RelExpContext* ctx) override {
      ir::Opcode op = ir::Opcode::ICmpLT;
      if (ctx->LT()) op = ir::Opcode::ICmpLT;
      else if (ctx->LE()) op = ir::Opcode::ICmpLE;
      else if (ctx->GT()) op = ir::Opcode::ICmpGT;
      else if (ctx->GE()) op = ir::Opcode::ICmpGE;
      return EvalCmpImpl(*ctx->exp(0), *ctx->exp(1), op);
    }

    std::any visitEqNeExp(SysYParser::EqNeExpContext* ctx) override {
      ir::Opcode op = ir::Opcode::ICmpEQ;
      if (ctx->EQ()) op = ir::Opcode::ICmpEQ;
      else if (ctx->NE()) op = ir::Opcode::ICmpNE;
      return EvalCmpImpl(*ctx->exp(0), *ctx->exp(1), op);
    }

    std::any visitAndExp(SysYParser::AndExpContext* ctx) override {
      auto* lhs = Eval(*ctx->exp(0));
      if (!IsTruthy(lhs)) {
        return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstInt(0));
      }
      return static_cast<ir::ConstantValue*>(
          module_.GetContext().GetConstInt(IsTruthy(Eval(*ctx->exp(1))) ? 1 : 0));
    }

    std::any visitOrExp(SysYParser::OrExpContext* ctx) override {
      auto* lhs = Eval(*ctx->exp(0));
      if (IsTruthy(lhs)) {
        return static_cast<ir::ConstantValue*>(module_.GetContext().GetConstInt(1));
      }
      return static_cast<ir::ConstantValue*>(
          module_.GetContext().GetConstInt(IsTruthy(Eval(*ctx->exp(1))) ? 1 : 0));
    }

    ir::ConstantValue* Eval(SysYParser::ExpContext& ctx) {
      return std::any_cast<ir::ConstantValue*>(ctx.accept(this));
    }

   private:
    ir::ConstantValue* EvalCmpImpl(SysYParser::ExpContext& lhs_ctx,
                                   SysYParser::ExpContext& rhs_ctx,
                                   ir::Opcode op) {
      auto* lhs = Eval(lhs_ctx);
      auto* rhs = Eval(rhs_ctx);
      bool result = false;
      if (lhs->GetType()->IsFloat() || rhs->GetType()->IsFloat()) {
        const float a = AsFloat(lhs);
        const float b = AsFloat(rhs);
        switch (op) {
          case ir::Opcode::ICmpLT: result = a < b; break;
          case ir::Opcode::ICmpLE: result = a <= b; break;
          case ir::Opcode::ICmpGT: result = a > b; break;
          case ir::Opcode::ICmpGE: result = a >= b; break;
          case ir::Opcode::ICmpEQ: result = a == b; break;
          case ir::Opcode::ICmpNE: result = a != b; break;
          default: break;
        }
      } else {
        const int a = AsInt(lhs);
        const int b = AsInt(rhs);
        switch (op) {
          case ir::Opcode::ICmpLT: result = a < b; break;
          case ir::Opcode::ICmpLE: result = a <= b; break;
          case ir::Opcode::ICmpGT: result = a > b; break;
          case ir::Opcode::ICmpGE: result = a >= b; break;
          case ir::Opcode::ICmpEQ: result = a == b; break;
          case ir::Opcode::ICmpNE: result = a != b; break;
          default: break;
        }
      }
      return module_.GetContext().GetConstInt(result ? 1 : 0);
    }

    ir::Module& module_;
    const SemanticContext& sema_;
  };

  ConstExprVisitor visitor(module_, sema_);
  return visitor.Eval(expr);
}

static ir::Value* CastValue(IRGenImpl& gen, ir::IRBuilder& builder, ir::Module& module,
                            ir::Value* value, std::shared_ptr<ir::Type> target_ty) {
  if (value->GetType() == target_ty) return value;
  if (target_ty->IsFloat() && value->GetType()->IsInt32()) {
    if (auto* ci = dynamic_cast<ir::ConstantInt*>(value)) {
      return module.GetContext().GetConstFloat(static_cast<float>(ci->GetValue()));
    }
    return builder.CreateSIToFP(value, target_ty, module.GetContext().NextTemp());
  }
  if (target_ty->IsInt32() && value->GetType()->IsFloat()) {
    if (auto* cf = dynamic_cast<ir::ConstantFloat*>(value)) {
      return module.GetContext().GetConstInt(static_cast<int>(cf->GetValue()));
    }
    return builder.CreateFPToSI(value, target_ty, module.GetContext().NextTemp());
  }
  return value;
}

std::any IRGenImpl::visitParenExp(SysYParser::ParenExpContext* ctx) {
  return EvalExpr(*ctx->exp());
}

std::any IRGenImpl::visitLValueExp(SysYParser::LValueExpContext* ctx) {
  auto* def = sema_.ResolveLValue(ctx->lValue());
  if (def && IsArrayLikeDef(def) && ctx->lValue()->exp().size() < GetArrayRank(def)) {
    return DecayArrayPtr(ctx->lValue());
  }
  ir::Value* ptr = GetLValuePtr(ctx->lValue());
  return static_cast<ir::Value*>(
      builder_.CreateLoad(ptr, module_.GetContext().NextTemp()));
}

std::any IRGenImpl::visitNumberExp(SysYParser::NumberExpContext* ctx) {
  return static_cast<ir::Value*>(EvalConstExpr(*ctx));
}

std::any IRGenImpl::visitFuncCallExp(SysYParser::FuncCallExpContext* ctx) {
  ir::Function* target_func = nullptr;
  if (auto* def = sema_.ResolveFuncCall(ctx)) {
    const std::string func_name = def->ID()->getText();
    for (const auto& f : module_.GetFunctions()) {
      if (f->GetName() == func_name) {
        target_func = f.get();
        break;
      }
    }
  } else {
    const std::string func_name = ctx->ID()->getText();
    for (const auto& f : module_.GetFunctions()) {
      if (f->GetName() == func_name) {
        target_func = f.get();
        break;
      }
    }
  }
  if (!target_func) {
    throw std::runtime_error(FormatError("irgen", "找不到函数: " + ctx->ID()->getText()));
  }

  std::vector<ir::Value*> args;
  const auto& arg_types = target_func->GetArguments();
  if (ctx->funcRParams()) {
    const auto& exps = ctx->funcRParams()->exp();
    for (size_t i = 0; i < exps.size(); ++i) {
      ir::Value* arg = EvalExpr(*exps[i]);
      if (i < arg_types.size()) {
        arg = CastValue(*this, builder_, module_, arg, arg_types[i]->GetType());
      }
      args.push_back(arg);
    }
  }

  return static_cast<ir::Value*>(
      builder_.CreateCall(target_func, args,
                          target_func->GetType()->IsVoid()
                              ? ""
                              : module_.GetContext().NextTemp()));
}

std::any IRGenImpl::visitNotExp(SysYParser::NotExpContext* ctx) {
  ir::Value* val = EvalExpr(*ctx->exp());
  if (auto* cv = dynamic_cast<ir::ConstantValue*>(val)) {
    return static_cast<ir::Value*>(
        module_.GetContext().GetConstInt(IsTruthy(cv) ? 0 : 1));
  }
  ir::Value* cond = ToI1(val);
  ir::Value* res =
      builder_.CreateICmp(ir::Opcode::ICmpEQ, cond, builder_.CreateConstInt(0),
                          module_.GetContext().NextTemp());
  return ToI32(res);
}

std::any IRGenImpl::visitUnaryAddExp(SysYParser::UnaryAddExpContext* ctx) {
  return EvalExpr(*ctx->exp());
}

std::any IRGenImpl::visitUnarySubExp(SysYParser::UnarySubExpContext* ctx) {
  ir::Value* val = EvalExpr(*ctx->exp());
  if (auto* ci = dynamic_cast<ir::ConstantInt*>(val)) {
    return static_cast<ir::Value*>(module_.GetContext().GetConstInt(-ci->GetValue()));
  }
  if (auto* cf = dynamic_cast<ir::ConstantFloat*>(val)) {
    return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(-cf->GetValue()));
  }
  if (val->GetType()->IsFloat()) {
    return static_cast<ir::Value*>(builder_.CreateFSub(
        builder_.CreateConstFloat(0.0f), val, module_.GetContext().NextTemp()));
  }
  return static_cast<ir::Value*>(builder_.CreateSub(
      builder_.CreateConstInt(0), val, module_.GetContext().NextTemp()));
}

#define DEFINE_ARITH_VISITOR(name, int_opcode, float_opcode)                         \
  std::any IRGenImpl::visit##name##Exp(SysYParser::name##ExpContext* ctx) {         \
    ir::Value* lhs = EvalExpr(*ctx->exp(0));                                         \
    ir::Value* rhs = EvalExpr(*ctx->exp(1));                                         \
    const auto common_ty = CommonArithType(lhs, rhs);                                \
    lhs = CastValue(*this, builder_, module_, lhs, common_ty);                       \
    rhs = CastValue(*this, builder_, module_, rhs, common_ty);                       \
    if (auto* lconst = dynamic_cast<ir::ConstantValue*>(lhs)) {                      \
      if (auto* rconst = dynamic_cast<ir::ConstantValue*>(rhs)) {                    \
        if (common_ty->IsFloat()) {                                                  \
          const float lv = AsFloat(lconst);                                          \
          const float rv = AsFloat(rconst);                                          \
          if constexpr (ir::Opcode::float_opcode == ir::Opcode::FAdd)                \
            return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(lv + rv)); \
          if constexpr (ir::Opcode::float_opcode == ir::Opcode::FSub)                \
            return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(lv - rv)); \
          if constexpr (ir::Opcode::float_opcode == ir::Opcode::FMul)                \
            return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(lv * rv)); \
          return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(rv == 0.0f ? 0.0f : lv / rv)); \
        }                                                                            \
        const int lv = AsInt(lconst);                                                \
        const int rv = AsInt(rconst);                                                \
        if constexpr (ir::Opcode::int_opcode == ir::Opcode::Add)                     \
          return static_cast<ir::Value*>(module_.GetContext().GetConstInt(lv + rv)); \
        if constexpr (ir::Opcode::int_opcode == ir::Opcode::Sub)                     \
          return static_cast<ir::Value*>(module_.GetContext().GetConstInt(lv - rv)); \
        if constexpr (ir::Opcode::int_opcode == ir::Opcode::Mul)                     \
          return static_cast<ir::Value*>(module_.GetContext().GetConstInt(lv * rv)); \
        return static_cast<ir::Value*>(module_.GetContext().GetConstInt(rv == 0 ? 0 : lv / rv)); \
      }                                                                              \
    }                                                                                \
    if (common_ty->IsFloat()) {                                                      \
      if constexpr (ir::Opcode::float_opcode == ir::Opcode::FAdd)                    \
        return static_cast<ir::Value*>(builder_.CreateFAdd(lhs, rhs, module_.GetContext().NextTemp())); \
      if constexpr (ir::Opcode::float_opcode == ir::Opcode::FSub)                    \
        return static_cast<ir::Value*>(builder_.CreateFSub(lhs, rhs, module_.GetContext().NextTemp())); \
      if constexpr (ir::Opcode::float_opcode == ir::Opcode::FMul)                    \
        return static_cast<ir::Value*>(builder_.CreateFMul(lhs, rhs, module_.GetContext().NextTemp())); \
      return static_cast<ir::Value*>(builder_.CreateFDiv(lhs, rhs, module_.GetContext().NextTemp())); \
    }                                                                                \
    return static_cast<ir::Value*>(builder_.CreateBinary(ir::Opcode::int_opcode, lhs, rhs, module_.GetContext().NextTemp())); \
  }

std::any IRGenImpl::visitMulDivModExp(SysYParser::MulDivModExpContext* ctx) {
  ir::Value* lhs = EvalExpr(*ctx->exp(0));
  ir::Value* rhs = EvalExpr(*ctx->exp(1));
  
  bool is_mul = ctx->MUL() != nullptr;
  bool is_div = ctx->DIV() != nullptr;
  bool is_mod = ctx->MOD() != nullptr;

  if (is_mod) {
    lhs = CastValue(*this, builder_, module_, lhs, ir::Type::GetInt32Type());
    rhs = CastValue(*this, builder_, module_, rhs, ir::Type::GetInt32Type());
    if (auto* lconst = dynamic_cast<ir::ConstantValue*>(lhs)) {
      if (auto* rconst = dynamic_cast<ir::ConstantValue*>(rhs)) {
        const int rv = AsInt(rconst);
        return static_cast<ir::Value*>(module_.GetContext().GetConstInt(rv == 0 ? 0 : AsInt(lconst) % rv));
      }
    }
    return static_cast<ir::Value*>(builder_.CreateMod(lhs, rhs, module_.GetContext().NextTemp()));
  }

  const auto common_ty = CommonArithType(lhs, rhs);
  lhs = CastValue(*this, builder_, module_, lhs, common_ty);
  rhs = CastValue(*this, builder_, module_, rhs, common_ty);

  if (auto* lconst = dynamic_cast<ir::ConstantValue*>(lhs)) {
    if (auto* rconst = dynamic_cast<ir::ConstantValue*>(rhs)) {
      if (common_ty->IsFloat()) {
        const float lv = AsFloat(lconst);
        const float rv = AsFloat(rconst);
        if (is_mul) return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(lv * rv));
        else return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(rv == 0.0f ? 0.0f : lv / rv));
      }
      const int lv = AsInt(lconst);
      const int rv = AsInt(rconst);
      if (is_mul) return static_cast<ir::Value*>(module_.GetContext().GetConstInt(lv * rv));
      else return static_cast<ir::Value*>(module_.GetContext().GetConstInt(rv == 0 ? 0 : lv / rv));
    }
  }

  if (common_ty->IsFloat()) {
    if (is_mul) return static_cast<ir::Value*>(builder_.CreateFMul(lhs, rhs, module_.GetContext().NextTemp()));
    else return static_cast<ir::Value*>(builder_.CreateFDiv(lhs, rhs, module_.GetContext().NextTemp()));
  }
  if (is_mul) return static_cast<ir::Value*>(builder_.CreateBinary(ir::Opcode::Mul, lhs, rhs, module_.GetContext().NextTemp()));
  else return static_cast<ir::Value*>(builder_.CreateBinary(ir::Opcode::Div, lhs, rhs, module_.GetContext().NextTemp()));
}

std::any IRGenImpl::visitAddSubExp(SysYParser::AddSubExpContext* ctx) {
  ir::Value* lhs = EvalExpr(*ctx->exp(0));
  ir::Value* rhs = EvalExpr(*ctx->exp(1));
  const auto common_ty = CommonArithType(lhs, rhs);
  lhs = CastValue(*this, builder_, module_, lhs, common_ty);
  rhs = CastValue(*this, builder_, module_, rhs, common_ty);

  bool is_sub = ctx->SUB() != nullptr;

  if (auto* lconst = dynamic_cast<ir::ConstantValue*>(lhs)) {
    if (auto* rconst = dynamic_cast<ir::ConstantValue*>(rhs)) {
      if (common_ty->IsFloat()) {
        const float lv = AsFloat(lconst);
        const float rv = AsFloat(rconst);
        return static_cast<ir::Value*>(module_.GetContext().GetConstFloat(is_sub ? lv - rv : lv + rv));
      }
      const int lv = AsInt(lconst);
      const int rv = AsInt(rconst);
      return static_cast<ir::Value*>(module_.GetContext().GetConstInt(is_sub ? lv - rv : lv + rv));
    }
  }

  if (common_ty->IsFloat()) {
    if (is_sub) return static_cast<ir::Value*>(builder_.CreateFSub(lhs, rhs, module_.GetContext().NextTemp()));
    else return static_cast<ir::Value*>(builder_.CreateFAdd(lhs, rhs, module_.GetContext().NextTemp()));
  }
  if (is_sub) return static_cast<ir::Value*>(builder_.CreateBinary(ir::Opcode::Sub, lhs, rhs, module_.GetContext().NextTemp()));
  else return static_cast<ir::Value*>(builder_.CreateBinary(ir::Opcode::Add, lhs, rhs, module_.GetContext().NextTemp()));
}

std::any IRGenImpl::visitRelExp(SysYParser::RelExpContext* ctx) {
  ir::Value* lhs = EvalExpr(*ctx->exp(0));
  ir::Value* rhs = EvalExpr(*ctx->exp(1));
  const auto common_ty = CommonArithType(lhs, rhs);
  lhs = CastValue(*this, builder_, module_, lhs, common_ty);
  rhs = CastValue(*this, builder_, module_, rhs, common_ty);

  ir::Opcode int_op = ir::Opcode::ICmpLT;
  ir::Opcode float_op = ir::Opcode::FCmpLT;
  bool is_lt = ctx->LT() != nullptr;
  bool is_le = ctx->LE() != nullptr;
  bool is_gt = ctx->GT() != nullptr;
  bool is_ge = ctx->GE() != nullptr;

  if (is_lt) { int_op = ir::Opcode::ICmpLT; float_op = ir::Opcode::FCmpLT; }
  else if (is_le) { int_op = ir::Opcode::ICmpLE; float_op = ir::Opcode::FCmpLE; }
  else if (is_gt) { int_op = ir::Opcode::ICmpGT; float_op = ir::Opcode::FCmpGT; }
  else if (is_ge) { int_op = ir::Opcode::ICmpGE; float_op = ir::Opcode::FCmpGE; }

  if (auto* lconst = dynamic_cast<ir::ConstantValue*>(lhs)) {
    if (auto* rconst = dynamic_cast<ir::ConstantValue*>(rhs)) {
      bool result = false;
      if (common_ty->IsFloat()) {
        float lv = AsFloat(lconst);
        float rv = AsFloat(rconst);
        if (is_lt) result = lv < rv;
        else if (is_le) result = lv <= rv;
        else if (is_gt) result = lv > rv;
        else if (is_ge) result = lv >= rv;
      } else {
        int lv = AsInt(lconst);
        int rv = AsInt(rconst);
        if (is_lt) result = lv < rv;
        else if (is_le) result = lv <= rv;
        else if (is_gt) result = lv > rv;
        else if (is_ge) result = lv >= rv;
      }
      return static_cast<ir::Value*>(module_.GetContext().GetConstInt(result ? 1 : 0));
    }
  }

  if (common_ty->IsFloat()) {
    return static_cast<ir::Value*>(builder_.CreateFCmp(float_op, lhs, rhs, module_.GetContext().NextTemp()));
  }
  return static_cast<ir::Value*>(builder_.CreateICmp(int_op, lhs, rhs, module_.GetContext().NextTemp()));
}

std::any IRGenImpl::visitEqNeExp(SysYParser::EqNeExpContext* ctx) {
  ir::Value* lhs = EvalExpr(*ctx->exp(0));
  ir::Value* rhs = EvalExpr(*ctx->exp(1));
  const auto common_ty = CommonArithType(lhs, rhs);
  lhs = CastValue(*this, builder_, module_, lhs, common_ty);
  rhs = CastValue(*this, builder_, module_, rhs, common_ty);

  ir::Opcode int_op = ir::Opcode::ICmpEQ;
  ir::Opcode float_op = ir::Opcode::FCmpEQ;
  bool is_eq = ctx->EQ() != nullptr;

  if (is_eq) { int_op = ir::Opcode::ICmpEQ; float_op = ir::Opcode::FCmpEQ; }
  else { int_op = ir::Opcode::ICmpNE; float_op = ir::Opcode::FCmpNE; }

  if (auto* lconst = dynamic_cast<ir::ConstantValue*>(lhs)) {
    if (auto* rconst = dynamic_cast<ir::ConstantValue*>(rhs)) {
      bool result = false;
      if (common_ty->IsFloat()) {
        float lv = AsFloat(lconst);
        float rv = AsFloat(rconst);
        result = is_eq ? (lv == rv) : (lv != rv);
      } else {
        int lv = AsInt(lconst);
        int rv = AsInt(rconst);
        result = is_eq ? (lv == rv) : (lv != rv);
      }
      return static_cast<ir::Value*>(module_.GetContext().GetConstInt(result ? 1 : 0));
    }
  }

  if (common_ty->IsFloat()) {
    return static_cast<ir::Value*>(builder_.CreateFCmp(float_op, lhs, rhs, module_.GetContext().NextTemp()));
  }
  return static_cast<ir::Value*>(builder_.CreateICmp(int_op, lhs, rhs, module_.GetContext().NextTemp()));
}

std::any IRGenImpl::visitAndExp(SysYParser::AndExpContext* ctx) {
  if (!builder_.GetInsertBlock()) {
    return static_cast<ir::Value*>(EvalConstExpr(*ctx));
  }

  ir::Value* lhs = EvalExpr(*ctx->exp(0));
  if (auto* c = dynamic_cast<ir::ConstantValue*>(lhs); c && !IsTruthy(c)) {
    return static_cast<ir::Value*>(module_.GetContext().GetConstInt(0));
  }

  const std::string suffix = module_.GetContext().NextTemp();
  ir::BasicBlock* rhs_bb = func_->CreateBlock("and.rhs." + suffix);
  ir::BasicBlock* merge_bb = func_->CreateBlock("and.merge." + suffix);
  auto* res_ptr = builder_.CreateAllocaI32(module_.GetContext().NextTemp());

  builder_.CreateStore(ToI32(ToI1(lhs)), res_ptr);
  builder_.CreateCondBr(ToI1(lhs), rhs_bb, merge_bb);

  builder_.SetInsertPoint(rhs_bb);
  ir::Value* rhs = EvalExpr(*ctx->exp(1));
  builder_.CreateStore(ToI32(ToI1(rhs)), res_ptr);
  builder_.CreateBr(merge_bb);

  builder_.SetInsertPoint(merge_bb);
  return static_cast<ir::Value*>(
      builder_.CreateLoad(res_ptr, module_.GetContext().NextTemp()));
}

std::any IRGenImpl::visitOrExp(SysYParser::OrExpContext* ctx) {
  if (!builder_.GetInsertBlock()) {
    return static_cast<ir::Value*>(EvalConstExpr(*ctx));
  }

  ir::Value* lhs = EvalExpr(*ctx->exp(0));
  if (auto* c = dynamic_cast<ir::ConstantValue*>(lhs); c && IsTruthy(c)) {
    return static_cast<ir::Value*>(module_.GetContext().GetConstInt(1));
  }

  const std::string suffix = module_.GetContext().NextTemp();
  ir::BasicBlock* rhs_bb = func_->CreateBlock("or.rhs." + suffix);
  ir::BasicBlock* merge_bb = func_->CreateBlock("or.merge." + suffix);
  auto* res_ptr = builder_.CreateAllocaI32(module_.GetContext().NextTemp());

  builder_.CreateStore(ToI32(ToI1(lhs)), res_ptr);
  builder_.CreateCondBr(ToI1(lhs), merge_bb, rhs_bb);

  builder_.SetInsertPoint(rhs_bb);
  ir::Value* rhs = EvalExpr(*ctx->exp(1));
  builder_.CreateStore(ToI32(ToI1(rhs)), res_ptr);
  builder_.CreateBr(merge_bb);

  builder_.SetInsertPoint(merge_bb);
  return static_cast<ir::Value*>(
      builder_.CreateLoad(res_ptr, module_.GetContext().NextTemp()));
}

bool IRGenImpl::IsArrayLikeDef(antlr4::ParserRuleContext* def) const {
  if (auto* const_def = dynamic_cast<SysYParser::ConstDefContext*>(def)) {
    return !const_def->exp().empty();
  }
  if (auto* var_def = dynamic_cast<SysYParser::VarDefContext*>(def)) {
    return !var_def->exp().empty();
  }
  if (auto* param = dynamic_cast<SysYParser::FuncFParamContext*>(def)) {
    return !param->LBRACK().empty();
  }
  return false;
}

size_t IRGenImpl::GetArrayRank(antlr4::ParserRuleContext* def) const {
  if (auto* const_def = dynamic_cast<SysYParser::ConstDefContext*>(def)) {
    return const_def->exp().size();
  }
  if (auto* var_def = dynamic_cast<SysYParser::VarDefContext*>(def)) {
    return var_def->exp().size();
  }
  if (auto* param = dynamic_cast<SysYParser::FuncFParamContext*>(def)) {
    return param->LBRACK().size();
  }
  return 0;
}

std::shared_ptr<ir::Type> IRGenImpl::GetDefType(antlr4::ParserRuleContext* def) const {
  std::shared_ptr<ir::Type> ty = ir::Type::GetInt32Type();
  auto apply_dims = [&](const auto& dims) {
    auto result = ty;
    for (auto it = dims.rbegin(); it != dims.rend(); ++it) {
      if constexpr (std::is_pointer_v<typename std::decay_t<decltype(*it)>>) {
        auto* dim_val = const_cast<IRGenImpl*>(this)->EvalConstExpr(**it);
        result = ir::ArrayType::Get(result, AsInt(dim_val));
      } else {
        auto* dim_val = const_cast<IRGenImpl*>(this)->EvalConstExpr(*it);
        result = ir::ArrayType::Get(result, AsInt(dim_val));
      }
    }
    return result;
  };

  if (auto* const_def = dynamic_cast<SysYParser::ConstDefContext*>(def)) {
    auto* decl = dynamic_cast<SysYParser::ConstDeclContext*>(const_def->parent);
    ty = (decl && decl->btype() && decl->btype()->FLOAT()) ? ir::Type::GetFloatType()
                                                           : ir::Type::GetInt32Type();
    return apply_dims(const_def->exp());
  }
  if (auto* var_def = dynamic_cast<SysYParser::VarDefContext*>(def)) {
    auto* decl = dynamic_cast<SysYParser::VarDeclContext*>(var_def->parent);
    ty = (decl && decl->btype() && decl->btype()->FLOAT()) ? ir::Type::GetFloatType()
                                                           : ir::Type::GetInt32Type();
    return apply_dims(var_def->exp());
  }
  if (auto* param = dynamic_cast<SysYParser::FuncFParamContext*>(def)) {
    ty = param->btype()->FLOAT() ? ir::Type::GetFloatType() : ir::Type::GetInt32Type();
    if (param->LBRACK().empty()) return ty;
    for (int i = static_cast<int>(param->exp().size()) - 1; i >= 0; --i) {
      auto* dim_val = const_cast<IRGenImpl*>(this)->EvalConstExpr(*param->exp(i));
      ty = ir::ArrayType::Get(ty, AsInt(dim_val));
    }
    return ty;
  }
  return ty;
}

ir::Value* IRGenImpl::DecayArrayPtr(SysYParser::LValueContext* ctx) {
  auto* def = sema_.ResolveLValue(ctx);
  if (!def) {
    throw std::runtime_error(FormatError("irgen", "数组退化失败: 未绑定定义"));
  }
  ir::Value* base_ptr = storage_map_.at(def);
  const auto base_ty = GetDefType(def);

  if (dynamic_cast<SysYParser::FuncFParamContext*>(def)) {
    ir::Value* loaded_base = builder_.CreateLoad(base_ptr, module_.GetContext().NextTemp());
    if (ctx->exp().empty()) return loaded_base;

    ir::Value* offset = CastValue(*this, builder_, module_, EvalExpr(*ctx->exp(0)),
                                  ir::Type::GetInt32Type());
    auto cur_ty = base_ty;
    for (size_t i = 1; i < ctx->exp().size(); ++i) {
      if (!cur_ty->IsArray()) break;
      auto arr_ty = cur_ty->GetAsArrayType();
      ir::Value* stride = builder_.CreateConstInt(static_cast<int>(arr_ty->GetNumElements()));
      offset = builder_.CreateMul(offset, stride, module_.GetContext().NextTemp());
      offset = builder_.CreateAdd(
          offset,
          CastValue(*this, builder_, module_, EvalExpr(*ctx->exp(i)),
                    ir::Type::GetInt32Type()),
          module_.GetContext().NextTemp());
      cur_ty = arr_ty->GetElementType();
    }
    return builder_.CreateGEP(ScalarPointerType(cur_ty), loaded_base, {offset},
                              module_.GetContext().NextTemp());
  }

  std::vector<ir::Value*> indices;
  indices.push_back(builder_.CreateConstInt(0));
  for (auto* exp : ctx->exp()) {
    indices.push_back(
        CastValue(*this, builder_, module_, EvalExpr(*exp), ir::Type::GetInt32Type()));
  }

  auto cur_ty = base_ty;
  while (cur_ty->IsArray()) {
    cur_ty = cur_ty->GetAsArrayType()->GetElementType();
  }
  if (!ctx->exp().empty()) {
    return builder_.CreateGEP(ScalarPointerType(cur_ty), base_ptr, indices,
                              module_.GetContext().NextTemp());
  }
  indices.push_back(builder_.CreateConstInt(0));
  return builder_.CreateGEP(ScalarPointerType(cur_ty), base_ptr, indices,
                            module_.GetContext().NextTemp());
}

ir::Value* IRGenImpl::GetLValuePtr(SysYParser::LValueContext* ctx) {
  auto* def = sema_.ResolveLValue(ctx);
  if (!def) {
    throw std::runtime_error(FormatError("irgen", "未定义的左值: " + ctx->ID()->getText()));
  }
  auto it = storage_map_.find(def);
  if (it == storage_map_.end()) {
    throw std::runtime_error(
        FormatError("irgen", "左值缺少存储槽位: " + ctx->ID()->getText()));
  }

  ir::Value* base_ptr = it->second;
  if (ctx->exp().empty()) return base_ptr;

  if (dynamic_cast<SysYParser::FuncFParamContext*>(def)) {
    return DecayArrayPtr(ctx);
  }

  const auto base_ty = GetDefType(def);
  std::vector<ir::Value*> indices;
  indices.push_back(builder_.CreateConstInt(0));
  for (auto* exp : ctx->exp()) {
    indices.push_back(
        CastValue(*this, builder_, module_, EvalExpr(*exp), ir::Type::GetInt32Type()));
  }

  auto cur_ty = base_ty;
  for (size_t i = 0; i < ctx->exp().size(); ++i) {
    if (!cur_ty->IsArray()) {
      throw std::runtime_error(FormatError("irgen", "数组下标层数超出定义"));
    }
    cur_ty = cur_ty->GetAsArrayType()->GetElementType();
  }
  return builder_.CreateGEP(ScalarPointerType(cur_ty), base_ptr, indices,
                            module_.GetContext().NextTemp());
}

ir::Value* IRGenImpl::ToI1(ir::Value* v) {
  if (auto* cv = dynamic_cast<ir::ConstantValue*>(v)) {
    return module_.GetContext().GetConstInt(IsTruthy(cv) ? 1 : 0);
  }
  if (auto* inst = dynamic_cast<ir::Instruction*>(v)) {
    switch (inst->GetOpcode()) {
      case ir::Opcode::ICmpEQ:
      case ir::Opcode::ICmpNE:
      case ir::Opcode::ICmpLT:
      case ir::Opcode::ICmpGT:
      case ir::Opcode::ICmpLE:
      case ir::Opcode::ICmpGE:
      case ir::Opcode::FCmpEQ:
      case ir::Opcode::FCmpNE:
      case ir::Opcode::FCmpLT:
      case ir::Opcode::FCmpGT:
      case ir::Opcode::FCmpLE:
      case ir::Opcode::FCmpGE:
        return v;
      default:
        break;
    }
  }
  if (v->GetType()->IsFloat()) {
    return builder_.CreateFCmp(ir::Opcode::FCmpNE, v, builder_.CreateConstFloat(0.0f),
                               module_.GetContext().NextTemp());
  }
  if (v->GetType()->IsInt32()) {
    return builder_.CreateICmp(ir::Opcode::ICmpNE, v, builder_.CreateConstInt(0),
                               module_.GetContext().NextTemp());
  }
  return v;
}

ir::Value* IRGenImpl::ToI32(ir::Value* v) {
  if (v->GetType()->IsInt32()) return v;
  if (v->GetType()->IsFloat()) {
    return builder_.CreateFPToSI(v, ir::Type::GetInt32Type(),
                                 module_.GetContext().NextTemp());
  }
  return builder_.CreateZExt(v, ir::Type::GetInt32Type(),
                             module_.GetContext().NextTemp());
}
