#include "interface.hpp"
Application *application = nullptr;
RuleEditor *ruleEditor = nullptr;

#include "resource/resource.hpp"
#include "resource/resource.cpp"

Application::Application(const string &pathname) : pathname(pathname) {
  setTitle("kaijuu v05");
  setFrameGeometry({64, 64, 725, 480});

  layout.setMargin(5);
  statusLabel.setFont("Tahoma, 8, Bold");
  uninstallButton.setText("Uninstall");
  installButton.setText("Install");
  settingList.setHeaderText("Name", "Default", "Match", "Pattern", "Command");
  settingList.setHeaderVisible();
  appendButton.setText("Append");
  modifyButton.setText("Modify");
  moveUpButton.setText("Move Up");
  moveDownButton.setText("Move Down");
  removeButton.setText("Remove");
  resetButton.setText("Reset");
  helpButton.setText("Help ...");

  append(layout);
  layout.append(installLayout, {~0, 0}, 5);
    installLayout.append(statusLabel, {~0, 0}, 5);
    installLayout.append(uninstallButton, {80, 0}, 5);
    installLayout.append(installButton, {80, 0});
  layout.append(settingLayout, {~0, ~0});
    settingLayout.append(settingList, {~0, ~0}, 5);
    settingLayout.append(controlLayout, {0, ~0});
      controlLayout.append(appendButton, {80, 0}, 5);
      controlLayout.append(modifyButton, {80, 0}, 5);
      controlLayout.append(moveUpButton, {80, 0}, 5);
      controlLayout.append(moveDownButton, {80, 0}, 5);
      controlLayout.append(removeButton, {80, 0}, 5);
      controlLayout.append(spacer, {0, ~0});
      controlLayout.append(resetButton, {80, 0}, 5);
      controlLayout.append(helpButton, {80, 0}, 5);
      controlLayout.append(canvas, {80, 88});

  canvas.setImage({resource::icon, sizeof resource::icon});
  canvas.update();

  onClose = &OS::quit;
  installButton.onActivate = {&Application::install, this};
  uninstallButton.onActivate = {&Application::uninstall, this};
  settingList.onActivate = {&Application::modifyAction, this};
  settingList.onChange = {&Application::synchronize, this};
  appendButton.onActivate = {&Application::appendAction, this};
  modifyButton.onActivate = {&Application::modifyAction, this};
  moveUpButton.onActivate = {&Application::moveUpAction, this};
  moveDownButton.onActivate = {&Application::moveDownAction, this};
  removeButton.onActivate = {&Application::removeAction, this};
  resetButton.onActivate = {&Application::resetAction, this};
  helpButton.onActivate = [&] { nall::invoke("kaijuu.html"); };
  refresh();
  synchronize();
  setVisible();
}

void Application::synchronize() {
  if(registry::read({"HKLM/Software/Microsoft/Windows/CurrentVersion/Shell Extensions/Approved/", classID}) == classDescription) {
    statusLabel.setText("Extension status: installed");
    installButton.setEnabled(false);
    uninstallButton.setEnabled(true);
  } else {
    statusLabel.setText("Extension status: not installed");
    installButton.setEnabled(true);
    uninstallButton.setEnabled(false);
  }
  modifyButton.setEnabled(settingList.selected());
  moveUpButton.setEnabled(settingList.selected() && settings.rules.size() > 1 && settingList.selection() != 0);
  moveDownButton.setEnabled(settingList.selected() && settings.rules.size() > 1 && settingList.selection() < settings.rules.size() - 1);
  removeButton.setEnabled(settingList.selected());
  resetButton.setEnabled(settings.rules.size() > 0);
}

void Application::refresh() {
  settings.load();
  settingList.reset();
  for(auto &rule : settings.rules) {
    string match = "Nothing";
    if(rule.matchFiles && rule.matchFolders) match = "Everything";
    else if(rule.matchFiles) match = "Files";
    else if(rule.matchFolders) match = "Folders";
    settingList.append(rule.name, rule.defaultAction ? "Yes" : "No", match, rule.pattern, rule.command);
  }
  settingList.autoSizeColumns();
}

void Application::install() {
  string command = {"regsvr32 \"", pathname, classDriver, "\""};
  _wsystem(utf16_t(command));
  synchronize();
}

void Application::uninstall() {
  string command = {"regsvr32 /u \"", pathname, classDriver, "\""};
  _wsystem(utf16_t(command));
  synchronize();
}

void Application::appendAction() {
  ruleEditor->show();
}

void Application::modifyAction() {
  if(settingList.selected() == false) return;
  unsigned selection = settingList.selection();
  ruleEditor->show(selection);
}

void Application::moveUpAction() {
  if(settingList.selected() == false) return;
  if(settingList.selection() == 0) return;
  unsigned selection = settingList.selection();
  auto temp = settings.rules(selection - 1);
  settings.rules(selection - 1) = settings.rules(selection);
  settings.rules(selection) = temp;
  settings.save();
  refresh();
  settingList.setSelection(selection - 1);
  synchronize();
}

void Application::moveDownAction() {
  if(settingList.selected() == false) return;
  if(settingList.selection() >= settings.rules.size() - 1) return;
  unsigned selection = settingList.selection();
  auto temp = settings.rules(selection + 1);
  settings.rules(selection + 1) = settings.rules(selection);
  settings.rules(selection) = temp;
  settings.save();
  refresh();
  settingList.setSelection(selection + 1);
  synchronize();
}

void Application::removeAction() {
  if(settingList.selected() == false) return;
  unsigned selection = settingList.selection();
  settings.rules.remove(selection);
  settings.save();
  refresh();
  synchronize();
}

void Application::resetAction() {
  if(MessageWindow::question(*this, "Warning: this will permanently remove all rules! Are you sure you want to do this?")
  == MessageWindow::Response::No) return;
  settings.rules.reset();
  settings.save();
  refresh();
  synchronize();
}

RuleEditor::RuleEditor() : index(-1) {
  setTitle("Rule Editor");
  setFrameGeometry({128, 256, 500, 160});

  layout.setMargin(5);
  nameLabel.setText("Name:");
  patternLabel.setText("Pattern:");
  commandLabel.setText("Command:");
  commandSelect.setText("Select ...");
  defaultAction.setText("Default Action");
  filesAction.setText("Match Files");
  foldersAction.setText("Match Folders");
  assignButton.setText("Assign");

  Font font("Tahoma, 8");
  unsigned length = 0;
  length = max(length, font.geometry("Name:").width);
  length = max(length, font.geometry("Pattern:").width);
  length = max(length, font.geometry("Command:").width);

  append(layout);
  layout.append(nameLayout, {~0, 0}, 5);
    nameLayout.append(nameLabel, {length, 0}, 5);
    nameLayout.append(nameValue, {~0, 0});
  layout.append(patternLayout, {~0, 0}, 5);
    patternLayout.append(patternLabel, {length, 0}, 5);
    patternLayout.append(patternValue, {~0, 0});
  layout.append(commandLayout, {~0, 0}, 5);
    commandLayout.append(commandLabel, {length, 0}, 5);
    commandLayout.append(commandValue, {~0, 0}, 5);
    commandLayout.append(commandSelect, {80, 0});
  layout.append(controlLayout, {~0, 0});
    controlLayout.append(defaultAction, {0, 0}, 5);
    controlLayout.append(filesAction, {0, 0}, 5);
    controlLayout.append(foldersAction, {0, 0}, 5);
    controlLayout.append(spacer, {~0, 0});
    controlLayout.append(assignButton, {80, 0});

  Geometry geometry = Window::geometry();
  geometry.height = layout.minimumGeometry().height;
  setGeometry(geometry);

  onClose = [&] {
    modal = false;
  };

  nameValue.onChange =
  patternValue.onChange =
  commandValue.onChange =
  {&RuleEditor::synchronize, this};

  commandSelect.onActivate = [&] {
    string pathname = DialogWindow::fileOpen(*this, application->pathname, "Programs (*.exe)", "All Files (*)");
    if(pathname.empty() == false) {
      pathname.transform("/", "\\");
      commandValue.setText({"\"", pathname, "\" {file}"});
    }
    synchronize();
  };

  nameValue.onActivate =
  patternValue.onActivate =
  commandValue.onActivate =
  assignButton.onActivate = [&] {
    Settings::Rule rule = {
      nameValue.text(),
      patternValue.text(),
      defaultAction.checked(),
      filesAction.checked(),
      foldersAction.checked(),
      commandValue.text()
    };
    if(index == -1) {
      settings.rules.append(rule);
    } else {
      settings.rules(index) = rule;
    }
    settings.save();
    application->refresh();
    application->synchronize();
    setVisible(false);
    modal = false;
  };
}

void RuleEditor::synchronize() {
  bool enable = true;
  if(nameValue.text().empty()) enable = false;
  if(patternValue.text().empty()) enable = false;
  if(commandValue.text().empty()) enable = false;
  assignButton.setEnabled(enable);
}

void RuleEditor::show(signed ruleID) {
  Settings::Rule rule{"", "", false, true, false, "", false};
  if(ruleID >= 0) rule = settings.rules(ruleID);

  index = ruleID;
  nameValue.setText(rule.name);
  patternValue.setText(rule.pattern);
  commandValue.setText(rule.command);
  defaultAction.setChecked(rule.defaultAction);
  filesAction.setChecked(rule.matchFiles);
  foldersAction.setChecked(rule.matchFolders);
  synchronize();
  setVisible();
  setFocused();
  nameValue.setFocused();

  setModal(modal = true);
  while(modal == true) {
    OS::processEvents();
  }
  setModal(false);
  application->setFocused();
}

typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);

bool isWow64() {
  LPFN_ISWOW64PROCESS fnIsWow64Process;
  fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process");
  if(!fnIsWow64Process) return false;
  BOOL result = FALSE;
  fnIsWow64Process(GetCurrentProcess(), &result);
  return result == TRUE;
}

int CALLBACK WinMain(HINSTANCE module, HINSTANCE, LPSTR, int) {
  if(isWow64()) {
    MessageWindow::critical(Window::none(), "Error: you must run kaijuu64.exe on 64-bit Windows.");
    return 0;
  }

  wchar_t filename[MAX_PATH];
  GetModuleFileNameW(module, filename, MAX_PATH);

  string pathname = (const char*)utf8_t(filename);
  pathname.transform("\\", "/");
  pathname = dir(pathname);

  application = new Application(pathname);
  ruleEditor = new RuleEditor;
  application->setFocused();
  OS::main();

  return 0;
}
