// File: /workspace/github_cpp_makefile_star2_O1/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 0x23c27(%rip), %rax     # 0x5fe20
movq %rax, 0x25eb0(%rip)     # 0x620b0
leaq 0x25ea9(%rip), %rdi     # 0x620b0
leaq 0x36cc2(%rip), %rax     # 0x72ed0
movq %rax, 0x23c7b(%rip)     # 0x5fe90
callq 0x38910
cmpl $0x1, %r14d
jg 0x3c231
leaq 0xdc42(%rip), %rdi      # 0x49e69
callq 0x7770
jmp 0x3c2ca
movq 0x8(%rbx), %rsi
leaq 0x23be4(%rip), %rdi     # 0x5fe20
callq 0x381ea
testb %al, %al
je 0x3c2a7
movl $0x20, %edi
callq 0x78e0
testl %eax, %eax
js 0x3c2b4
xorps %xmm0, %xmm0
movaps %xmm0, -0x30(%rbp)
movaps %xmm0, -0x40(%rbp)
movl $0x2000, %edi           # imm = 0x2000
callq 0x73f0
testl %eax, %eax
js 0x3c2da
callq 0x7420
testl %eax, %eax
jle 0x3c2f0
movl %eax, %r14d
xorl %ebx, %ebx
xorl %r15d, %r15d
movl %ebx, %edi
callq 0x78a0
testl %eax, %eax
je 0x3c297
movl %ebx, %edi
callq 0x7570
movq %rax, -0x40(%rbp,%rbx,8)
incl %r15d
incq %rbx
cmpq %r14, %rbx
jae 0x3c2f0
cmpl $0x4, %r15d
jl 0x3c27d
jmp 0x3c2f0
movq 0x8(%rbx), %rsi
leaq 0xd98e(%rip), %rdi      # 0x49c40
jmp 0x3c2c3
callq 0x77d0
leaq 0xd996(%rip), %rdi      # 0x49c56
movq %rax, %rsi
xorl %eax, %eax
callq 0x7070
movl $0xffffffff, %eax       # imm = 0xFFFFFFFF
addq $0x48, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
retq
callq 0x77d0
leaq 0xd97f(%rip), %rdi      # 0x49c65
movq %rax, %rsi
xorl %eax, %eax
callq 0x7070
movl $0x5, %edi
movl $0x1, %esi
callq 0x77b0
movl $0x6, %edi
movl $0x18, %esi
callq 0x77b0
movl $0x7, %edi
movl $0x8, %esi
callq 0x77b0
movl $0x11, %edi
movl $0x2, %esi
callq 0x77b0
movl $0x12, %edi
movl $0x2, %esi
callq 0x77b0
leaq -0x58(%rbp), %rsi
xorl %edi, %edi
callq 0x75e0
leaq 0xd944(%rip), %rdi      # 0x49c91
movl $0x2fff0000, %esi       # imm = 0x2FFF0000
movl $0x2fff0000, %edx       # imm = 0x2FFF0000
movl $0x400, %ecx            # imm = 0x400
movl $0x300, %r8d            # imm = 0x300
movl $0x22, %r9d
callq 0x7320
movq %rax, 0x36bf4(%rip)     # 0x72f68
movq %rax, %rdi
callq 0x7530
movq %rax, %rbx
movq 0x36be2(%rip), %rdi     # 0x72f68
callq 0x37d78
leaq 0x36bde(%rip), %rsi     # 0x72f70
movl $0x1, %edi
callq 0x72d0
movl 0x36bce(%rip), %esi     # 0x72f70
movl $0xde1, %edi            # imm = 0xDE1
callq 0x78b0
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2801, %esi           # imm = 0x2801
movl $0x2600, %edx           # imm = 0x2600
callq 0x7590
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2800, %esi           # imm = 0x2800
movl $0x2600, %edx           # imm = 0x2600
callq 0x7590
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2802, %esi           # imm = 0x2802
movl $0x812f, %edx           # imm = 0x812F
callq 0x7590
movl $0xde1, %edi            # imm = 0xDE1
movl $0x2803, %esi           # imm = 0x2803
movl $0x812f, %edx           # imm = 0x812F
callq 0x7590
movl 0x25cc2(%rip), %ecx     # 0x620c4
movl 0x25cbf(%rip), %r8d     # 0x620c8
subq $0x8, %rsp
leaq 0x25cb9(%rip), %rax     # 0x620cd
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 0x7810
addq $0x20, %rsp
cmpb $0x0, 0x36b22(%rip)     # 0x72f60
jne 0x3c44e
callq 0x3b658
cmpb $0x1, 0x36b14(%rip)     # 0x72f60
jne 0x3c440
leaq 0x36b1b(%rip), %rsi     # 0x72f70
movl $0x1, %edi
callq 0x77c0
xorl %r14d, %r14d
movq -0x40(%rbp,%r14,8), %rdi
testq %rdi, %rdi
je 0x3c471
callq 0x7280
incq %r14
cmpq $0x4, %r14
jne 0x3c462
callq 0x37e04
movq %rbx, %rdi
callq 0x70a0
movq 0x36ada(%rip), %rdi     # 0x72f68
callq 0x7470
callq 0x7250
xorl %eax, %eax
jmp 0x3c2cf