#include "ir/PassManager.h"
#include <algorithm>
#include <iostream>
#include <queue>
#include <unordered_set>

namespace ir {

// Helper to rebuild CFG predecessors and successors.
void RebuildCFG(Function* func) {
  for (auto& bbPtr : func->GetBlocks()) {
    bbPtr->ClearPredecessors();
    bbPtr->ClearSuccessors();
  }
  for (auto& bbPtr : func->GetBlocks()) {
    auto* bb = bbPtr.get();
    const auto& insts = bb->GetInstructions();
    if (insts.empty()) continue;
    auto* term = insts.back().get();
    if (auto* br = dynamic_cast<BranchInst*>(term)) {
      if (br->IsConditional()) {
        auto* t = br->GetIfTrue();
        auto* f = br->GetIfFalse();
        if (t) {
          bb->AddSuccessor(t);
          t->AddPredecessor(bb);
        }
        if (f) {
          bb->AddSuccessor(f);
          f->AddPredecessor(bb);
        }
      } else {
        auto* dest = br->GetDest();
        if (dest) {
          bb->AddSuccessor(dest);
          dest->AddPredecessor(bb);
        }
      }
    }
  }
}

static void PostOrderDFS(BasicBlock* bb, std::unordered_set<BasicBlock*>& visited,
                         std::vector<BasicBlock*>& post_order) {
  visited.insert(bb);
  for (auto* succ : bb->GetSuccessors()) {
    if (visited.find(succ) == visited.end()) {
      PostOrderDFS(succ, visited, post_order);
    }
  }
  post_order.push_back(bb);
}

DominatorTree::DominatorTree(Function* func) : func_(func) {}

void DominatorTree::Run() {
  RebuildCFG(func_);
  ComputeRPO();
  ComputeIdom();
  ComputeDomTree();
  ComputeDF();
}

void DominatorTree::ComputeRPO() {
  rpo_.clear();
  if (func_->GetBlocks().empty()) return;
  std::unordered_set<BasicBlock*> visited;
  std::vector<BasicBlock*> post_order;
  PostOrderDFS(func_->GetEntry(), visited, post_order);
  rpo_ = std::vector<BasicBlock*>(post_order.rbegin(), post_order.rend());
}

void DominatorTree::ComputeIdom() {
  idom_.clear();
  if (rpo_.empty()) return;

  BasicBlock* entry = rpo_.front();
  idom_[entry] = entry;

  std::unordered_map<BasicBlock*, int> rpo_index;
  for (size_t i = 0; i < rpo_.size(); ++i) {
    rpo_index[rpo_[i]] = i;
  }

  bool changed = true;
  while (changed) {
    changed = false;
    for (size_t i = 1; i < rpo_.size(); ++i) {
      BasicBlock* b = rpo_[i];
      BasicBlock* new_idom = nullptr;

      // Find first predecessor with a defined idom
      for (auto* pred : b->GetPredecessors()) {
        if (idom_.find(pred) != idom_.end()) {
          new_idom = pred;
          break;
        }
      }

      if (new_idom) {
        for (auto* pred : b->GetPredecessors()) {
          if (pred != new_idom && idom_.find(pred) != idom_.end()) {
            // Intersect
            auto* finger1 = pred;
            auto* finger2 = new_idom;
            int finger_iter = 0;
            while (finger1 != finger2) {
              finger_iter++;
              if (finger_iter > 1000) {
                std::cerr << "FATAL: DominatorTree finger loop stuck! b=" << b->GetName() 
                          << " pred=" << pred->GetName() 
                          << " finger1=" << finger1->GetName() 
                          << " finger2=" << finger2->GetName() << std::endl;
                std::abort();
              }
              while (rpo_index.at(finger1) > rpo_index.at(finger2)) {
                finger1 = idom_.at(finger1);
              }
              while (rpo_index.at(finger2) > rpo_index.at(finger1)) {
                finger2 = idom_.at(finger2);
              }
            }
            new_idom = finger1;
          }
        }

        if (idom_.find(b) == idom_.end() || idom_[b] != new_idom) {
          idom_[b] = new_idom;
          changed = true;
        }
      }
    }
  }
}

void DominatorTree::ComputeDomTree() {
  dom_tree_.clear();
  for (auto* b : rpo_) {
    dom_tree_[b] = {};
  }
  for (auto* b : rpo_) {
    if (b != rpo_.front()) {
      auto* parent = idom_[b];
      dom_tree_[parent].push_back(b);
    }
  }
}

void DominatorTree::ComputeDF() {
  df_.clear();
  for (auto* b : rpo_) {
    df_[b] = {};
  }
  for (auto* b : rpo_) {
    if (b->GetPredecessors().size() >= 2) {
      for (auto* pred : b->GetPredecessors()) {
        auto* runner = pred;
        auto* idom_b = idom_[b];
        while (runner && runner != idom_b) {
          auto idom_it = idom_.find(runner);
          if (idom_it == idom_.end()) {
            break; // Unreachable predecessor
          }
          auto* next_runner = idom_it->second;
          if (next_runner == runner) {
            break; // Reached root / entry
          }

          auto& runner_df = df_[runner];
          if (std::find(runner_df.begin(), runner_df.end(), b) == runner_df.end()) {
            runner_df.push_back(b);
          }
          runner = next_runner;
        }
      }
    }
  }
}

BasicBlock* DominatorTree::GetIdom(BasicBlock* bb) const {
  auto it = idom_.find(bb);
  return it != idom_.end() ? it->second : nullptr;
}

const std::vector<BasicBlock*>& DominatorTree::GetDominatedBlocks(BasicBlock* bb) const {
  static const std::vector<BasicBlock*> empty;
  auto it = dom_tree_.find(bb);
  return it != dom_tree_.end() ? it->second : empty;
}

const std::vector<BasicBlock*>& DominatorTree::GetDominanceFrontier(BasicBlock* bb) const {
  static const std::vector<BasicBlock*> empty;
  auto it = df_.find(bb);
  return it != df_.end() ? it->second : empty;
}

bool DominatorTree::Dominates(BasicBlock* a, BasicBlock* b) const {
  if (a == b) return true;
  auto* runner = b;
  while (runner != rpo_.front()) {
    auto it = idom_.find(runner);
    if (it == idom_.end()) return false;
    runner = it->second;
    if (runner == a) return true;
  }
  return false;
}

} // namespace ir
