const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron');
const Store = require("electron-store");
const fs = require("fs");
const util = require('util')
let Validator = require("jsonschema").Validator;
let validator = new Validator();

let all_schemas = [
  "src/schemas/current_schema.json",
  "src/schemas/1.0.0_schema.json",
]
let schema_updaters = {
  "1.0.0_schema.json": update_1_0_0_schema
}
let jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { window } = new JSDOM();
let $ = jQuery = require('jquery')(window);

const store = new Store();
let current_file = null;
let menu_template = [
  {
    label: "File",
    submenu: [
      {
        label: "Save",
        accelerator: "Ctrl+S",
        click: function () {
          let window = BrowserWindow.getFocusedWindow();
          window.webContents.send("save", false);
        }
      },
      {
        label: "Save As",
        accelerator: "Ctrl+Shift+S",
        click: function () {
          let window = BrowserWindow.getFocusedWindow();
          window.webContents.send("save", true);
        }
      },
      {
        label: "Open",
        accelerator: "Ctrl+O",
        click: function () {
          openFile();
        }
      },
      {
        label: "Open Recent",
        submenu:[]
      },
      {
        label: "Exit",
        click: function () {
          app.quit();
        }
      }
    ]
  }
]

let table_col_schema = {
  "id": "/table_column",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "field": {"type": "string"},
      "title": {"type": "string"},
      "sortable": {"type": "boolean"},
      "selectable": {"type": "boolean"},
    }
  }
}

let table_row_schema = {
  "id": "/table_row",
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "uid": {"type": "string", "pattern": /row\d+/},
    },
    "patternProperties": {
      "column\d+": {"type": "string"}
    },
    "minProperties": 2
  }
}

let table_schema = {
  "id": "/table",
  "type": "object",
  "properties": {
    "id": {"type": "string"},
    "name": {"type": "string"},
    "filter": {"type": "boolean"},
    "hidden": {"type": "boolean"},
    "tab_name": {"type": "string"},
    "description": {"type": "string"},
    "columns": {"$ref": "/table_column"},
    "rows": {"$ref": "/table_row"}
  }
}

let data_file_schema = {
  "type": "object",
  "properties": {
    "version": {"type": "string"},
    "tables": {"$ref": "/table"}
  }
}

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
  app.quit();
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createSplashScreen);

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) {
    createSplashScreen();
  }
});

function createSplashScreen() {
  const splash_screen = new BrowserWindow({
    width: 806,
    height: 529,
    autoHideMenuBar: true,
    webPreferences: {
      nodeIntegration: true
    },
    resizable: false,
    frame: true
  });

  if (!store.has("recent_files")) {
    store.set("recent_files", []);
  }

  splash_screen.loadFile('src/splash.html');

  // Open the DevTools.
  splash_screen.webContents.openDevTools();
  // store.openInEditor();
}

function createEditorWindow() {
  let main_window = new BrowserWindow({
    width: 1000,
    height: 600,
    autoHideMenuBar: false,
    webPreferences: {
      nodeIntegration: true
    }
  });
  main_window.maximize();
  updateMenuBar();

  main_window.loadFile("src/editor.html");
  // Open the DevTools.
  main_window.webContents.openDevTools();
  return main_window;
}

function createNew() {
  let old_window = BrowserWindow.getFocusedWindow();
  let main_window = createEditorWindow();
  main_window.once("ready-to-show", () => {
    main_window.webContents.send("open-default");
  })
  old_window.close();
}

function chooseFile(title, for_save=false) {
  let options = {
    title: title,
    defaultPath: store.get("default_dir", app.getPath("documents")),
    filters: [
      {name: 'json', extensions: ['json']},
      {name: 'All Files', extensions: ['*']}
    ]
  }

  let file_path;
  if (for_save) {
    file_path = dialog.showSaveDialogSync(options);
  } else {
    file_path = dialog.showOpenDialogSync(options);
  }
  if (file_path) {
    if (for_save) {
      return file_path
    } else {
      return file_path[0];
    }
  }
}

function openFile(file_path="", new_file=false) {
  if (file_path === "") {
    file_path = chooseFile("Open website data");
  }
  if (file_path) {
    let result = checkFile(file_path);
    if (result) {
      let json = getUpdatedJson(result);
      updateRecentFiles(file_path);
      current_file = file_path
      if (new_file) {
        let old_window = BrowserWindow.getFocusedWindow();
        let window = createEditorWindow();
        window.once("ready-to-show", () => {
          window.webContents.send("open", json);
        })
        old_window.close();
      } else {
        let window = BrowserWindow.getFocusedWindow();
        window.webContents.send("open", json);
      }
    }
  }
}

function saveFile(json_data, create_new_file) {
  if (current_file == null || create_new_file) {
    let file_path = chooseFile("Save website data", true);
    if (file_path) {
      updateRecentFiles(file_path);
      current_file = file_path;
    }
  }
  if (current_file) {
    let tables_data = json_data["tables"]
    fs.writeFileSync(current_file, JSON.stringify(json_data, null, 4));
    let nav_content_path = current_file.replace(".json", "-nav-content.html");
    let tab_content_path = current_file.replace(".json", "-tab-content.html");
    let nav = "";
    let tabs = "";
    let table_tabs = {"": []};
    let tables_added = [];

    for (let table_data of tables_data) {
      if (table_data["tab_name"] === "") {
        table_tabs[""].push(table_data["id"]);
      } else {
        if (!Object.keys(table_tabs).includes(table_data["tab_name"])) {
          table_tabs[table_data["tab_name"]] = [table_data];
        } else {
          table_tabs[table_data["tab_name"]].push(table_data);
        }
      }
    }

    for (let table_data of tables_data) {
      let table_id = table_data["id"];
      if (table_data["hidden"]) {continue;}
      if (tables_added.includes(table_id)) {continue;}
      let li;
      if (table_tabs[""].includes(table_id)) {
        li = $("<li>").addClass("nav-item");
        let tab_link = $("<a>")
          .attr({"id": table_id+"-tab",
            "class": "nav-link",
            "data-bs-toggle": "tab",
            "href": "#"+table_id,
            "role": "tab",
            "aria-controls": table_id,
            "aria-selected": false})
          .html(table_data["name"]);
        li.append(tab_link);
        let div = $("<div>")
          .attr({"id": table_id,
            "class": "tab-pane fade",
            "role": "tabpanel",
            "aria-labelledby": table_id+"-tab"})
        let h3 = $("<h3>")
          .attr("id", table_id+"-header")
          .html(`${table_data["name"]} (<span id="${table_id}-completed"></span>/<span id="${table_id}-total"></span>)`);
        div.append(h3)
          .append(`<p>${table_data["description"]}</p>`)
          .append(`<table id="${table_data["name"]}-table" class="table table-hover"></table>`);
        tabs += div.prop("outerHTML");
        tables_added.push(table_id)
      } else {
        li = $("<li>").addClass("nav-item dropdown");
        let a = $("<a>")
          .attr({"class": "nav-link dropdown-toggle",
            "href": "#",
            "data-bs-toggle": "dropdown",
            "role": "button",
            "aria-expanded": false})
          .html(table_data["tab_name"]);
        li.append(a)
        let ul = $("<ul>").addClass("dropdown-menu");
        for (let table of table_tabs[table_data["tab_name"]]) {
          let nav_link = $("<li>").append($("<a>")
            .attr({"id": table["id"]+"-tab",
              "class": "dropdown-item",
              "data-bs-toggle": "tab",
              "href": "#"+table["id"],
              "role": "tab",
              "aria-controls": table["id"],
              "aria-selected": false})
            .html(table["name"]));
          ul.append(nav_link);
          let div = $("<div>")
            .attr({"id": table["id"],
              "class": "tab-pane fade",
              "role": "tabpanel",
              "aria-labelledby": table["id"]+"-tab"})
          let h3 = $("<h3>")
            .attr("id", table["id"]+"-header")
            .html(`${table["name"]} (<span id="${table["id"]}-completed"></span>/<span id="${table["id"]}-total"></span>)`);
          div.append(h3)
            .append(`<p>${table["description"]}</p>`)
            .append(`<table id="${table["name"]}-table" class="table table-hover"></table>`);
          tabs += div.prop("outerHTML");
          tables_added.push(table["id"]);
        }
        li.append(ul);
      }
      nav += li.prop("outerHTML");
    }
    fs.writeFileSync(nav_content_path, nav);
    fs.writeFileSync(tab_content_path, tabs);
  }
}

function updateRecentFiles(new_file) {
  store.set("default_dir", new_file);
  let recent_files = store.get("recent_files");
  if (recent_files.includes(new_file)) {
    recent_files.splice(recent_files.indexOf(new_file), 1);
  }
  recent_files.splice(0, 0, new_file);
  store.set("recent_files", recent_files);
  updateMenuBar();
}

function updateMenuBar() {
  let recent_files = store.get("recent_files");
  for (let menu of menu_template[0].submenu) {
    if (menu.label === "Open Recent") {
      menu.submenu = []
      for (let file of recent_files) {
        if (fs.existsSync(file)) {
          menu.submenu.push({
            label: file, click: () => {
              openFile(file);
            }
          });
        }
      }
    }
  }
  let menu = Menu.buildFromTemplate(menu_template);
  Menu.setApplicationMenu(menu);
}

function loadJson(file_path) {
  let file = fs.readFileSync(file_path);
  return JSON.parse(file.toString());
}

function checkFile(file_path) {
  let json = loadJson(file_path);
  let result;
  let valid_result = false;
  for (let schema_path of all_schemas) {
    let schema = loadJson(schema_path);
    result = validator.validate(json, schema);
    if (result.valid) {
      let split_schema = schema_path.split("/");
      result = {"schema": split_schema[split_schema.length-1], "result": result};
      valid_result = true;
      break;
    }
  }
  if (valid_result) {
    return result;
  }
}

function getUpdatedJson(validation_result) {
  let json = validation_result["result"].instance;
  let schema = validation_result["schema"];
  if (schema === "current_schema.json") {
    return json
  }
  return schema_updaters[schema](json);
}

function update_1_0_0_schema(json) {
  $.each(json, (i, table_data) => {
    table_data["id"] = "table"+i;
    let col_dict = {};
    $.each(table_data["col-def"], (j, col_data) => {
      col_dict[col_data["field"]] = "column"+j;
      col_data["field"] = "column"+j;
    });
    table_data["columns"] = table_data["col-def"];
    delete table_data["col-def"];
    $.each(table_data["rows"], (j, row_data) => {
      row_data["uid"] = "row"+j;
      $.each(col_dict, (old_key, new_key) => {
        row_data[new_key] = row_data[old_key];
        delete row_data[old_key];
      });
    });
  });
  json = {
    "version": "1.1.0",
    "tables": json,
  }
  return json;
}

function duplicateJson(json) {
  return JSON.parse(JSON.stringify(json));
}

ipcMain.handle("get-store-value", (event, key) => {
  return store.get(key)
})

ipcMain.on("create-new", (event) => {
  createNew();
})

ipcMain.on("save", (event, json_data, create_new_file) => {
  saveFile(json_data, create_new_file);
})

ipcMain.on("open", (event, file_path="") => {
  openFile(file_path, true)
})
