// 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);
}

ConstantFloat* IRBuilder::CreateConstFloat(float v) {
  return ctx_.GetConstFloat(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::GetPointerType(Type::GetFloat32Type()), name);
}

AllocaInst* IRBuilder::CreateAlloca(std::shared_ptr<Type> ty, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  if (!ty || !ty->IsPointer()) {
    throw std::runtime_error(FormatError("ir", "CreateAlloca 仅支持指针类型"));
  }
  return insert_block_->Append<AllocaInst>(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"));
  }
  if (!ptr->GetType() || !ptr->GetType()->IsPointer()) {
    throw std::runtime_error(FormatError("ir", "IRBuilder::CreateLoad ptr 必须为指针类型"));
  }
  const auto* ptr_ty = static_cast<const PointerType*>(ptr->GetType().get());
  return insert_block_->Append<LoadInst>(ptr_ty->GetPointeeType(), 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 未设置插入点"));
  }
  // ReturnInst expects its own type to be void; the returned value is an
  // operand. Always use void as the instruction type.
  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);
}

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

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

CmpInst* IRBuilder::CreateICmp(CmpInst::Predicate pred, Value* lhs, Value* rhs,
                               const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<CmpInst>(Opcode::ICmp, pred, lhs, rhs, name);
}

CmpInst* IRBuilder::CreateFCmp(CmpInst::Predicate pred, Value* lhs, Value* rhs,
                               const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  return insert_block_->Append<CmpInst>(Opcode::FCmp, pred, lhs, rhs, name);
}

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

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

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

GetElementPtrInst* IRBuilder::CreateGEP(Value* ptr, std::vector<Value*> indices, const std::string& name) {
  if (!insert_block_) {
    throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
  }
  // 计算结果类型：根据传入的 indices 逐步从 pointee 类型走到目标元素类型。
  // 例如 ptr 是指向数组的指针，GEP 使用一个索引应返回指向数组元素的指针。
  std::shared_ptr<Type> current;
  if (ptr->GetType() && ptr->GetType()->IsPointer()) {
    const PointerType* pty = static_cast<const PointerType*>(ptr->GetType().get());
    current = pty->GetPointeeType();
  } else {
    current = ptr->GetType();
  }
  // 根据每个索引推进类型层次：数组 -> 元素类型，指针 -> 指向类型
  for (size_t i = 0; i < indices.size(); ++i) {
    if (!current) break;
    if (current->IsArray()) {
      const ArrayType* aty = static_cast<const ArrayType*>(current.get());
      current = aty->GetElementType();
    } else if (current->IsPointer()) {
      const PointerType* ppty = static_cast<const PointerType*>(current.get());
      current = ppty->GetPointeeType();
    } else {
      // 非数组/指针类型，无法继续下钻，保持当前类型
      break;
    }
  }
  auto result_ty = Type::GetPointerType(current);
  return insert_block_->Append<GetElementPtrInst>(result_ty, ptr, indices, name);
}

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

}  // namespace ir
