/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* globals getMessagePaneBrowser, addMenuItem, getSelectedCalendar
   createEventWithDialog*/

var { Extractor } = ChromeUtils.import("resource:///modules/calendar/calExtract.jsm");
var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
var { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");

XPCOMUtils.defineLazyModuleGetters(this, {
  CalEvent: "resource:///modules/CalEvent.jsm",
  CalTodo: "resource:///modules/CalTodo.jsm",
});

XPCOMUtils.defineLazyGetter(this, "extractService", () => {
  const { CalExtractParserService } = ChromeUtils.import(
    "resource:///modules/calendar/extract/CalExtractParserService.jsm"
  );
  return new CalExtractParserService();
});

var calendarExtract = {
  onShowLocaleMenu(target) {
    let localeList = document.getElementById(target.id);
    let langs = [];
    let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"]
      .getService(Ci.nsIXULChromeRegistry)
      .QueryInterface(Ci.nsIToolkitChromeRegistry);
    let langRegex = /^(([^-]+)-*(.*))$/;

    for (let locale of chrome.getLocalesForPackage("calendar")) {
      let localeParts = langRegex.exec(locale);
      let langName = localeParts[2];

      try {
        langName = cal.l10n.getAnyString("global", "languageNames", langName);
      } catch (ex) {
        // If no language name is found that is ok, keep the technical term
      }

      let label = cal.l10n.getCalString("extractUsing", [langName]);
      if (localeParts[3] != "") {
        label = cal.l10n.getCalString("extractUsingRegion", [langName, localeParts[3]]);
      }

      langs.push([label, localeParts[1]]);
    }

    // sort
    let pref = "calendar.patterns.last.used.languages";
    let lastUsedLangs = Services.prefs.getStringPref(pref, "");

    langs.sort((a, b) => {
      let idx_a = lastUsedLangs.indexOf(a[1]);
      let idx_b = lastUsedLangs.indexOf(b[1]);

      if (idx_a == -1 && idx_b == -1) {
        return a[0].localeCompare(b[0]);
      } else if (idx_a != -1 && idx_b != -1) {
        return idx_a - idx_b;
      } else if (idx_a == -1) {
        return 1;
      }
      return -1;
    });
    while (localeList.lastChild) {
      localeList.lastChild.remove();
    }

    for (let lang of langs) {
      addMenuItem(localeList, lang[0], lang[1], null);
    }
  },

  extractWithLocale(event, isEvent) {
    event.stopPropagation();
    let locale = event.target.value;
    this.extractFromEmail(null, isEvent, true, locale);
  },

  async extractFromEmail(message, isEvent, fixedLang, fixedLocale) {
    let folder = message.folder;
    let title = message.mime2DecodedSubject;

    let content = "";
    await new Promise((resolve, reject) => {
      let listener = {
        QueryInterface: ChromeUtils.generateQI(["nsIStreamListener"]),
        onDataAvailable(request, inputStream, offset, count) {
          let text = folder.getMsgTextFromStream(
            inputStream,
            message.charset,
            count, // bytesToRead
            32768, // maxOutputLen
            false, // compressQuotes
            true, // stripHTMLTags
            {} // out contentType
          );
          // If we ever got text, we're good. Ignore further chunks.
          content ||= text;
        },
        onStartRequest(request) {},
        onStopRequest(request, statusCode) {
          if (!Components.isSuccessCode(statusCode)) {
            reject(new Error(statusCode));
          }
          resolve();
        },
      };
      let uri = message.folder.getUriForMsg(message);
      MailServices.messageServiceFromURI(uri).streamMessage(uri, listener, null, null, false, "");
    });

    cal.LOG("[calExtract] Original email content: \n" + title + "\r\n" + content);
    let date = new Date(message.date / 1000);
    let time = new Date().getTime();

    let item = isEvent ? new CalEvent() : new CalTodo();
    item.title = message.mime2DecodedSubject;
    item.calendar = getSelectedCalendar();
    item.setProperty("DESCRIPTION", content);
    item.setProperty("URL", `mid:${message.messageId}`);
    cal.dtz.setDefaultStartEndHour(item);
    cal.alarms.setDefaultValues(item);
    let tabmail = document.getElementById("tabmail");
    let messagePaneBrowser =
      tabmail?.currentTabInfo.chromeBrowser.contentWindow.visibleMessagePaneBrowser?.() ||
      tabmail?.currentAboutMessage?.getMessagePaneBrowser() ||
      document.getElementById("messageBrowser")?.contentWindow?.getMessagePaneBrowser();
    let sel = messagePaneBrowser?.contentWindow?.getSelection();
    // Check if there's an iframe with a selection (e.g. Thunderbird Conversations)
    if (sel && sel.type !== "Range") {
      try {
        sel = messagePaneBrowser?.contentDocument
          .querySelector("iframe")
          .contentDocument.getSelection();
      } catch (ex) {
        // If Thunderbird Conversations is not installed that is fine,
        // we will just have an empty or null selection.
      }
    }

    let guessed;
    let endGuess;
    let extractor;
    let collected = [];
    let useService = Services.prefs.getBoolPref("calendar.extract.service.enabled");
    if (useService) {
      let result = extractService.extract(content, { now: date });
      if (!result) {
        useService = false;
      } else {
        guessed = result.startTime;
        endGuess = result.endTime;
      }
    }

    if (!useService) {
      let locale = Services.locale.requestedLocale;
      let dayStart = Services.prefs.getIntPref("calendar.view.daystarthour", 6);
      if (fixedLang) {
        extractor = new Extractor(fixedLocale, dayStart);
      } else {
        extractor = new Extractor(locale, dayStart, false);
      }
      collected = extractor.extract(title, content, date, sel);
    }

    // if we only have email date then use default start and end
    if (!useService && collected.length <= 1) {
      cal.LOG("[calExtract] Date and time information was not found in email/selection.");
      createEventWithDialog(null, null, null, null, item);
    } else {
      if (!useService) {
        guessed = extractor.guessStart(!isEvent);
        endGuess = extractor.guessEnd(guessed, !isEvent);
      }
      let allDay = (guessed.hour == null || guessed.minute == null) && isEvent;

      if (isEvent) {
        if (guessed.year != null) {
          item.startDate.year = guessed.year;
        }
        if (guessed.month != null) {
          item.startDate.month = guessed.month - 1;
        }
        if (guessed.day != null) {
          item.startDate.day = guessed.day;
        }
        if (guessed.hour != null) {
          item.startDate.hour = guessed.hour;
        }
        if (guessed.minute != null) {
          item.startDate.minute = guessed.minute;
        }

        item.endDate = item.startDate.clone();
        item.endDate.minute += Services.prefs.getIntPref("calendar.event.defaultlength", 60);

        if (endGuess.year != null) {
          item.endDate.year = endGuess.year;
        }
        if (endGuess.month != null) {
          item.endDate.month = endGuess.month - 1;
        }
        if (endGuess.day != null) {
          item.endDate.day = endGuess.day;
          if (allDay) {
            item.endDate.day++;
          }
        }
        if (endGuess.hour != null) {
          item.endDate.hour = endGuess.hour;
        }
        if (endGuess.minute != null) {
          item.endDate.minute = endGuess.minute;
        }
      } else {
        let dtz = cal.dtz.defaultTimezone;
        let dueDate = new Date();
        // set default
        dueDate.setHours(0);
        dueDate.setMinutes(0);
        dueDate.setSeconds(0);

        if (endGuess.year != null) {
          dueDate.setYear(endGuess.year);
        }
        if (endGuess.month != null) {
          dueDate.setMonth(endGuess.month - 1);
        }
        if (endGuess.day != null) {
          dueDate.setDate(endGuess.day);
        }
        if (endGuess.hour != null) {
          dueDate.setHours(endGuess.hour);
        }
        if (endGuess.minute != null) {
          dueDate.setMinutes(endGuess.minute);
        }

        cal.item.setItemProperty(item, "entryDate", cal.dtz.jsDateToDateTime(date, dtz));
        if (endGuess.year != null) {
          cal.item.setItemProperty(item, "dueDate", cal.dtz.jsDateToDateTime(dueDate, dtz));
        }
      }

      // if time not guessed set allday for events
      if (allDay) {
        createEventWithDialog(null, null, null, null, item, true);
      } else {
        createEventWithDialog(null, null, null, null, item);
      }
    }

    let timeSpent = new Date().getTime() - time;
    cal.LOG(
      "[calExtract] Total time spent for conversion (including loading of dictionaries): " +
        timeSpent +
        "ms"
    );
  },
};
