#include <cstdio>
#include <l4/sys/debugger.h>
#include <l4/sys/obj_info.h>
#include <l4/re/error_helper>
#include <l4/re/util/kumem_alloc>

int main()
{
  enum
  {
    Order_kumem_pages = 4,
    Size_kumem = L4_PAGESIZE << Order_kumem_pages,
  };

  l4_addr_t kumem;
  L4Re::chksys(L4Re::Util::kumem_alloc(&kumem, Order_kumem_pages),
               "Allocate KU memory for object information");

  unsigned skip_entries = 0;
  for (;;)
    {
      l4_umword_t result_cnt;
      l4_umword_t result_all;
      L4Re::chksys(l4_debugger_query_obj_infos(L4_BASE_DEBUGGER_CAP,
                                               kumem, Size_kumem, skip_entries,
                                               &result_cnt, &result_all),
                   "Retrieve kernel object information");

      if (skip_entries == 0)
        printf("\033[31mGot %lu/%lu entries during first call.\033[m\n",
               result_cnt, result_all);

      if (result_cnt == 0)
        break;

      auto const *o = reinterpret_cast<L4_kobj_info *>(kumem);
      for (unsigned i = 0; i < result_cnt; ++i, ++o)
        {
          if (o->type != L4_kobj_info::Mapping::Type)
            printf("%llx %llx ", o->id, o->mapping_ptr);
          switch (o->type)
            {
            case L4_kobj_info::Mapping::Type:
              printf("%*s%0*lx[C:%x]: space=D:%llx%s%.*s%s rights=%x flags=%x obj=0x%llx",
                     8, "",
                     sizeof(l4_umword_t) == 8 ? 16 : 8,
                     (l4_umword_t)o->mapping.mapping_ptr,
                     o->mapping.cap_idx,
                     o->id,
                     o->mapping.space_name[0] ? "(" : "",
                     o->mapping.space_name[0] ? int{sizeof(o->mapping.space_name)} : 0,
                     o->mapping.space_name[0] ? o->mapping.space_name : "",
                     o->mapping.space_name[0] ? ")" : "",
                     o->mapping.entry_rights,
                     o->mapping.entry_flags,
                     o->mapping.entry_ptr);
              break;

            case L4_kobj_info::Thread::Type:
              printf("[\033[32mThread\033[m]%s C=%u",
                     o->thread.is_kernel ? " {KERNEL}" : "",
                     o->thread.home_cpu);
              if (o->thread.home_cpu != o->thread.current_cpu)
                printf(":%u", o->thread.current_cpu);
              if (o->thread.is_kernel_task)
                printf(" R=%lld rdy%s",
                       o->thread.ref_cnt,
                       o->thread.is_current ? " \033[32mcur\033[m" : "");
              else
                printf(" S=D:%llx R=%lld%s%s",
                       o->thread.space_id,
                       o->thread.ref_cnt,
                       o->thread.in_ready_list ? " rdy" : "",
                       o->thread.is_current ? " \033[32mcur\033[m" : "");
              break;

            case L4_kobj_info::Space::Type:
              printf("[\033[31mTask\033[m]%s R=%lld",
                     o->space.is_kernel ? " {KERNEL}" : "",
                     o->space.ref_cnt);
              break;

            case L4_kobj_info::Vm::Type:
              printf("Vm:");
              break;

            case L4_kobj_info::Ipc_gate::Type:
              printf("[\033[35mGate\033[m] L=%s%08llx\033[m D=%llx",
                     o->ipc_gate.label & 3 ? "\033[36;1m" : "",
                     o->ipc_gate.label,
                     o->ipc_gate.thread_id);
              break;

            case L4_kobj_info::Irq_sender::Type:
              printf("[\033[37mIRQ ipc\033[m] I=%x %.*s F=%x L=%llx T=%llx Q=%lld",
                     o->irq_sender.pin,
                     int{sizeof(o->irq_sender.chip_type)},
                     o->irq_sender.chip_type,
                     o->irq_sender.flags,
                     o->irq_sender.label,
                     o->irq_sender.target_id,
                     o->irq_sender.queued);
              break;

            case L4_kobj_info::Irq_semaphore::Type:
              printf("[\033[37mIRQ sem\033[m] I=%x %.*s F=%x Q=%lld",
                     o->irq_semaphore.pin,
                     int{sizeof(o->irq_semaphore.chip_type)},
                     o->irq_semaphore.chip_type,
                     o->irq_semaphore.flags,
                     o->irq_semaphore.queued);
              break;

            case L4_kobj_info::Factory::Type:
              printf("\033[33;1mFactory\033[m c=%llu l=%llu",
                     o->factory.current,
                     o->factory.limit);
              break;

            case L4_kobj_info::Jdb::Type:
              printf("[Jdb]");
              break;

            case L4_kobj_info::Scheduler::Type:
              printf("[\033[34mSched\033[m]");
              break;

            case L4_kobj_info::Vlog::Type:
              printf("[Vlog]");
              break;

            case L4_kobj_info::Pfc::Type:
              printf("[Icu/Pfc]");
              break;

            case L4_kobj_info::Dmar_space::Type:
              printf("[IOMMU]");
              break;

            case L4_kobj_info::Iommu::Type:
              printf("[IOMMU]");
              break;

            case L4_kobj_info::Smmu::Type:
              printf("[SMMU]");
              break;
            }
          if (o->type != L4_kobj_info::Mapping::Type)
            printf(" ref_cnt=%lld", o->ref_cnt);
          printf("\n");
        }

      skip_entries += result_cnt;
    }

  printf("\nFinished!\n");

  return 0;
}
