#include <cstdint>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>

#include <ipu_arch_info/ipuArchInfo.h>

namespace {

void print(const IPUArchInfo &arch, const char *name, std::uint32_t word) {
  std::cout << std::left << std::setw(24) << name << " 0x" << std::right
            << std::hex << std::setw(8) << std::setfill('0') << word
            << std::setfill(' ') << "  "
            << arch.disassembler.disassemble(true, word) << '\n';
}

} // namespace

int main(int argc, char **argv) {
  const auto &arch = ipuArchInfoByName("ipu21");
  if (argc == 3 && std::string(argv[1]) == "pair") {
    const unsigned receiver = std::stoul(argv[2], nullptr, 0);
    for (unsigned sender = 0; sender != 1472; sender += 2) {
      std::cout << "pair receiver=" << receiver << " sender=" << sender
                << " neighbour=" << arch.TPair_NeighbourId(receiver)
                << " primary=" << arch.TPair_TileIsPrimary(receiver)
                << " early=" << arch.TPair_RxIsEarly(receiver, sender)
                << " paired-mux="
                << arch.TPair_WritePairedINCOMING_MUX(sender)
                << " tt=" << arch.ColossusBNET_TT(sender, receiver)
                << " ttdbl=" << arch.ColossusBNET_TTDBL(sender, receiver)
                << " ttim=" << arch.ColossusBNET_TTIM(sender, receiver)
                << " mxp=" << arch.ColossusBNET_MXP(receiver) << '\n';
    }
    return 0;
  }
  if (argc >= 2) {
    std::ifstream input(argv[1], std::ios::binary);
    std::vector<std::uint32_t> words;
    std::uint32_t word;
    while (input.read(reinterpret_cast<char *>(&word), sizeof(word)))
      words.push_back(word);
    for (std::size_t index = 0; index != words.size(); ++index) {
      const auto decoded = arch.disassembler.disassemble(true, words[index]);
      if (argc == 3 || decoded.find("send") != std::string::npos ||
          decoded.find("delaypic") != std::string::npos ||
          decoded.find("delayxpic") != std::string::npos ||
          decoded.find("put") != std::string::npos ||
          decoded.find("sync") != std::string::npos)
        std::cout << std::hex << index * 4 << " 0x" << words[index] << "  "
                  << decoded << '\n';
    }
    return 0;
  }
  std::cout << "incoming-format 32=" << arch.TEXCH_IFMT_32I
            << " 64-early=" << arch.TEXCH_IFMT_64E
            << " 64-wait=" << arch.TEXCH_IFMT_64W
            << " spare=" << arch.TEXCH_IFMT_SPARE << '\n';
  for (unsigned receiver = 0; receiver != 16; receiver += 2) {
    for (unsigned sender = 0; sender != 16; sender += 2) {
      std::cout << "pair receiver=" << receiver << " sender=" << sender
                << " neighbour=" << arch.TPair_NeighbourId(receiver)
                << " primary=" << arch.TPair_TileIsPrimary(receiver)
                << " early=" << arch.TPair_RxIsEarly(receiver, sender)
                << " paired-mux="
                << arch.TPair_WritePairedINCOMING_MUX(sender)
                << " tt=" << arch.ColossusBNET_TT(sender, receiver)
                << " ttdbl=" << arch.ColossusBNET_TTDBL(sender, receiver)
                << " ttim=" << arch.ColossusBNET_TTIM(sender, receiver)
                << " mxp=" << arch.ColossusBNET_MXP(receiver) << '\n';
    }
  }
  for (const auto [receiver, sender] :
       {std::pair{64u, 0u}, std::pair{66u, 0u},
        std::pair{1409u, 0u}, std::pair{1411u, 2u},
        std::pair{0u, 1409u}, std::pair{2u, 1411u}}) {
    std::cout << "far-pair receiver=" << receiver << " sender=" << sender
              << " neighbour=" << arch.TPair_NeighbourId(receiver)
              << " primary=" << arch.TPair_TileIsPrimary(receiver)
              << " early=" << arch.TPair_RxIsEarly(receiver, sender)
              << " tt=" << arch.ColossusBNET_TT(sender, receiver)
              << " ttdbl=" << arch.ColossusBNET_TTDBL(sender, receiver)
              << " ttim=" << arch.ColossusBNET_TTIM(sender, receiver)
              << " mxp=" << arch.ColossusBNET_MXP(receiver) << '\n';
  }
  for (const auto address : {0u, 1u, 0x14021u}) {
    const auto name = "sendpicp address=" + std::to_string(address);
    print(arch, name.c_str(),
          arch.encode.sendpicp_mmmn_zi_bf_zi_zi(63, address, 0, 0));
  }
  for (unsigned sctl = 0; sctl != 8; ++sctl) {
    const auto name = "sendpicp sctl=" + std::to_string(sctl);
    print(arch, name.c_str(),
          arch.encode.sendpicp_mmmn_zi_bf_zi_zi(63, 0, sctl, 0));
  }
  for (unsigned picSelector = 0; picSelector != 2; ++picSelector) {
    const auto name = "sendpicp pic-selector=" + std::to_string(picSelector);
    print(arch, name.c_str(),
          arch.encode.sendpicp_mmmn_zi_bf_zi_zi(0, 0, 0, picSelector));
  }
  for (unsigned sctl = 0; sctl != 8; ++sctl) {
    const auto name = "send sctl=" + std::to_string(sctl);
    print(arch, name.c_str(),
          arch.encode.send_mmmn_zi_bf_zi(63, 0x14000, sctl));
  }
  for (const auto word : {0x7b6a0241u, 0x70f00640u, 0x71f95000u,
                          0xf7e00018u, 0x03018000u, 0xf54a0109u,
                          0x19015000u, 0x64002082u, 0x61940001u,
                          0x60094000u, 0x6402a640u, 0x61940000u,
                          0x640b4000u, 0x64000640u, 0x619140ceu,
                          0x619c0001u, 0x61ec0000u, 0x640a0000u,
                          0x64002640u, 0x61b14048u, 0x640a4000u,
                          0x61b940cau, 0x7fe50005u}) {
    print(arch, "oracle word", word);
  }
}
