// File: /workspace/github_cpp_makefile_star2_O2/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
pushq %r15
pushq %r14
pushq %rbx
subq $0x48, %rsp
movq %rsi, %rbx
movl %edi, %r14d
leaq 0x25c8d(%rip), %rax     # 0x5ae10
movq %rax, 0x27f16(%rip)     # 0x5d0a0
leaq 0x27f0f(%rip), %rdi     # 0x5d0a0
leaq 0x38d28(%rip), %rax     # 0x6dec0
movq %rax, 0x25ce1(%rip)     # 0x5ae80
callq 0x31cfc
cmpl $0x1, %r14d
jg 0x351bb
leaq 0xdef0(%rip), %rdi      # 0x430a1
callq 0x7730
jmp 0x35256
movq 0x8(%rbx), %rsi
leaq 0x25c4a(%rip), %rdi     # 0x5ae10
callq 0x31580
testb %al, %al
je 0x351ee
pushq $0x20
popq %rdi
callq 0x78b0
testl %eax, %eax
js 0x35240
xorl %eax, %eax
cmpq $0x4, %rax
je 0x351fb
andq $0x0, -0x40(%rbp,%rax,8)
incq %rax
jmp 0x351dd
movq 0x8(%rbx), %rsi
leaq 0xdc7f(%rip), %rdi      # 0x42e78
jmp 0x3524f
movl $0x2000, %edi           # imm = 0x2000
callq 0x73e0
testl %eax, %eax
js 0x35264
callq 0x7410
movslq %eax, %r14
xorl %ebx, %ebx
xorl %r15d, %r15d
cmpq %r14, %rbx
jge 0x3527a
cmpl $0x3, %r15d
jg 0x3527a
movl %ebx, %edi
callq 0x7860
testl %eax, %eax
je 0x3523b
movl %ebx, %edi
callq 0x7560
movq %rax, -0x40(%rbp,%rbx,8)
incl %r15d
incq %rbx
jmp 0x35216
callq 0x7790
leaq 0xdc42(%rip), %rdi      # 0x42e8e
movq %rax, %rsi
xorl %eax, %eax
callq 0x7070
pushq $-0x1
popq %rax
addq $0x48, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
callq 0x7790
leaq 0xdc2d(%rip), %rdi      # 0x42e9d
movq %rax, %rsi
xorl %eax, %eax
callq 0x7070
pushq $0x5
popq %rdi
pushq $0x1
popq %r14
movl %r14d, %esi
callq 0x7770
pushq $0x6
popq %rdi
pushq $0x18
popq %rsi
callq 0x7770
pushq $0x7
popq %rdi
pushq $0x8
popq %rsi
callq 0x7770
pushq $0x11
popq %rdi
pushq $0x2
popq %rbx
movl %ebx, %esi
callq 0x7770
pushq $0x12
popq %rdi
movl %ebx, %esi
callq 0x7770
leaq -0x58(%rbp), %rsi
xorl %edi, %edi
callq 0x75b0
leaq 0xdc01(%rip), %rdi      # 0x42ec9
pushq $0x22
popq %r9
movl $0x2fff0000, %esi       # imm = 0x2FFF0000
movl $0x2fff0000, %edx       # imm = 0x2FFF0000
movl $0x400, %ecx            # imm = 0x400
movl $0x300, %r8d            # imm = 0x300
callq 0x7330
movq %rax, 0x38c6b(%rip)     # 0x6df58
movq %rax, %rdi
callq 0x7510
movq %rax, %rbx
movq 0x38c59(%rip), %rdi     # 0x6df58
callq 0x310e2
leaq 0x38c55(%rip), %rsi     # 0x6df60
movl %r14d, %edi
callq 0x72d0
movl 0x38c47(%rip), %esi     # 0x6df60
movl $0xde1, %edi            # imm = 0xDE1
callq 0x7870
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2801, %esi           # imm = 0x2801
movl $0x2600, %edx           # imm = 0x2600
callq 0x7570
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2800, %esi           # imm = 0x2800
movl $0x2600, %edx           # imm = 0x2600
callq 0x7570
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2802, %esi           # imm = 0x2802
movl $0x812f, %edx           # imm = 0x812F
callq 0x7570
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2803, %esi           # imm = 0x2803
movl $0x812f, %edx           # imm = 0x812F
callq 0x7570
movl 0x27d3b(%rip), %ecx     # 0x5d0b4
movl 0x27d38(%rip), %r8d     # 0x5d0b8
subq $0x8, %rsp
leaq 0x27d32(%rip), %rax     # 0x5d0bd
movl $0xde1, %edi            # imm = 0xDE1
xorl %esi, %esi
movl $0x1907, %edx           # imm = 0x1907
xorl %r9d, %r9d
pushq %rax
pushq $0x1401                 # imm = 0x1401
pushq $0x1907                 # imm = 0x1907
callq 0x77d0
addq $0x20, %rsp
cmpb $0x0, 0x38b9b(%rip)     # 0x6df50
jne 0x353be
callq 0x34734
jmp 0x353ae
leaq 0x38b9b(%rip), %rsi     # 0x6df60
pushq $0x1
popq %rdi
callq 0x7780
xorl %r14d, %r14d
cmpq $0x4, %r14
je 0x353ea
movq -0x40(%rbp,%r14,8), %rdi
testq %rdi, %rdi
je 0x353e5
callq 0x7280
incq %r14
jmp 0x353d0
callq 0x3116e
movq %rbx, %rdi
callq 0x70a0
movq 0x38b5a(%rip), %rdi     # 0x6df58
callq 0x7460
callq 0x7250
xorl %eax, %eax
jmp 0x35259