var { ExtensionCommon } = ChromeUtils.import ?  ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm")
:   ChromeUtils.importESModule(
  "resource://gre/modules/ExtensionCommon.sys.mjs"
);
var { TagUtils } = ChromeUtils.import ?  ChromeUtils.import("resource:///modules/TagUtils.jsm")
:  ChromeUtils.importESModule("resource:///modules/TagUtils.sys.mjs");
//var Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

var win = Services.wm.getMostRecentWindow("mail:3pane");

//var { FileUtils } = ChromeUtils.importESModule("resource://gre/modules/FileUtils.sys.mjs");
//var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
var { FileUtils } = ChromeUtils.import ?  ChromeUtils.import("resource://gre/modules/FileUtils.jsm")
:  ChromeUtils.importESModule("resource://gre/modules/FileUtils.sys.mjs");


async function wait(t) {
  //	let t = 5000;
  await new Promise(resolve => win.setTimeout(resolve, t));
  //  console.log("waited in exp");
}

var resProto = Cc[
  "@mozilla.org/network/protocol;1?name=resource"
].getService(Ci.nsISubstitutingProtocolHandler);


function waitForResourceUrl() {
  let res = false;
  let i = 0;

  for (let i = 0; i < 5000; i++) {
    //do {
    try {
      res = resProto.hasSubstitution("xnote");
    }

    catch (ev) { };
    i++;
    // console.log("res url exists", i, res);
    if (res) break;
    //    await wait (100);
    //  }   while (!res);
  };
};



async function waitForResourceUrlA() {
  let res = false;
  let i = 0;

  for (let i = 0; i < 5000; i++) {
    //do {
    try {
      res = resProto.hasSubstitution("xnote");
    }

    catch (ev) { };
    i++;
    //    console.log("res url exists", i, res);
    if (res) break;
    await wait(100);
    //  }   while (!res);
  };
};


//waitForResourceUrl();
//var { xnote } = ChromeUtils.import("resource://xnote/modules/xnote.jsm");
var xnote;



const XNOTE_BASE_PREF_NAME = "extensions.xnote.";

/**
 * This maps preference names to their types. This is needed as the prefs
 * system doesn't actually know what format you've stored your pref in.
 */
function prefType(name) {
  switch (name) {
    case XNOTE_BASE_PREF_NAME + "usetag": {
      return "bool";
    }
    case XNOTE_BASE_PREF_NAME + "dateformat": {
      return "string";
    }
    case XNOTE_BASE_PREF_NAME + "width": {
      return "int";
    }
    case XNOTE_BASE_PREF_NAME + "height": {
      return "int";
    }
    case XNOTE_BASE_PREF_NAME + "HorPos": {
      return "int";
    }
    case XNOTE_BASE_PREF_NAME + "VertPos": {
      return "int";
    }
    case XNOTE_BASE_PREF_NAME + "show_on_select": {
      return "bool";
    }
    case XNOTE_BASE_PREF_NAME + "show_in_messageDisplay": {
      return "bool";
    }
    case XNOTE_BASE_PREF_NAME + "show_first_x_chars_in_col": {
      return "int";
    }
    case XNOTE_BASE_PREF_NAME + "storage_path": {
      return "string";
    }
    case "mailnews.tags.xnote.tag": {
      return "string";
    }
    case "mailnews.tags.xnote.color": {
      return "string";
    }
    case XNOTE_BASE_PREF_NAME + "show_full_in_messageDisplay": {
      return "bool";
    }
    /* */
    case XNOTE_BASE_PREF_NAME + "fetched_old_XNotes": {
      return "bool";
    }
    case XNOTE_BASE_PREF_NAME + "show_column": {
      return "bool";
    }

  }
  console.error(`Unexpected pref type ${name}`);
  throw new Error(`Unexpected pref type ${name}`);
}


//console.log("xnote - experiments API");
var xnoteapi = class extends ExtensionCommon.ExtensionAPI {
  getAPI(context) {
    return {
      xnoteapi: {


        async loadXNoteJSM() {
          //          await waitForResourceUrlA();
          let  xnoteloc  = ChromeUtils.import ?  ChromeUtils.import("resource://xnote/modules/xnote.jsm")
          :  ChromeUtils.importESModule("resource://xnote/modules/xnote.sys.mjs");
                  await wait(2000);
          //                  console.log("xnote", xnoteloc);
          xnote = xnoteloc.xnote;

        },


        async init() {

          xnote.ns.Commons.init();
          //         console.log("context", context );
          xnote.ns.Commons.context = context;
          xnote.ns.Storage.updateStoragePath();
          //TODO: Move the stored version to the browser storage or do the check 
          //for a previous installation before preferences are migrated
          let storedVersion = xnote.ns.Commons.xnoteLegacyPrefs.prefHasUserValue("version") ?
            xnote.ns.Commons.xnoteLegacyPrefs.getStringPref("version") : null; //getCharPref

          //        console.log(`storedVersion: ${storedVersion}; comparison: `+ (storedVersion == null));
          xnote.ns.Commons.isNewInstallation = storedVersion == null;
          xnote.ns.Upgrades.checkUpgrades(storedVersion, xnote.ns.Commons.XNOTE_VERSION)
          xnote.ns.Commons.xnoteLegacyPrefs.setStringPref("version", xnote.ns.Commons.XNOTE_VERSION);  //setCharPref
          xnote.ns.Commons.checkXNoteTag();
        },

        async hasOpenNoteWindow(windowId) {  //not working with about:3pane
          //        console.error("hasOpenNoteWindow not working with about:3pane");
          let window = context.extension.windowManager.get(windowId).window;
          // There may be a better property to know wether the window still exists, top works.
          return !!(window?.xnoteOverlayObj?.xnoteWindow?.top);
        },

        async openNoteWindow(windowId, messageId, forceShowing, focus) {
          //          console.log("context openw", context );

          let upper = 280, left = 200, wi = 200, hei = 300;
          let wop = Services.wm.getMostRecentWindow("mail:3pane");

          //         window.xnoteOverlayObj.xnoteWindow = window.browsingContext.topChromeWindow
          /*       wop.openDialog("chrome://xnote/content/testwin.xhtml",
                             "_blank",
                             "chrome,dialog=no,all,screenX=" + left + ",screenY=" + upper + ",height=200,width=200");
     
     
   //              console.log("xnotewnd-exp", window.xnoteOverlayObj.xnoteWindow, window);
     
               
   //            debugger;
               */
          let wnd = context.extension.windowManager.get(windowId).window;
          let window = wnd.gTabmail.currentTabInfo.chromeBrowser.contentWindow;
          //        console.log("WL", wnd.WL ,window.WL,wop.WL);
          //        console.log("opennotewind", windowId, wnd, wnd.gTabmail.currentTabInfo, window, wop, window.browsingContext.topChromeWindow,
          //          window.browsingContext.topChromeWindow == wop);
          let msgHdr = context.extension.messageManager.get(messageId);
          wnd.xnoteOverlayObj.closeNote();
          wnd.xnoteOverlayObj.note = new xnote.ns.Note(msgHdr.messageId); // we could pass in the headerMessageId directly
          wnd.xnoteOverlayObj.msgHdr = msgHdr;
          let note = wnd.xnoteOverlayObj.note;

          // This uses a core function to update the currently selected message, bad, get rid of it
          // and explicitly set the tag on the message. Directly in the background caller using WebExt API.
          //now done in onload         window.xnoteOverlayObj.updateTag(note.text);

          let xnotePrefs = xnote.ns.Commons.xnotePrefs;
          //console.log("open note", xnotePrefs.show_on_select, forceShowing, note.text != '', note.text);
          if (
            (xnotePrefs.show_on_select && note.text != '') ||
            forceShowing
          ) {
        //              console.log("shownote", note.text);
            wnd.xnoteOverlayObj.context_createNote(true,msgHdr.messageId); //parameter currently unused
            /*   */
            /*
                        window.xnoteOverlayObj.xnoteWindow = window.openDialog(
                          "chrome://xnote/content/xnote-window.xhtml",
                          "XNote",
                          `chrome,dependent=yes,resizable,modal=no, left=${window.screenX + note.x},top=${window.screenY + note.y},width=${note.width},height=${note.height}`,
                          note,
                          focus
                        );
            */
            /*   
            //           window.xnoteOverlayObj.xnoteWindow = window.browsingContext.topChromeWindow.openDialog(
            let wnd1, wnd2;
            window.xnoteOverlayObj.xnoteWindow = wnd.openDialog(
              "chrome://xnote/content/xnote-window.xhtml",
              "XNote",
              `chrome,dialog=no,all,screenX=${wnd.screenX + note.x},screenY=${wnd.screenY + note.y},outerWidth=${note.width},outerHeight=${note.height}`,
//              `chrome,dialog=no,all,screenX=${window.screenX + note.x},screenY=${window.screenY + note.y},outerWidth=${note.width},outerHeight=${note.height}`,
              note,
              focus
            );
            //chrome,dependent=yes,resizable,modal=no
//wnd2=wnd1;*/
            /*  
 //           let upper = 280, left = 200, wi = 200, hei = 300;

 window.xnoteOverlayObj.xnoteWindow = window.browsingContext.topChromeWindow
              .openDialog("chrome://xnote/content/testwin.xhtml",
                "_blank",
                "chrome,dialog=no,all,screenX=" + left + ",screenY=" + upper + ",height=200,width=200");

               // window.xnoteOverlayObj.xnoteWindow = wnd2;
              //  wnd2.focus();
*/
            //         console.log("xnotewnd-exp", window.xnoteOverlayObj.xnoteWindow, window);

            return true;
          };

          return false;
        },

        async closeNoteWindow(windowId) {
          //     console.log("exm close wId", windowId);

          let wnd = context.extension.windowManager.get(windowId).window;
          let window = wnd.gTabmail?.currentTabInfo?.chromeBrowser?.contentWindow;
          /*
                   //let window = wnd.gTabmail?.tabInfo[0]?.chromeBrowser?.contentWindow;
                  let xnoteWindow = window?.xnoteOverlayObj?.xnoteWindow;
                  //       let xnoteWindow = window.gTabmail.tabInfo[0].chromeBrowser.contentWindow.xnoteWindow;
   //               console.log("exm close xnoteWindow", xnoteWindow);
                  if (xnoteWindow) xnoteWindow.close();
           */
          wnd?.xnoteOverlayObj?.closeNote();
        },


        async closeNoteInTab(tabId) {
          //        console.log("exm close wId", windowId);

          let wnd = context.extension.windowManager.get(windowId).window;
          let window = wnd.gTabmail?.tabInfo[tabId]?.chromeBrowser?.contentWindow;
          /*
                   //let window = wnd.gTabmail?.tabInfo[0]?.chromeBrowser?.contentWindow;
                  let xnoteWindow = window?.xnoteOverlayObj?.xnoteWindow;
                  //       let xnoteWindow = window.gTabmail.tabInfo[0].chromeBrowser.contentWindow.xnoteWindow;
    //              console.log("exm close xnoteWindow", xnoteWindow);
                  if (xnoteWindow) xnoteWindow.close();
           */
          window?.xnoteOverlayObj?.closeNote();
        },

        async isExperimentReady() {
          return true;
        },


        async getAllXNoteKeys() {
          //       console.log("fi");
          let jsAll = [];
          let keys = [], keys_orig = [];
          //   let fileDir = FileUtils.getFile(xnote.ns.Storage.noteStorageDir);
          //                    let fileDir = FileUtils.getFile("ProfD", ["XNote"]);
          //_storageDir.clone();
          //console.log("stordir", xnote.ns.Storage.noteStorageDir);
          try {
            let fileDir = xnote.ns.Storage.noteStorageDir;//Services.dirsvc.get(xnote.ns.Storage.noteStorageDir, Ci.nsIFile);
            let filename;
//            console.log("fi", fileDir, fileDir.directoryEntries, fileDir.directoryEntries.length);
            for (let dd of fileDir.directoryEntries) {
              // console.log("fn", dd.leafName);
              if (
                !dd.isFile() ||
                !dd.isReadable() ||
                !dd.leafName.endsWith(".xnote")
              ) {
                continue;
              };
              let jsNote = {};





              if (dd.isFile()) {
                try {


                  filename = dd.leafName;

                  keys_orig.push(filename);
                  //name is:   notesFile.append(escape(messageId).replace(/\//g, "%2F") + '.xnote');

                  let convertedKey = filename.replace("/%2F/g", "/");
                  convertedKey = unescape(filename);
                  //console.log("converted fn", convertedKey);
                  let hdrId = convertedKey.substring(0, convertedKey.length - 6);
                  keys.push(hdrId);
                  //let note = xnote.ns.Note(convertedKey.substring(0, convertedKey.length - 6));
                  //            console.log("legnote", note);
                  //let jsn = JSON.stringify(note);
                  //let jsNote1 = JSON.parse(jsn);
                  //           console.log("old note", filename, jsn,jsNote1);
                  //       note.saveNoteToLocalStorage(hdrId, jsNote1);
                  // jsNote = [hdrId, jsNote1];
                  // jsAll.push(jsNote);
                }
                catch (ex) {
                  console.error("cannot convert xnote file", dd, ex);
                }
                ;
              }
            };
          } catch (ex) {
            console.error("no directory entries for xnotes");
          }
          return { keys: keys, keys_orig: keys_orig };
        },

        async getAllXNotesFromDirectory() {
          //        console.log("fi");
          //         let fileDir = FileUtils.getFile("ProfD", ["XNote"]);
          //        console.log("fi", fileDir, fileDir.directoryEntries);
          //          for (let dd of fileDir.directoryEntries) console.log(dd.displayName);
          //return [];




          //       console.log("fi");
          let jsAll = [];
          let keys = [], keys_orig = [];
          //   let fileDir = FileUtils.getFile(xnote.ns.Storage.noteStorageDir);
          //                    let fileDir = FileUtils.getFile("ProfD", ["XNote"]);
          //_storageDir.clone();
          //      console.log("stordir", xnote.ns.Storage.noteStorageDir);
          let fileDir = xnote.ns.Storage.noteStorageDir;//Services.dirsvc.get(xnote.ns.Storage.noteStorageDir, Ci.nsIFile);
          let filename;
          //      console.log("fi", fileDir, fileDir.directoryEntries);
          for (let dd of fileDir.directoryEntries) {
            //        console.log("fn", dd.leafName);
            if (
              !dd.isFile() ||
              !dd.isReadable() ||
              !dd.leafName.endsWith(".xnote")
            ) {
              continue;
            };
            let jsNote = {};





            if (dd.isFile()) {
              try {


                filename = dd.leafName;

                keys_orig.push(filename);
                //name is:   notesFile.append(escape(messageId).replace(/\//g, "%2F") + '.xnote');

                let convertedKey = filename.replace("/%2F/g", "/");
                convertedKey = unescape(filename);
                //             console.log("converted fn", convertedKey);
                let hdrId = convertedKey.substring(0, convertedKey.length - 6);
                keys.push(convertedKey.substring(0, convertedKey.length - 6));
                let note = xnote.ns.Note(convertedKey.substring(0, convertedKey.length - 6));
                //            console.log("legnote", note);
                let jsn = JSON.stringify(note);
                let jsNote1 = JSON.parse(jsn);
                //              console.log("old note", filename, jsn, jsNote1);
                //       note.saveNoteToLocalStorage(hdrId, jsNote1);
                jsNote = [hdrId, jsNote1];
                jsAll.push(jsNote);
              }
              catch (ex) {
                console.error("cannot convert xnote file", dd, ex);
              }
              ;
            }
          };
          return { keys: keys, keys_orig: keys_orig, all: jsAll };

        },

        async getXNote(id) {
          let note = {};
          try {

            let realMessage = context.extension.messageManager.get(id);
            // TODO: The constructor of xnote.ns.Note accepts a window as second
            //       parameter and falls back to the most recent one, if none is
            //       given. This should probably become multi window aware and
            //       the API should pass in a windowId and calculate the actuall
            //       window and specify that explicitly as second parameter here.
            note = new xnote.ns.Note(realMessage.messageId);
            //         console.log("xnote", note);

          } catch (ex) {
            console.error(`Could not get TB mesg`);
          }
          return { text: note.text, date: note.modificationDate };
        },

        async syncXNote(msgKey, XNote) {
          //not sending back to replicate after save
          //          debugger;
          let note = new xnote.ns.Note(msgKey);
          note.x = XNote.x;
          note.y = XNote.y;
          note.width = XNote.width;
          note.height = XNote.height;
          note.text = XNote.text;
          note.modificationDate = XNote.modificationDate;
          note.saveNote(false);  //not sending back to replicate after save


        },


        async deleteXNoteFromMsgKey(msgKey) {
          let note = new xnote.ns.Note(msgKey);
          note.deleteNote(false);  //not sending back to replicate after delete

        },



        async getXNoteFromMsgKey(msgKey) {
          let note = {};
          try {

            // TODO: The constructor of xnote.ns.Note accepts a window as second
            //       parameter and falls back to the most recent one, if none is
            //       given. This should probably become multi window aware and
            //       the API should pass in a windowId and calculate the actuall
            //       window and specify that explicitly as second parameter here.
            //console.log("exp key", msgKey);
            //  debugger;
            note = new xnote.ns.Note(msgKey);
            //         console.log("xnote", note);

          } catch (ex) {
            console.error(`Could not get TB mesg`);
          }
          if (note == null) return false; else return { note: JSON.parse(JSON.stringify(note)) };
        },


        async getPref(name) {
          return this.getTbPref(`${XNOTE_BASE_PREF_NAME}${name}`)
        },

        async setPreferences(prefs) {
          xnote.ns.Commons.xnotePrefs = prefs;
          //                   console.debug({"XnotePrefs" : xnote.ns.Commons.xnotePrefs});
          xnote.ns.Storage.updateStoragePath();
          xnote.ns.Commons.checkXNoteTag();
        },

        async getTbPref(name) {
          try {
            switch (prefType(name)) {
              case "bool": {
                return Services.prefs.getBoolPref(name);
              }
              case "int": {
                return Services.prefs.getIntPref(name);
              }
              case "char": {
                return Services.prefs.getStringPref(name);
              }
              case "string": {
                return Services.prefs.getStringPref(name);
              }
              default: {
                console.error(`Unexpected pref type for: ${name}`);
              }
            }
          } catch (ex) {
            console.error(`Could not get TB pref ${name}`, ex);
            return undefined;
          }
        },

        async setTbPref(name, value) {
          try {
            if (name == "mailnews.tags.xnote.color") {
              TagUtils.addTagToAllDocumentSheets("xnote", value);
              Services.prefs.setStringPref(name, value);
            }
            else switch (prefType(name)) {
              case "bool": {
                Services.prefs.setBoolPref(name, value);
                break;
              }
              case "int": {
                Services.prefs.setIntPref(name, value);
                break;
              }
              case "char": {
                Services.prefs.setStringPref(name, value);
                break;
              }
              case "string": {
                Services.prefs.setStringPref(name, value);
                break;
              }
              default:
                console.error(`Unknown preference type: ${prefType(name)}`)
            }
          } catch (ex) {
            console.error(`Could not set TB pref ${name}`, ex);
          }
        }
      }
    }
  }

  onShutdown(isAppShutdown) {
    //    console.debug(`onShutdown: isAppShutdown=${isAppShutdown}`);
    if (isAppShutdown) return;

   if (Components.utils.unload) Components.utils.unload("resource://xnote/modules/dateformat.jsm");
   if (Components.utils.unload) Components.utils.unload("resource://xnote/modules/commons.jsm");
   if (Components.utils.unload) Components.utils.unload("resource://xnote/modules/xnote.jsm");

    // invalidate the startup cache, such that after updating the addon the old
    // version is no longer cached
    Services.obs.notifyObservers(null, "startupcache-invalidate");
    Services.obs.notifyObservers(null, "chrome-flush-caches", null);
    //  console.log("end onShutdown in xn-exp")

  }
}
