/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Ben Bucksch <ben.bucksch beonex.com>
 * Portions created by the Initial Developer are Copyright (C) 2008-2009
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   David Bienvenu <bienvenu@davidbienvenu.org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/**
 * Takes an |AccountConfig| JS object and creates that account in the
 * Thunderbird backend (which also writes it to prefs).
 *
 * @param config {AccountConfig} The account to create
 *
 * @return - the account created.
 */
function createAccountInBackend(config)
{
  var accountManager = Cc["@mozilla.org/messenger/account-manager;1"]
                       .getService(Ci.nsIMsgAccountManager);
  var smtpManager = Cc["@mozilla.org/messengercompose/smtp;1"]
                    .getService(Ci.nsISmtpService);

  // incoming server
  var inServer = accountManager.createIncomingServer(
      config.incoming.username,
      config.incoming.hostname,
      sanitize.enum(config.incoming.type, ["pop3", "imap", "nntp"]));
  inServer.port = config.incoming.port;
  inServer.authMethod = config.incoming.auth;
  inServer.password = config.incoming.password;
  if (config.rememberPassword && config.incoming.password.length)
    rememberPassword(inServer, config.incoming.password);

  // SSL
  if (config.incoming.socketType == 1) // plain
    inServer.socketType = Ci.nsMsgSocketType.plain;
  else if (config.incoming.socketType == 2) // SSL / TLS
    inServer.socketType = Ci.nsMsgSocketType.SSL;
  else if (config.incoming.socketType == 3) // STARTTLS
    inServer.socketType = Ci.nsMsgSocketType.alwaysSTARTTLS;
  //inServer.prettyName = config.displayName;
  inServer.prettyName = config.identity.emailAddress;

  inServer.doBiff = true;
  inServer.biffMinutes = config.incoming.checkInterval;
  let prefs = Cc["@mozilla.org/preferences-service;1"]
              .getService(Ci.nsIPrefBranch);
  const loginAtStartupPrefTemplate =
    "mail.server.%serverkey%.login_at_startup";
  var loginAtStartupPref =
    loginAtStartupPrefTemplate.replace("%serverkey%", inServer.key);
  prefs.setBoolPref(loginAtStartupPref,
                    config.incoming.loginAtStartup);
  if (config.incoming.type == "pop3")
  {
    const leaveOnServerPrefTemplate =
      "mail.server.%serverkey%.leave_on_server";
    const daysToLeaveOnServerPrefTemplate =
      "mail.server.%serverkey%.num_days_to_leave_on_server";
    const deleteFromServerPrefTemplate =
      "mail.server.%serverkey%.delete_mail_left_on_server";
    const deleteByAgeFromServerPrefTemplate =
      "mail.server.%serverkey%.delete_by_age_from_server";
    const downloadOnBiffPrefTemplate =
      "mail.server.%serverkey%.download_on_biff";
    var leaveOnServerPref =
      leaveOnServerPrefTemplate.replace("%serverkey%", inServer.key);
    var ageFromServerPref =
      deleteByAgeFromServerPrefTemplate.replace("%serverkey%", inServer.key);
    var daysToLeaveOnServerPref =
      daysToLeaveOnServerPrefTemplate.replace("%serverkey%", inServer.key);
    var deleteFromServerPref =
      deleteFromServerPrefTemplate.replace("%serverkey%", inServer.key);
    let downloadOnBiffPref =
      downloadOnBiffPrefTemplate.replace("%serverkey%", inServer.key);
    prefs.setBoolPref(leaveOnServerPref,
                      config.incoming.leaveMessagesOnServer);
    prefs.setIntPref(daysToLeaveOnServerPref,
                     config.incoming.daysToLeaveMessagesOnServer);
    prefs.setBoolPref(deleteFromServerPref,
                      config.incoming.deleteOnServerWhenLocalDelete);
    prefs.setBoolPref(ageFromServerPref,
                      config.incoming.deleteByAgeFromServer);
    prefs.setBoolPref(downloadOnBiffPref,
                      config.incoming.downloadOnBiff);
  }
  inServer.valid = true;

  let username = config.outgoing.auth > 1 ? config.outgoing.username : null;
  let outServer = smtpManager.findServer(username, config.outgoing.hostname);
  assert(config.outgoing.addThisServer ||
         config.outgoing.useGlobalPreferredServer ||
         config.outgoing.existingServerKey,
         "No SMTP server: inconsistent flags");

  if (config.outgoing.addThisServer && !outServer)
  {
    outServer = smtpManager.createSmtpServer();
    outServer.hostname = config.outgoing.hostname;
    outServer.port = config.outgoing.port;
    outServer.authMethod = config.outgoing.auth;
    if (config.outgoing.auth > 1)
    {
      outServer.username = username;
      outServer.password = config.incoming.password;
      if (config.rememberPassword && config.incoming.password.length)
        rememberPassword(outServer, config.incoming.password);
    }

    if (config.outgoing.socketType == 1) // no SSL
      outServer.socketType = Ci.nsMsgSocketType.plain;
    else if (config.outgoing.socketType == 2) // SSL / TLS
      outServer.socketType = Ci.nsMsgSocketType.SSL;
    else if (config.outgoing.socketType == 3) // STARTTLS
      outServer.socketType = Ci.nsMsgSocketType.alwaysSTARTTLS;

    // API problem: <http://mxr.mozilla.org/seamonkey/source/mailnews/compose/public/nsISmtpServer.idl#93>
    outServer.description = config.displayName;
    if (config.password)
      outServer.password = config.outgoing.password;

    // If this is the first SMTP server, set it as default
    if (!smtpManager.defaultServer ||
        !smtpManager.defaultServer.hostname)
      smtpManager.defaultServer = outServer;
  }

  // identity
  // TODO accounts without identity?
  var identity = accountManager.createIdentity();
  identity.fullName = config.identity.realname;
  identity.email = config.identity.emailAddress;
  if (config.incoming.type == "nntp")
    identity.composeHtml = false;

  identity.valid = true;

  if (config.outgoing.existingServerKey)
    identity.smtpServerKey = config.outgoing.existingServerKey;
  else if (!config.outgoing.useGlobalPreferredServer)
    identity.smtpServerKey = outServer.key;

  // account and hook up
  // Note: Setting incomingServer will cause the AccountManager to refresh
  // itself, which could be a problem if we came from it and we haven't set
  // the identity (see bug 521955), so make sure everything else on the
  // account is set up before you set the incomingServer.
  var account = accountManager.createAccount();
  account.addIdentity(identity);
  account.incomingServer = inServer;
  if (!accountManager.defaultAccount)
    accountManager.defaultAccount = account;

  verifyLocalFoldersAccount(accountManager);
  setFolders(identity, inServer);

  // save
  accountManager.saveAccountInfo();
  try {
    Cc["@mozilla.org/preferences-service;1"]
    .getService(Ci.nsIPrefService)
    .savePrefFile(null);
  } catch (ex) {
    ddump("Could not write out prefs: " + ex);
  }
  return account;
}

function setFolders(identity, server)
{
  // TODO: support for local folders for global inbox (or use smart search
  // folder instead)

  var baseURI = server.serverURI + "/";

  // Names will be localized in UI, not in folder names on server/disk
  // TODO allow to override these names in the XML config file,
  // in case e.g. Google or AOL use different names?
  // Workaround: Let user fix it :)
  var fccName = "Sent";
  var draftName = "Drafts";
  var templatesName = "Templates";

  identity.draftFolder = baseURI + draftName;
  identity.stationeryFolder = baseURI + templatesName;
  identity.fccFolder = baseURI + fccName;

  identity.fccFolderPickerMode = 0;
  identity.draftsFolderPickerMode = 0;
  identity.tmplFolderPickerMode = 0;
}

function rememberPassword(server, password)
{
  if (server instanceof Components.interfaces.nsIMsgIncomingServer)
    var passwordURI = server.localStoreType + "://" + server.hostName;
  else if (server instanceof Components.interfaces.nsISmtpServer)
    var passwordURI = "smtp://" + server.hostname;
  else
    throw new NotReached("Server type not supported");

  let lm = Cc["@mozilla.org/login-manager;1"]
           .getService(Ci.nsILoginManager);
  let login = Cc["@mozilla.org/login-manager/loginInfo;1"]
              .createInstance(Ci.nsILoginInfo);
  login.init(passwordURI, null, passwordURI, server.username, password, "", "");
  try {
    lm.addLogin(login);
  } catch (e if e.message.indexOf("This login already exists") != -1) {
    // TODO modify
  }
}

/**
 * Check whether the user's setup already has an incoming server
 * which matches (hostname, port, username) the primary one
 * in the config.
 * (We also check the email address as username.)
 *
 * @param config {AccountConfig} filled in (no placeholders)
 * @return {nsIMsgIncomingServer} If it already exists, the server
 *     object is returned.
 *     If it's a new server, |null| is returned.
 */
function checkIncomingServerAlreadyExists(config)
{
  assert(config instanceof AccountConfig);
  var accountManager = Cc["@mozilla.org/messenger/account-manager;1"]
                       .getService(Ci.nsIMsgAccountManager);
  var incoming = config.incoming;
  var existing = accountManager.findRealServer(incoming.username,
        incoming.hostname,
        sanitize.enum(incoming.type, ["pop3", "imap", "nntp"]),
        incoming.port);

  // if username does not have an '@', also check the e-mail
  // address form of the name.
  if (!existing && incoming.username.indexOf("@") == -1)
    existing = accountManager.findRealServer(config.identity.emailAddress,
          incoming.hostname,
          sanitize.enum(incoming.type, ["pop3", "imap", "nntp"]),
          incoming.port);
  return existing;
};

/**
 * Check whether the user's setup already has an outgoing server
 * which matches (hostname, port, username) the primary one
 * in the config.
 *
 * @param config {AccountConfig} filled in (no placeholders)
 * @return {nsISmtpServer} If it already exists, the server
 *     object is returned.
 *     If it's a new server, |null| is returned.
 */
function checkOutgoingServerAlreadyExists(config)
{
  assert(config instanceof AccountConfig);
  var smtpManager = Cc["@mozilla.org/messengercompose/smtp;1"]
                    .getService(Ci.nsISmtpService);
  var smtpServers = smtpManager.smtpServers;
  while (smtpServers.hasMoreElements())
  {
    let existingServer = smtpServers.getNext()
        .QueryInterface(Ci.nsISmtpServer);
    // TODO check username with full email address, too, like for incoming
    if (existingServer.hostname == config.outgoing.hostname &&
        existingServer.port == config.outgoing.port &&
        existingServer.username == config.outgoing.username)
      return existingServer;
  }
  return null;
};

/**
 * Check if there already is a "Local Folders". If not, create it.
 * Copied from AccountWizard.js with minor updates.
 */
function verifyLocalFoldersAccount(am) 
{
  let localMailServer;
  try {
    localMailServer = am.localFoldersServer;
  }
  catch (ex) {
    localMailServer = null;
  }

  try {
    if (!localMailServer) 
    {
      // creates a copy of the identity you pass in
      am.createLocalMailAccount();
      try {
        localMailServer = am.localFoldersServer;
      }
      catch (ex) {
        ddump("Error! we should have found the local mail server " +
              "after we created it.");
      }
    }
  }
  catch (ex) { ddump("Error in verifyLocalFoldersAccount " + ex); }
}
