/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/PluralForm.jsm");

// Returns the load context for the current window
function getLoadContext() {
  return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
               .getInterface(Components.interfaces.nsIWebNavigation)
               .QueryInterface(Components.interfaces.nsILoadContext);
}

var abFlavorDataProvider = {
  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIFlavorDataProvider]),

  getFlavorData: function(aTransferable, aFlavor, aData, aDataLen)
  {
    if (aFlavor == "application/x-moz-file-promise")
    {
      var primitive = {};
      aTransferable.getTransferData("text/vcard", primitive, {});
      var vCard = primitive.value.QueryInterface(Components.interfaces.nsISupportsString).data;
      aTransferable.getTransferData("application/x-moz-file-promise-dest-filename", primitive, {});
      var leafName = primitive.value.QueryInterface(Components.interfaces.nsISupportsString).data;
      aTransferable.getTransferData("application/x-moz-file-promise-dir", primitive, {});
      var localFile = primitive.value.QueryInterface(Components.interfaces.nsIFile).clone();
      localFile.append(leafName);

      var ofStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
      ofStream.init(localFile, -1, -1, 0);
      var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
      converter.init(ofStream, null, 0, 0);
      converter.writeString(vCard);
      converter.close();

      aData.value = localFile;
    }
  }
};

var abResultsPaneObserver = {
  onDragStart: function (aEvent, aXferData, aDragAction)
    {
      var selectedRows = GetSelectedRows();

      if (!selectedRows)
        return;

      var selectedAddresses = GetSelectedAddresses();

      aXferData.data = new TransferData();
      aXferData.data.addDataForFlavour("moz/abcard", selectedRows);
      aXferData.data.addDataForFlavour("text/x-moz-address", selectedAddresses);
      aXferData.data.addDataForFlavour("text/unicode", selectedAddresses);

      let srcDirectory = getSelectedDirectory();
      // The default allowable actions are copy, move and link, so we need
      // to restrict them here.
      if (!srcDirectory.readOnly)
        // Only allow copy & move from read-write directories.
        aDragAction.action = Components.interfaces.
                             nsIDragService.DRAGDROP_ACTION_COPY |
                             Components.interfaces.
                             nsIDragService.DRAGDROP_ACTION_MOVE;
      else
        // Only allow copy from read-only directories.
        aDragAction.action = Components.interfaces.
                             nsIDragService.DRAGDROP_ACTION_COPY;

      var card = GetSelectedCard();
      if (card && card.displayName) {
        let vCard = card.translateTo("vcard");
        aXferData.data.addDataForFlavour("text/vcard", decodeURIComponent(vCard));
        aXferData.data.addDataForFlavour("application/x-moz-file-promise-dest-filename", card.displayName + ".vcf");
        aXferData.data.addDataForFlavour("application/x-moz-file-promise-url", "data:text/vcard," + vCard);
        aXferData.data.addDataForFlavour("application/x-moz-file-promise", abFlavorDataProvider);
      }
    },

  onDrop: function (aEvent, aXferData, aDragSession)
    {
    },

  onDragExit: function (aEvent, aDragSession)
    {
    },

  onDragOver: function (aEvent, aFlavour, aDragSession)
    {
    },

  getSupportedFlavours: function ()
    {
      return null;
    }
};


var dragService = Components.classes["@mozilla.org/widget/dragservice;1"]
                            .getService(Components.interfaces.nsIDragService);

var abDirTreeObserver = {
  /**
   * canDrop - determine if the tree will accept the dropping of a item
   * onto it.
   *
   * Note 1: We don't allow duplicate mailing list names, therefore copy
   * is not allowed for mailing lists.
   * Note 2: Mailing lists currently really need a card in the parent
   * address book, therefore only moving to an address book is allowed.
   *
   * The possibilities:
   *
   *   anything          -> same place             = Not allowed
   *   anything          -> read only directory    = Not allowed
   *   mailing list      -> mailing list           = Not allowed
   *   (we currently do not support recursive lists)
   *   address book card -> different address book = MOVE or COPY
   *   address book card -> mailing list           = COPY only
   *   (cards currently have to exist outside list for list to work correctly)
   *   mailing list      -> different address book = MOVE only
   *   (lists currently need to have unique names)
   *   card in mailing list -> parent mailing list = Not allowed
   *   card in mailing list -> other mailing list  = MOVE or COPY
   *   card in mailing list -> other address book  = MOVE or COPY
   *   read only directory item -> anywhere        = COPY only
   */
  canDrop: function(index, orientation, dataTransfer)
  {
    if (orientation != Components.interfaces.nsITreeView.DROP_ON)
      return false;
    if (!dataTransfer.types.includes("moz/abcard")) {
      return false;
    }

    let targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;

    let srcURI = getSelectedDirectoryURI();

    // We cannot allow copy/move to "All Address Books".
    if (targetURI == kAllDirectoryRoot + "?")
      return false;

    // The same place case
    if (targetURI == srcURI)
      return false;

    // determine if we dragging from a mailing list on a directory x to the parent (directory x).
    // if so, don't allow the drop
    if (srcURI.startsWith(targetURI))
      return false;

    // check if we can write to the target directory
    // e.g. LDAP is readonly currently
    var targetDirectory = GetDirectoryFromURI(targetURI);

    if (targetDirectory.readOnly)
      return false;

    var dragSession = dragService.getCurrentSession();
    if (!dragSession)
      return false;

    // XXX Due to bug 373125/bug 349044 we can't specify a default action,
    // so we default to move and this means that the user would have to press
    // ctrl to copy which most users don't realise.
    //
    // If target directory is a mailing list, then only allow copies.
    //    if (targetDirectory.isMailList &&
    //   dragSession.dragAction != Components.interfaces.
    //                             nsIDragService.DRAGDROP_ACTION_COPY)
    //return false;

    var srcDirectory = GetDirectoryFromURI(srcURI);

    // Only allow copy from read-only directories.
    if (srcDirectory.readOnly &&
        dragSession.dragAction != Components.interfaces.
                                  nsIDragService.DRAGDROP_ACTION_COPY)
      return false;

    // Go through the cards checking to see if one of them is a mailing list
    // (if we are attempting a copy) - we can't copy mailing lists as
    // that would give us duplicate names which isn't allowed at the
    // moment.
    var draggingMailList = false;

    // The data contains the a string of "selected rows", eg.: "1,2".
    var rows = dataTransfer.getData("moz/abcard").split(",").map(j => parseInt(j, 10));

    for (var j = 0; j < rows.length; j++)
    {
      if (gAbView.getCardFromRow(rows[j]).isMailList)
      {
        draggingMailList = true;
        break;
      }
    }

    // The rest of the cases - allow cards for copy or move, but only allow
    // move of mailing lists if we're not going into another mailing list.
    if (draggingMailList &&
        (targetDirectory.isMailList ||
         dragSession.dragAction == Components.interfaces.
                                   nsIDragService.DRAGDROP_ACTION_COPY))
    {
      return false;
    }

    dragSession.canDrop = true;
    return true;
  },

  /**
   * onDrop - we don't need to check again for correctness as the
   * tree view calls canDrop just before calling onDrop.
   *
   */
  onDrop: function(index, orientation, dataTransfer)
  {
    var dragSession = dragService.getCurrentSession();
    if (!dragSession)
      return;
    if (!dataTransfer.types.includes("moz/abcard")) {
      return;
    }

    let targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
    let srcURI = getSelectedDirectoryURI();

    // The data contains the a string of "selected rows", eg.: "1,2".
    var rows = dataTransfer.getData("moz/abcard").split(",").map(j => parseInt(j, 10));
    var numrows = rows.length;

    var result;
    // needToCopyCard is used for whether or not we should be creating
    // copies of the cards in a mailing list in a different address book
    // - it's not for if we are moving or not.
    var needToCopyCard = true;
    if (srcURI.length > targetURI.length) {
      result = srcURI.split(targetURI);
      if (result[0] != srcURI) {
        // src directory is a mailing list on target directory, no need to copy card
        needToCopyCard = false;
      }
    }
    else {
      result = targetURI.split(srcURI);
      if (result[0] != targetURI) {
        // target directory is a mailing list on src directory, no need to copy card
        needToCopyCard = false;
      }
    }

    // if we still think we have to copy the card,
    // check if srcURI and targetURI are mailing lists on same directory
    // if so, we don't have to copy the card
    if (needToCopyCard) {
      var targetParentURI = GetParentDirectoryFromMailingListURI(targetURI);
      if (targetParentURI && (targetParentURI == GetParentDirectoryFromMailingListURI(srcURI)))
        needToCopyCard = false;
    }

    var directory = GetDirectoryFromURI(targetURI);

    // Only move if we are not transferring to a mail list
    var actionIsMoving = (dragSession.dragAction & dragSession.DRAGDROP_ACTION_MOVE) && !directory.isMailList;

    let cardsToCopy = [];
    for (let j = 0; j < numrows; j++) {
      cardsToCopy.push(gAbView.getCardFromRow(rows[j]));
    }
    for (let card of cardsToCopy) {
      if (card.isMailList) {
        // This check ensures we haven't slipped through by mistake
        if (needToCopyCard && actionIsMoving) {
          directory.addMailList(GetDirectoryFromURI(card.mailListURI));
        }
      } else {
        let srcDirectory = null;
        if (srcURI == (kAllDirectoryRoot + "?") && actionIsMoving) {
          let dirId = card.directoryId.substring(0, card.directoryId.indexOf("&"));
          srcDirectory = MailServices.ab.getDirectoryFromId(dirId);
        }

        directory.dropCard(card, needToCopyCard);

        // This is true only if srcURI is "All ABs" and action is moving.
        if (srcDirectory) {
          let cardArray =
            Components.classes["@mozilla.org/array;1"]
                      .createInstance(Components.interfaces.nsIMutableArray);
          cardArray.appendElement(card, false);
          srcDirectory.deleteCards(cardArray);
        }
      }
    }

    var cardsTransferredText;

    // If we are moving, but not moving to a directory, then delete the
    // selected cards and display the appropriate text
    if (actionIsMoving && srcURI != (kAllDirectoryRoot + "?")) {
      // If we have moved the cards, then delete them as well.
      gAbView.deleteSelectedCards();
    }

    if (actionIsMoving) {
      cardsTransferredText = PluralForm.get(numrows,
        gAddressBookBundle.getFormattedString("contactsMoved", [numrows]));
    } else {
      cardsTransferredText = PluralForm.get(numrows,
        gAddressBookBundle.getFormattedString("contactsCopied", [numrows]));
    }

    if (srcURI == kAllDirectoryRoot + "?") {
      SetAbView(srcURI);
    }

    document.getElementById("statusText").label = cardsTransferredText;
  },

  onToggleOpenState: function()
  {
  },

  onCycleHeader: function(colID, elt)
  {
  },
      
  onCycleCell: function(row, colID)
  {
  },
      
  onSelectionChanged: function()
  {
  },

  onPerformAction: function(action)
  {
  },

  onPerformActionOnRow: function(action, row)
  {
  },

  onPerformActionOnCell: function(action, row, colID)
  {
  }
}

function DragAddressOverTargetControl(event)
{
  var dragSession = gDragService.getCurrentSession();

  if (!dragSession.isDataFlavorSupported("text/x-moz-address"))
     return;

  var trans = Components.classes["@mozilla.org/widget/transferable;1"]
                        .createInstance(Components.interfaces.nsITransferable);
  trans.init(getLoadContext());
  trans.addDataFlavor("text/x-moz-address");

  var canDrop = true;

  for ( var i = 0; i < dragSession.numDropItems; ++i )
  {
    dragSession.getData ( trans, i );
    var dataObj = new Object();
    var bestFlavor = new Object();
    var len = new Object();
    try
    {
      trans.getAnyTransferData ( bestFlavor, dataObj, len );
    }
    catch (ex)
    {
      canDrop = false;
      break;
    }
  }
  dragSession.canDrop = canDrop;
}

function DropAddressOverTargetControl(event)
{
  var dragSession = gDragService.getCurrentSession();

  var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  trans.addDataFlavor("text/x-moz-address");

  for ( var i = 0; i < dragSession.numDropItems; ++i )
  {
    dragSession.getData ( trans, i );
    var dataObj = new Object();
    var bestFlavor = new Object();
    var len = new Object();

    // Ensure we catch any empty data that may have slipped through
    try
    {
      trans.getAnyTransferData ( bestFlavor, dataObj, len);
    }
    catch (ex)
    {
      continue;
    }

    if ( dataObj )
      dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
    if ( !dataObj )
      continue;

    // pull the address out of the data object
    var address = dataObj.data.substring(0, len.value);
    if (!address)
      continue;

    DropRecipient(address);
  }
}