#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "afl-fuzz.h"
#include "mangle.h"

#define NUMBER_OF_MUTATIONS 5

uint8_t          *queue_input;
size_t            queue_input_size;
afl_state_t      *afl_struct;
run_t             run;
honggfuzz_t       global;
struct _dynfile_t dynfile;

typedef struct my_mutator {

  afl_state_t *afl;
  run_t       *run;
  u8          *mutator_buf;
  unsigned int seed;
  unsigned int extras_cnt, a_extras_cnt;

} my_mutator_t;

my_mutator_t *afl_custom_init(afl_state_t *afl, unsigned int seed) {

  my_mutator_t *data = calloc(1, sizeof(my_mutator_t));
  if (!data) {

    perror("afl_custom_init alloc");
    return NULL;

  }

  if ((data->mutator_buf = malloc(MAX_FILE)) == NULL) {

    free(data);
    perror("mutator_buf alloc");
    return NULL;

  }

  run.dynfile = &dynfile;
  run.global = &global;
  data->afl = afl;
  data->seed = seed;
  data->run = &run;
  afl_struct = afl;

  run.global->mutate.maxInputSz = MAX_FILE;
  run.global->mutate.mutationsPerRun = NUMBER_OF_MUTATIONS;
  run.mutationsPerRun = NUMBER_OF_MUTATIONS;
  run.global->timing.lastCovUpdate = 6;

  // global->feedback.cmpFeedback
  // global->feedback.cmpFeedbackMap

  return data;

}

/* When a new queue entry is added we check if there are new dictionary
   entries to add to honggfuzz structure */

void afl_custom_queue_new_entry(my_mutator_t  *data,
                                const uint8_t *filename_new_queue,
                                const uint8_t *filename_orig_queue) {

  if (run.global->mutate.dictionaryCnt >= 1024) return;

  while (data->extras_cnt < data->afl->extras_cnt &&
         run.global->mutate.dictionaryCnt < 1024) {

    memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
           data->afl->extras[data->extras_cnt].data,
           data->afl->extras[data->extras_cnt].len);
    run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
        data->afl->extras[data->extras_cnt].len;
    run.global->mutate.dictionaryCnt++;
    data->extras_cnt++;

  }

  while (data->a_extras_cnt < data->afl->a_extras_cnt &&
         run.global->mutate.dictionaryCnt < 1024) {

    memcpy(run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].val,
           data->afl->a_extras[data->a_extras_cnt].data,
           data->afl->a_extras[data->a_extras_cnt].len);
    run.global->mutate.dictionary[run.global->mutate.dictionaryCnt].len =
        data->afl->a_extras[data->a_extras_cnt].len;
    run.global->mutate.dictionaryCnt++;
    data->a_extras_cnt++;

  }

  return;

}

/* we could set only_printable if is_ascii is set ... let's see
uint8_t afl_custom_queue_get(void *data, const uint8_t *filename) {

  //run.global->cfg.only_printable = ...

}

*/

/* here we run the honggfuzz mutator, which is really good */

size_t afl_custom_fuzz(my_mutator_t *data, uint8_t *buf, size_t buf_size,
                       u8 **out_buf, uint8_t *add_buf, size_t add_buf_size,
                       size_t max_size) {

  /* set everything up, costly ... :( */
  memcpy(data->mutator_buf, buf, buf_size);
  queue_input = data->mutator_buf;
  run.dynfile->data = data->mutator_buf;
  queue_input_size = buf_size;
  run.dynfile->size = buf_size;
  *out_buf = data->mutator_buf;

  /* the mutation */
  mangle_mangleContent(&run, NUMBER_OF_MUTATIONS);

  /* return size of mutated data */
  return run.dynfile->size;

}

/**
 * Deinitialize everything
 *
 * @param data The data ptr from afl_custom_init
 */
void afl_custom_deinit(my_mutator_t *data) {

  free(data->mutator_buf);
  free(data);

}

