// File: donfreiday[P]gb/main.cpp
// Function: main
int main(int argc, char** argv) {
  // Initialize emulator components
  g_gpu.mmu = &g_cpu.mmu;
  g_cpu.mmu.joypad = &g_joypad;
  g_gpu.reset();

  // Load ROM
#ifdef __EMSCRIPTEN__
  if (!g_cpu.mmu.load("roms/tetris.gb")) {
    printf("Invalid ROM file: roms/tetris.gb\n");
    return -1;
  }
#else
  if (argc < 2) {
    printf("Please specify a ROM file.\n");
    return -1;
  }
  if (!g_cpu.mmu.load(argv[1])) {
    printf("Invalid ROM file: %s\n", argv[1]);
    return -1;
  }
#endif

  // Setup SDL
  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    printf("SDL Error: %s\n", SDL_GetError());
    return -1;
  }

  // Setup MAX_CONTROLLERS game controllers
  // MAX_CONTROLLERS is (defined in common.hpp)
  SDL_GameController* controllers[MAX_CONTROLLERS];

  // Initialize array of controllers with nullptrs
  for (int i = 0; i < MAX_CONTROLLERS; i++) {
    controllers[i] = nullptr;
  }

  // Open game controllers
  if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) {
    printf("SDL Error initializing gamepad support: %s\n", SDL_GetError());
  } else {
    int maxJoysticks = SDL_NumJoysticks();
    int controllerID = 0;
    for (int i = 0; i < maxJoysticks && controllerID < MAX_CONTROLLERS; ++i) {
      if (SDL_IsGameController(i)) {
        controllers[i] = SDL_GameControllerOpen(i);
        controllerID++;
      }
    }
  }

  // Setup window
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
  SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
  SDL_DisplayMode current;
  SDL_GetCurrentDisplayMode(0, &current);
  g_window = SDL_CreateWindow("gb: A Gameboy Emulator", SDL_WINDOWPOS_CENTERED,
                              SDL_WINDOWPOS_CENTERED, 1024, 768,
                              SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
  SDL_GLContext glcontext = SDL_GL_CreateContext(g_window);

  // Setup ImGui binding
  ImGui_ImplSdl_Init(g_window);

  // Generate a new texture and store the handle
  glGenTextures(1, &g_lcdTexture);

  // These settings stick with the texture that's bound. Only need to set them
  // once.
  glBindTexture(GL_TEXTURE_2D, g_lcdTexture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

  // allocate memory on the graphics card for the texture. It's fine if
  // texture_data doesn't have any data in it, the texture will just appear
  // black until you update it.
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, g_gpu.width, g_gpu.height, 0, GL_RGB,
               GL_UNSIGNED_BYTE, g_gpu.screenData);

  // Main loop
#ifdef __EMSCRIPTEN__
  emscripten_set_main_loop(main_loop, 0, 1);
#else
  while (!g_quit) {
    main_loop();
  }
#endif

  // Cleanup
  // When you're done using the texture, delete it. This will set texname to 0 &
  // delete all of the graphics card memory associated with the texture. If you
  // don't call this method, the texture will stay in graphics card memory until
  // you close the application.
  glDeleteTextures(1, &g_lcdTexture);

  // Close game controllers
  for (int i = 0; i < MAX_CONTROLLERS; ++i) {
    if (controllers[i]) {
      SDL_GameControllerClose(controllers[i]);
    }
  }

  ImGui_ImplSdl_Shutdown();
  SDL_GL_DeleteContext(glcontext);
  SDL_DestroyWindow(g_window);
  SDL_Quit();

  return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x80, %rsp
movl $0x0, -0x4(%rbp)
movl %edi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
leaq 0x2b8e8(%rip), %rax     # 0x83e38
addq $0x18, %rax
movq %rax, 0x2db85(%rip)     # 0x860e0
leaq 0x3e99e(%rip), %rax     # 0x96f00
movq %rax, 0x2b957(%rip)     # 0x83ec0
leaq 0x2db70(%rip), %rdi     # 0x860e0
callq 0x51200
cmpl $0x2, -0x8(%rbp)
jge 0x58595
leaq 0xf4de(%rip), %rdi      # 0x67a60
movb $0x0, %al
callq 0x7070
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
jmp 0x5888e
movq -0x10(%rbp), %rax
movq 0x8(%rax), %rsi
leaq 0x2b894(%rip), %rdi     # 0x83e38
addq $0x18, %rdi
callq 0x4ea50
testb $0x1, %al
jne 0x585d3
movq -0x10(%rbp), %rax
movq 0x8(%rax), %rsi
leaq 0xf4bc(%rip), %rdi      # 0x67a7c
movb $0x0, %al
callq 0x7070
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
jmp 0x5888e
movl $0x20, %edi
callq 0x7920
cmpl $0x0, %eax
jge 0x58604
callq 0x77f0
movq %rax, %rsi
leaq 0xf4a1(%rip), %rdi      # 0x67a92
movb $0x0, %al
callq 0x7070
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
jmp 0x5888e
movl $0x0, -0x34(%rbp)
cmpl $0x4, -0x34(%rbp)
jge 0x58629
movslq -0x34(%rbp), %rax
movq $0x0, -0x30(%rbp,%rax,8)
movl -0x34(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x34(%rbp)
jmp 0x5860b
movl $0x2000, %edi           # imm = 0x2000
callq 0x7410
cmpl $0x0, %eax
jge 0x58650
callq 0x77f0
movq %rax, %rsi
leaq 0xf45a(%rip), %rdi      # 0x67aa1
movb $0x0, %al
callq 0x7070
jmp 0x586bf
callq 0x7450
movl %eax, -0x38(%rbp)
movl $0x0, -0x3c(%rbp)
movl $0x0, -0x40(%rbp)
movl -0x40(%rbp), %ecx
xorl %eax, %eax
cmpl -0x38(%rbp), %ecx
movb %al, -0x65(%rbp)
jge 0x5867d
cmpl $0x4, -0x3c(%rbp)
setl %al
movb %al, -0x65(%rbp)
movb -0x65(%rbp), %al
testb $0x1, %al
jne 0x58686
jmp 0x586bd
movl -0x40(%rbp), %edi
callq 0x78e0
cmpl $0x0, %eax
je 0x586b0
movl -0x40(%rbp), %edi
callq 0x75a0
movq %rax, %rcx
movslq -0x40(%rbp), %rax
movq %rcx, -0x30(%rbp,%rax,8)
movl -0x3c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3c(%rbp)
jmp 0x586b2
movl -0x40(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x40(%rbp)
jmp 0x58666
jmp 0x586bf
movl $0x5, %edi
movl $0x1, %esi
callq 0x77d0
movl $0x6, %edi
movl $0x18, %esi
callq 0x77d0
movl $0x7, %edi
movl $0x8, %esi
callq 0x77d0
movl $0x11, %edi
movl $0x2, %esi
callq 0x77d0
movl $0x12, %edi
movl $0x2, %esi
callq 0x77d0
xorl %edi, %edi
leaq -0x58(%rbp), %rsi
callq 0x7610
leaq 0xf3b1(%rip), %rdi      # 0x67acd
movl $0x2fff0000, %edx       # imm = 0x2FFF0000
movl $0x400, %ecx            # imm = 0x400
movl $0x300, %r8d            # imm = 0x300
movl $0x22, %r9d
movl %edx, %esi
callq 0x7370
movq %rax, 0x3e858(%rip)     # 0x96f98
movq 0x3e851(%rip), %rdi     # 0x96f98
callq 0x7550
movq %rax, -0x60(%rbp)
movq 0x3e841(%rip), %rdi     # 0x96f98
callq 0x4df80
movl $0x1, %edi
leaq 0x3e838(%rip), %rsi     # 0x96fa0
callq 0x7310
movl 0x3e82d(%rip), %esi     # 0x96fa0
movl $0xde1, %edi            # imm = 0xDE1
callq 0x78f0
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2801, %esi           # imm = 0x2801
movl $0x2600, %edx           # imm = 0x2600
callq 0x75c0
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2800, %esi           # imm = 0x2800
movl $0x2600, %edx           # imm = 0x2600
callq 0x75c0
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2802, %esi           # imm = 0x2802
movl $0x812f, %edx           # imm = 0x812F
callq 0x75c0
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2803, %esi           # imm = 0x2803
movl $0x812f, %edx           # imm = 0x812F
callq 0x75c0
movl 0x2d921(%rip), %ecx     # 0x860f4
movl 0x2d91e(%rip), %r8d     # 0x860f8
movl $0xde1, %edi            # imm = 0xDE1
xorl %r9d, %r9d
movl $0x1907, %edx           # imm = 0x1907
leaq 0x2d8f2(%rip), %rax     # 0x860e0
addq $0x1d, %rax
movl %r9d, %esi
movl $0x1907, (%rsp)         # imm = 0x1907
movl $0x1401, 0x8(%rsp)      # imm = 0x1401
movq %rax, 0x10(%rsp)
callq 0x7840
movb 0x3e77c(%rip), %al      # 0x96f90
xorb $-0x1, %al
testb $0x1, %al
jne 0x5881c
jmp 0x58823
callq 0x574d0
jmp 0x5880e
movl $0x1, %edi
leaq 0x3e771(%rip), %rsi     # 0x96fa0
callq 0x77e0
movl $0x0, -0x64(%rbp)
cmpl $0x4, -0x64(%rbp)
jge 0x58868
movslq -0x64(%rbp), %rax
cmpq $0x0, -0x30(%rbp,%rax,8)
je 0x5885b
movslq -0x64(%rbp), %rax
movq -0x30(%rbp,%rax,8), %rdi
callq 0x72b0
jmp 0x5885d
movl -0x64(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x64(%rbp)
jmp 0x5883b
callq 0x4e0e0
movq -0x60(%rbp), %rdi
callq 0x70a0
movq 0x3e71b(%rip), %rdi     # 0x96f98
callq 0x74a0
callq 0x7280
movl $0x0, -0x4(%rbp)
movl -0x4(%rbp), %eax
addq $0x80, %rsp
popq %rbp
retq
nopw (%rax,%rax)