#include "exchange_plan.hpp"

#include <cstdint>
#include <fstream>
#include <iostream>
#include <regex>
#include <string>
#include <vector>

namespace {
std::vector<std::uint32_t> wordsIn(const std::string &line) {
  static const std::regex wordPattern("0x([0-9a-fA-F]+)");
  std::vector<std::uint32_t> words;
  for (std::sregex_iterator it(line.begin(), line.end(), wordPattern), end;
       it != end; ++it) {
    words.push_back(std::stoul((*it)[1].str(), nullptr, 16));
  }
  return words;
}

template <typename Row>
bool equalAt(const std::vector<std::uint32_t> &words, std::size_t offset,
             const Row &row) {
  return words.size() >= offset + row.size() &&
         std::equal(row.begin(), row.end(), words.begin() + offset);
}
} // namespace

int main(int argc, char **argv) {
  if (argc != 4) {
    std::cerr << "usage: exchange_plan_verify FILE SENDER RECEIVER\n";
    return 2;
  }
  const unsigned fixedSender = std::stoul(argv[2]);
  const unsigned fixedReceiver = std::stoul(argv[3]);
  std::ifstream input(argv[1]);
  std::string line;
  unsigned checked = 0;
  unsigned failed = 0;
  unsigned alignmentIndependent = 0;
  while (std::getline(input, line)) {
    std::smatch match;
    if (std::regex_search(line, match, std::regex("^count ([0-9]+):"))) {
      const unsigned count = std::stoul(match[1].str());
      const auto expected = wordsIn(line);
      const auto actual = xcom::assemble(fixedSender, fixedReceiver, count);
      // Count 52 deliberately uses a sequential receiver plan instead of the
      // SDK's address-sensitive dual-issue bundle.
      const bool ok = equalAt(expected, 0, actual.sender) &&
                      (count == 52 || equalAt(expected, 9, actual.receiver));
      alignmentIndependent += count == 52;
      ++checked;
      failed += !ok;
      if (!ok && failed <= 10) {
        std::cerr << "count " << count << " differs\n";
      }
    } else if (std::regex_search(line, match,
                                 std::regex("^sender ([0-9]+):"))) {
      const unsigned sender = std::stoul(match[1].str());
      const auto actual = xcom::assemble(sender, fixedReceiver, 3);
      ++checked;
      const auto expected = wordsIn(line);
      if (!equalAt(expected, 0, actual.sender)) {
        ++failed;
        if (failed <= 10) {
          std::cerr << "sender " << sender << " differs: delay expected=0x"
                    << std::hex << expected[1] << " actual=0x"
                    << actual.sender[1] << " route expected=0x" << expected[2]
                    << " actual=0x" << actual.sender[2] << std::dec << '\n';
        }
      }
    }
  }
  std::cout << "checked=" << checked << " failed=" << failed
            << " alignmentIndependent=" << alignmentIndependent << '\n';
  return failed == 0 && checked != 0 ? 0 : 1;
}
