#include "disasm.h"
#include "decoder/decoder.h"
#include "decoder/formatter.h"
#include "lexical/parser.h"
#include "main.h"
#include "resolver/render.h"
#include "utils/error.h"
#include "utils/logger.h"
#include "utils/reverse_endian.h"
#include "utils/str_pile.h"
#include "utils/vector.h"
#include <assert.h>
#include <errno.h>
#include <linux/bpf_common.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>

filter *g_filters;
static uint32_t g_sz;

bool
init_global_filters (void)
{
  if (g_filters)
    return true;
  g_sz = (sizeof (filter) * (BPF_MAXINSNS + 1) + 0xfff) & ~0xfff;
  void *map = mmap (NULL, g_sz, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
                    -1, 0);
  if (map == MAP_FAILED)
    return false;
#ifdef PR_SET_VMA
  prctl (PR_SET_VMA, PR_SET_VMA_ANON_NAME, map, g_sz, "global filters");
#endif
  g_filters = map;
  return true;
}

__attribute__ ((destructor)) static void
free_global_filters (void)
{
  if (g_filters)
    munmap (g_filters, g_sz);
}

static uint32_t
read_filters (filter *filters, FILE *from)
{
  uint32_t todo = sizeof (filter) * (BPF_MAXINSNS + 1);
  uint8_t *ptr = (uint8_t *)filters;
  int fd = fileno (from);
  assert (fd != -1);
  while (todo)
    {
      long rc = read (fd, ptr, todo);
      if (rc == -1)
        error (M_READ_FAIL, strerror (errno));
      if (rc == 0)
        break;
      ptr += rc;
      todo -= rc;
    }
  if (!todo)
    error ("%s", M_TOO_LARGE_INPUT);
  uint32_t leftover = ((size_t)ptr - (size_t)filters) & 7;
  if (leftover)
    warn (M_INPUT_HAS_LEFTOVER, leftover);
  return (ptr - (uint8_t *)filters) >> 3;
}

void
print_prog (uint32_t scmp_arch, fprog *prog, FILE *output_fp, bool trustful)
{
  if (need_reverse_endian (scmp_arch))
    for (uint32_t i = 0; i < prog->len; i++)
      reverse_endian (&prog->filter[i]);

  vector_t v;

  // str pile for syscall names
  assert (prog->len);
  assert (init_pile (prog->len * 40 /* statistical choice */));
  init_vector (&v, sizeof (statement_t), prog->len + 1);
  // check_prog in decode_filters might decect some errors
  // render might mem overflow, so skip render
  if (!decode_filters (prog, &v, trustful))
    render (&v, scmp_arch);
  print_as_comment (output_fp, "Label  CODE  JT   JF      K");
  print_as_comment (output_fp, "---------------------------------");

  filter *filters = prog->filter; // give compiler some hint
  for (uint32_t i = 1; i < v.count; i++)
    {
      filter f = filters[i - 1];
      fprintf (output_fp, " " DEFAULT_LABEL ": 0x%02x 0x%02x 0x%02x 0x%08x ",
               i, f.code, f.jt, f.jf, f.k);
      print_statement (output_fp, get_vector (&v, i));
    }

  print_as_comment (output_fp, "---------------------------------");

  free_vector (&v);
  free_pile ();
}

void
disasm (FILE *fp, uint32_t scmp_arch)
{
  fprog prog;
  assert (init_global_filters ());
  prog.filter = g_filters;
  prog.len = read_filters (g_filters, fp);
  if (prog.len == 0)
    {
      warn ("%s", M_NO_FILTER);
      return;
    }

  print_prog (scmp_arch, &prog, stdout, false);
}
