// IR 构建工具：
// - 管理插入点（当前基本块/位置）
// - 提供创建各类指令的便捷接口，降低 IRGen 复杂度

#include "ir/IR.h"

#include <stdexcept>

#include "utils/Log.h"

namespace ir {
IRBuilder::IRBuilder(Context& ctx, BasicBlock* bb)
    : ctx_(ctx), insert_block_(bb) {}

void IRBuilder::SetInsertPoint(BasicBlock* bb) { insert_block_ = bb; }

BasicBlock* IRBuilder::GetInsertBlock() const { return insert_block_; }

ConstantInt* IRBuilder::CreateConstInt(int v) {
  // 常量不需要挂在基本块里，由 Context 负责去重与生命周期。
  return ctx_.GetConstInt(v);
}

BinaryInst* IRBuilder::CreateBinary(Opcode op, Value* lhs, Value* rhs,
                                    const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!lhs) {
    throw std::runtime_error(
        FormatError("ir", "IRBuilder::CreateBinary 缺少 lhs"));
  }
  if (!rhs) {
    throw std::runtime_error(
        FormatError("ir", "IRBuilder::CreateBinary 缺少 rhs"));
  }
  return insert_block_->Append<BinaryInst>(op, lhs->GetType(), lhs, rhs, name);
}

BinaryInst* IRBuilder::CreateAdd(Value* lhs, Value* rhs,
                                 const std::string& name) {
  return CreateBinary(Opcode::Add, lhs, rhs, name);
}

AllocaInst* IRBuilder::CreateAllocaI32(const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<AllocaInst>(Type::GetPtrInt32Type(), name);
}

AllocaInst* IRBuilder::CreateAllocaFloat(const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<AllocaInst>(Type::GetPtrFloatType(), name);
}

AllocaInst* IRBuilder::CreateAlloca(std::shared_ptr<Type> ty, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  auto ptr_ty = Type::GetPointerType(ty);
  return insert_block_->Append<AllocaInst>(ptr_ty, name);
}

LoadInst* IRBuilder::CreateLoad(Value* ptr, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!ptr) {
    throw std::runtime_error(
        FormatError("ir", "IRBuilder::CreateLoad 缺少 ptr"));
  }
  auto val_ty = ptr->GetType()->GetPointedType();
  return insert_block_->Append<LoadInst>(val_ty, ptr, name);
}

StoreInst* IRBuilder::CreateStore(Value* val, Value* ptr) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!val) {
    throw std::runtime_error(
        FormatError("ir", "IRBuilder::CreateStore 缺少 val"));
  }
  if (!ptr) {
    throw std::runtime_error(
        FormatError("ir", "IRBuilder::CreateStore 缺少 ptr"));
  }
  return insert_block_->Append<StoreInst>(Type::GetVoidType(), val, ptr);
}

ReturnInst* IRBuilder::CreateRet(Value* v) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<ReturnInst>(Type::GetVoidType(), v);
}

BinaryInst* IRBuilder::CreateSub(Value* lhs, Value* rhs, const std::string& name) {
  return CreateBinary(Opcode::Sub, lhs, rhs, name);
}

BinaryInst* IRBuilder::CreateMul(Value* lhs, Value* rhs, const std::string& name) {
  return CreateBinary(Opcode::Mul, lhs, rhs, name);
}

UnaryInst* IRBuilder::CreateNeg(Value* operand, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!operand) {
    throw std::runtime_error(FormatError("ir", "IRBuilder::CreateNeg 缺少操作数"));
  }
  auto val_ty = (operand->GetType() && operand->GetType()->IsFloat()) ? Type::GetFloatType() : Type::GetInt32Type();
  return insert_block_->Append<UnaryInst>(Opcode::Neg, val_ty, operand, name);
}

Instruction* IRBuilder::CreateCmp(CmpOp op, Value* lhs, Value* rhs, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!lhs || !rhs) {
    throw std::runtime_error(FormatError("ir", "IRBuilder::CreateCmp 缺少操作数"));
  }
  if (lhs->GetType()->IsFloat() || rhs->GetType()->IsFloat()) {
    if (!lhs->GetType()->IsFloat()) {
      lhs = CreateSIToFP(lhs, ctx_.NextTemp());
    }
    if (!rhs->GetType()->IsFloat()) {
      rhs = CreateSIToFP(rhs, ctx_.NextTemp());
    }
    return insert_block_->Append<FCmpInst>(op, lhs, rhs, name);
  }
  return insert_block_->Append<CmpInst>(op, lhs, rhs, name);
}

ZextInst* IRBuilder::CreateZext(Value* val, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!val) {
    throw std::runtime_error(FormatError("ir", "IRBuilder::CreateZext 缺少操作数"));
  }
  return insert_block_->Append<ZextInst>(Type::GetInt32Type(), val, name);
}

BranchInst* IRBuilder::CreateBr(BasicBlock* dest) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!dest) {
    throw std::runtime_error(FormatError("ir", "IRBuilder::CreateBr 缺少操作数"));
  }
  return insert_block_->Append<BranchInst>(dest);
}

CondBranchInst* IRBuilder::CreateCondBr(Value* cond, BasicBlock* true_bb, BasicBlock* false_bb) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!cond || !true_bb || !false_bb) {
    throw std::runtime_error(FormatError("ir", "IRBuilder::CreateCondBr 缺少操作数"));
  }
  return insert_block_->Append<CondBranchInst>(cond, true_bb, false_bb);
}

CallInst* IRBuilder::CreateCall(Function* func, std::vector<Value*> args, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!func) {
    throw std::runtime_error(FormatError("ir", "IRBuilder::CreateCall 缺少目标函数"));
  }
  return insert_block_->Append<CallInst>(func, std::move(args), name);
}

GEPInst* IRBuilder::CreateGEP(std::shared_ptr<Type> ty, Value* ptr, std::vector<Value*> indices, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<GEPInst>(ty, ptr, std::move(indices), name);
}

SIToFPInst* IRBuilder::CreateSIToFP(Value* val, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<SIToFPInst>(Type::GetFloatType(), val, name);
}

FPToSIInst* IRBuilder::CreateFPToSI(Value* val, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<FPToSIInst>(Type::GetInt32Type(), val, name);
}

}  // namespace ir
