#include "include/aliases.h"
#include "include/reg_access.h"
#include "include/sim86_lib.h"
#include <bits/types/FILE.h>
#include <stdio.h>
#include <string.h>

void decode_operand(instruction_operand operand);
void mov_to_register(const register_access &reg,
                     const instruction_operand &source);

int main(int argc, char *argv[]) {
  if (argc < 2) {
    printf("Usage: sim86 BINARY_FILE\n");
    return 1;
  }

  const char *filename = argv[1];

  printf("Filename: %s\n", filename);

  FILE *fp = fopen(filename, "rb");
  if (!fp) {
    printf("Failed to open file %s\n", filename);
  }

  fseek(fp, 0, SEEK_END);

  u32 size = ftell(fp);

  fseek(fp, 0, SEEK_SET);

  u8 buffer[size + 1];
  memset((void *)buffer, 0, size + 1);

  fread((void *)buffer, sizeof(u8), size, fp);

  fclose(fp);

  instruction_table table;
  Sim86_Get8086InstructionTable(&table);

  u32 offset = 0;

  bool accessed_registers[REGISTER_COUNT] = {false};

  while (offset < size) {
    instruction decoded;
    Sim86_Decode8086Instruction(size - offset, buffer + offset, &decoded);

    if (decoded.Op) {
      offset += decoded.Size;

      if (decoded.Op == Op_mov) {
        instruction_operand dest = decoded.Operands[0];
        instruction_operand source = decoded.Operands[1];

        if (dest.Type == Operand_Register) {
          mov_to_register(dest.Register, source);

          accessed_registers[dest.Register.Index] = true;
        }
      }
    }
  }

  for (u32 i = 0; i < REGISTER_COUNT; ++i) {
    if (accessed_registers[i]) {
      register_access reg = {i, 0, 2};
      u16 value = get_register(reg);

      printf("%s: 0x%04x (%d)\n", get_register_name(reg), value, value);
    }
  }

  return 0;
}

void decode_operand(instruction_operand operand) {
  switch (operand.Type) {
  case Operand_Register:
    printf("Register operand: %d, %d, %d\n", operand.Register.Index,
           operand.Register.Offset, operand.Register.Count);
    break;
  case Operand_Memory:
    // printf("Memory operand\n");
    break;
  case Operand_Immediate:
    // printf("Immediate operand\n");
    break;
  default:
    break;
  }
}

void mov_to_register(const register_access &reg,
                     const instruction_operand &source) {
  switch (source.Type) {
  case Operand_Immediate:
    set_register(reg, source.Immediate.Value);
    break;
  case Operand_Register:
    set_register(reg, get_register(source.Register));
    break;
  default:
    break;
  }
}