LibraryManager* libraryManager = nullptr;

LibraryBrowser::LibraryBrowser(Emulator::Interface& emulator) : emulator(emulator) {
  setMargin(5);

  informationType.setText({
    "Title:\n",
    "Serial:"
  });

  for(auto& media : emulator.media) {
    mediaMode.append(media.name);
  }

  unsigned height = Font::size(program->normalFont, " ").height;

  append(folders, {~0, ~0}, 5);
  append(informationLayout, {~0, 0});
  informationLayout.append(informationType, {0, height * 2}, 5);
  informationLayout.append(information, {~0, height * 2}, 5);
  informationLayout.append(mediaMode, {0, 0});

  folders.onActivate = {&LibraryBrowser::onActivate, this};
  folders.onChange = {&LibraryBrowser::onChange, this};
  mediaMode.onChange = {&LibraryBrowser::setMode, this};
}

void LibraryBrowser::onActivate() {
  if(folders.selected() == false) return;
  if(libraryManager->loadButton.enabled() == false) return;

  unsigned selection = folders.selection();
  string pathname = {this->pathname, folders.text(selection, 0), typeSuffix};

  libraryManager->loaded.append(folders.text(selection, 0));
  libraryManager->setInformation(false);

  if(libraryManager->slotLoad == false) {
    utility->loadMedia(pathname);
  } else {
    libraryManager->loadPathname = pathname;
    libraryManager->setModal(false);
  }
}

void LibraryBrowser::onChange() {
  if(folders.selected() == false) return information.setText("");

  string manifest = {pathname, folders.text(folders.selection(), 0), typeSuffix, "manifest.bml"};
  auto document = Markup::Document(file::read(manifest));

  information.setText({
    document["information/title"].text(), "\n",
    document["information/serial"].text()
  });
}

void LibraryBrowser::refresh() {
  folders.reset();
  lstring pathnames = directory::ifolders(pathname, typeMask);
  unsigned selection = 0;
  for(auto& pathname : pathnames) {
    folders.append(string{pathname}.rtrim<1>(typeSuffix));
    folders.setImage(selection++, 0, {resource::game, sizeof resource::game});
  }
  folders.setSelection(0);
  onChange();
}

void LibraryBrowser::setMode() {
  config->library.mediaMode = mediaMode.selection();
  auto& media = emulator.media[mediaMode.selection()];

  pathname = {utility->libraryPath(), media.name, "/"};
  type = media.type;
  typeMask = {"*.", type};
  typeSuffix = {".", type, "/"};

  refresh();
  folders.setFocused();
  libraryManager->synchronize();
}

LibraryImport::LibraryImport() {
  setMargin(5);
  information.setText({
    "higan manages games in a library. To play a game, you must first import the game.\n"
    "After doing so, the game will appear inside your library, and can then be loaded and played."
  });
  importButton.setText("Import Game ...");
  append(information, {~0, 0}, 5);
  append(importButton, {0, 0});

  importButton.onActivate = {&LibraryImport::onImportActivate, this};
}

void LibraryImport::onImportActivate() {
  if(program->ananke.open() == false) {
    MessageWindow().setText("ananke must be installed to use this feature").warning();
    return;
  }
  function<string ()> browse = program->ananke.sym("ananke_browse");
  if(!browse) return;
  audio.clear();  //ananke's browser is modal
  string pathname = browse();
  pathname.rtrim<1>("/");
  if(pathname.empty()) return;

  //after importing game, take user to the relevant game list to show the newly imported title
  string type = extension(pathname);
  for(signed bootable = 1; bootable >= 0; bootable--) {
    unsigned selection = 0;
    for(auto& browser : libraryManager->browsers) {
      unsigned mode = 0;
      for(auto& media : browser->emulator.media) {
        if(type == media.type && media.bootable == bootable) {
          browser->mediaMode.setSelection(mode);
          libraryManager->libraryFrame.setSelection(selection);
          libraryManager->onChange();

          //find game in list and select it
          string name = notdir(nall::basename(pathname));
          for(unsigned n = 0; n < browser->folders.rows(); n++) {
            if(browser->folders.text(n, 0) == name) {
              browser->folders.setSelection(n);
              browser->onChange();
              break;
            }
          }

          return;
        }
        mode++;
      }
      selection++;
    }
  }
}

LibraryManager::LibraryManager() {
  libraryManager = this;

  setTitle("Game Library");
  setGeometry({128, 128, 640, 680});
  windowManager->append(this, "LibraryManager");

  layout.setMargin(5);
  bootstrap();
  libraryFrame.append("Import");
  libraryFrame.setLayout(browsers.size(), libraryImport);
  loadButton.setText("Load");

  unsigned height = Font::size(program->normalFont, " ").height;

  append(layout);
  layout.append(libraryFrame, {~0, ~0}, 5);
  layout.append(informationLayout, {~0, 0});
  informationLayout.append(information, {~0, height * 3}, 5);
  informationLayout.append(skipButton, {80, 0}, 5);
  informationLayout.append(loadButton, {80, 0});

  onClose = skipButton.onActivate = [&] {
    setModal(false);
    setVisible(false);
  };

  libraryFrame.onChange = {&LibraryManager::onChange, this};
  loadButton.onActivate = {&LibraryManager::onLoad, this};

  //initial config value of -1 defaults to import tab on first launch of higan
  if(config->library.selection < 0) config->library.selection = browsers.size();
  libraryFrame.setSelection(config->library.selection);

  if(libraryFrame.selection() < browsers.size()) {
    browsers[libraryFrame.selection()]->mediaMode.setSelection(config->library.mediaMode);
    browsers[libraryFrame.selection()]->setMode();
  }
}

void LibraryManager::bootstrap() {
  unsigned selection = 0;
  for(auto& emulator : program->emulator) {
    LibraryBrowser* browser = new LibraryBrowser(*emulator);
    libraryFrame.append(emulator->information.name);
    libraryFrame.setLayout(selection++, *browser);
    browsers.append(browser);
  }
}

string LibraryManager::load(const string& type) {
  requestedLoadType = type;
  unsigned selection = 0;
  for(auto& browser : browsers) {
    unsigned mode = 0;
    for(auto& media : browser->emulator.media) {
      if(type == media.type && media.bootable == false) {
        libraryFrame.setSelection(selection);
        browser->mediaMode.setSelection(mode);
        browser->setMode();

        slotLoad = true;
        loadPathname = "";
        show();
        setModal();
        slotLoad = false;
        browser->mediaMode.setSelection(config->library.mediaMode = 0);
        return loadPathname;
      }
      mode++;
    }
    selection++;
  }
  return "";  //should never occur
}

void LibraryManager::onChange() {
  unsigned selection = libraryFrame.selection();
  config->library.selection = selection;
  if(selection < browsers.size()) {
    browsers[selection]->setMode();
  } else {
    loadButton.setEnabled(false);
  }
}

void LibraryManager::onLoad() {
  unsigned selection = libraryFrame.selection();
  if(selection < browsers.size()) browsers[selection]->onActivate();
}

void LibraryManager::setInformation(bool load) {
  string text;
  if(loaded.size() == 0) {
    text = {" \nPlease select a game to load ...\n "};
  } else if(loaded.size() == 1 && load == false) {
    text = {" \n", loaded[0], "\n "};
  } else if(loaded.size() == 1 && load == true) {
    text = {loaded[0], " \nPlease select a slot game to load ...\n "};
  } else if(loaded.size() == 2 && load == false) {
    text = {loaded[0], "\n", loaded[1], "\n "};
  } else if(loaded.size() == 2 && load == true) {
    text = {loaded[0], "\n", loaded[1], "\nPlease select a slot game to load ..."};
  } else if(loaded.size() == 3) {
    text = {loaded[0], "\n", loaded[1], "\n", loaded[2]};
  }
  information.setText(text);
}

void LibraryManager::show() {
  if(slotLoad == false) {
    loaded.reset();
    requestedLoadType.reset();
    skipButton.setText("Cancel");
  } else {
    skipButton.setText("Skip");
  }

  setInformation(true);
  setVisible();
  setFocused();
  onChange();
}

//set library to show a specific media type, and then show the library
void LibraryManager::show(const string& type) {
  unsigned selection = 0;
  for(auto& browser : browsers) {
    unsigned mode = 0;
    for(auto& media : browser->emulator.media) {
      if(media.bootable && media.type == type) {
        libraryFrame.setSelection(selection);
        browser->mediaMode.setSelection(mode);
        browser->setMode();
        return show();
      }
      mode++;
    }
    selection++;
  }
}

void LibraryManager::synchronize() {
  if(libraryFrame.selection() < browsers.size()) {
    auto& emulator = browsers[libraryFrame.selection()]->emulator;
    auto& media = emulator.media[browsers[libraryFrame.selection()]->mediaMode.selection()];

    if(requestedLoadType.empty()) {
      loadButton.setEnabled(media.bootable);
    } else {
      bool enabled = (requestedLoadType == media.type);
      //allow Super Game Boy to load Game Boy Color games
      if(requestedLoadType == "gb" && loaded.size() == 1 && media.type == "gbc") enabled = true;
      loadButton.setEnabled(enabled);
    }
  } else {
    loadButton.setEnabled(false);
  }
}
