// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2009
// http://flock.com
//
// This file may be used under the terms of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// 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.
//
// END FLOCK GPL

const FAVORITES_ROOT = "urn:media:favorites";
const MEDIA_ROOT_STREAMS_ID = "media-folder-root";
const MEDIA_ROOT_SEPARATOR_ID = "media-folder-root-separator";
const MEDIA_FOLDER_SEPARATOR_ID = "media-folder-tree-separator";
const CHOOSE_MENUITEM_ID = "choose-custom-media-folder-tree";
const MEDIABAR_PROPERTIES = "chrome://flock/locale/photo/mediabar.properties";

var gCoop = Components.classes["@flock.com/singleton;1"]
            .getService(Components.interfaces.flockISingleton)
            .getSingleton("chrome://flock/content/common/load-faves-coop.js")
            .wrappedJSObject;

var gMediaQueryEditUI = {
  // Define getter for the entire panel
  get panel() {
    return document.getElementById("editMediaStreamPanel");
  },

  // Define getter for the tree component
  get tree() {
    return document.getElementById("streamFolderTree");
  },

  // Define getter for the menulist component
  get menulist() {
    return document.getElementById("streamMenuList");
  },

  /**
   * Return the proper folder, do not reference top:media
   */
  _getCurrentMediaQueryFolder: function mqeUI__getCurrentMediaQueryFolder() {
    return gCoop.get(gPhotoDrawer.mediaQueryId).getParent();
  },

  /**
   * Populate data and show the Media Stream panel popup for editing
   * @param aAnchorElement - Element to anchor the current popup panel to
   * @param aPosition - Position based on achor element to display the panel
   * @param isTreeCollapsed - Boolean for tree collapsed
   */
  _showPanel: function mqeUI___showPanel(aAnchorElement,
                                        aPosition,
                                        isTreeCollapsed)
  {
    // Populate tree with folder data
    this._initTreeView();

    // Explicitly set the current state of the tree
    this.treeExpanded = !this.tree.collapsed;

    // Initialize the folder <menulist>
    this._initFolderMenuList(FAVORITES_ROOT);

    // Reset state to a collapsed tree
    if (this.tree && !this.tree.collapsed && isTreeCollapsed) {
      this._toggleFolderTree(true);
    }

    // Select the proper menulist item with the current folder
    var currentFolder = this._getCurrentMediaQueryFolder();
    if (currentFolder) {
      this._updateFolderMenuList(currentFolder);
    }

    // Reset label of choose menu item
    var chooseMenuItem = document.getElementById(CHOOSE_MENUITEM_ID);
    if (chooseMenuItem) {
      var chooseText =
          flockGetString("photo/mediabar", "flock.media.edit.choose");
      chooseMenuItem.setAttribute("label", chooseText);
    }

    // Update Media Stream info
    this.currentCoopObj = gCoop.get(gPhotoDrawer.mediaQueryId);
    var streamName = document.getElementById("streamName");
    streamName.value = this.currentCoopObj.name;

    // Display popup
    this.panel.popupBoxObject
        .setConsumeRollupEvent(CI.nsIPopupBoxObject.ROLLUP_CONSUME);
    this.panel.openPopup(aAnchorElement, aPosition, -1, -1);
  },

  // Hide the Media Stream panel popup
  _hidePanel: function mqeUI__hidePanel() {
    this.panel.hidePopup();
  },

  _pushFoldersToOpen: function mqeUI__pushFoldersToOpen(aCoopObj) {
    var parentCoop = aCoopObj.getParent();
    if (parentCoop && parentCoop.id()) {
      this.foldersToOpen.push(parentCoop.id());
      this._pushFoldersToOpen(parentCoop);
    }
  },

  _hasSubFolders: function mqeUI__hasSubFolders(aCoopObj) {
    // Determine whether or not the folder has sub folders
    var subEnum = aCoopObj.children.enumerate();
    while (subEnum.hasMoreElements()) {
      var subCoopObj = subEnum.getNext();
      if (subCoopObj.isA(gCoop.Folder)) {
        return true;
      }
    }
    return false;
  },

  _populateTreeRecursively:
  function mqeUI__populateTreeRecursively(aCoopObj, aParent) {
    var childEnum = aCoopObj.children.enumerate();
    while (childEnum.hasMoreElements()) {
      var coopObj = childEnum.getNext();
      var coopId = coopObj.id();
      if (coopObj.isA(gCoop.Folder)) {
        var hasSubFolders = this._hasSubFolders(coopObj);
        var node = this._addRowTo(coopObj, aParent, hasSubFolders);
        if (hasSubFolders) {
          var children = document.createElement("treechildren");
          node.appendChild(children);
          this._populateTreeRecursively(coopObj, children);
        }
      }
    }
  },

  /**
   * Adds a new treeitem to a parent node
   * @param aCoopObj - Coop object of the folder
   * @param aParent - A parent node to append the new childnode onto
   * @param hasChildren - If the current row contains sub folders
   */
  _addRowTo: function mqeUI__addRowTo(aCoopObj, aParent, hasChildren) {
    var cell = document.createElement("treecell");
    var row = document.createElement("treerow");
    var treeitem = document.createElement("treeitem");
    if (hasChildren) {
      // Display a twisty to toggle open/closed
      treeitem.setAttribute("container", "true");

      // Open folder if the currently selected stream is a subfolder
      for each (var i in this.foldersToOpen) {
        if (aCoopObj.id() == i) {
          treeitem.setAttribute("open", "true");
        }
      }
    }
    cell.setAttribute("label", aCoopObj.name);
    cell.setAttribute("id", aCoopObj.id());

    // Use container property for CSS folder to render correctly
    cell.setAttribute("properties", "container");
    cell.setAttribute("editable", "true");
    treeitem.appendChild(row);
    row.appendChild(cell);
    aParent.appendChild(treeitem);
    return treeitem;
  },

  /**
   * Initialize the tree folders
   */
  _initTreeView: function mqeUI__initTreeView() {
    var treeChildren = document.getElementById("streamFolderTreeChildren");
    while (treeChildren.childNodes.length > 0) {
      treeChildren.removeChild(treeChildren.lastChild);
    }
    // Array of parent folders of the currently selected media stream
    // This will open the folder state when creating the DOM node
    this.foldersToOpen = [];
    this.foldersToOpen.push(FAVORITES_ROOT);
    this._pushFoldersToOpen(this._getCurrentMediaQueryFolder());

    // Root container for all folders
    var rootObj = {
      name: flockGetString("photo/mediabar", "flock.media.edit.folders.root"),
      id: function rootObj_id() {
        return FAVORITES_ROOT;
      }
    };
    var rootNode = this._addRowTo(rootObj, treeChildren, true);
    rootNode.firstChild.firstChild.setAttribute("editable", "false");
    var children = document.createElement("treechildren");
    children.setAttribute("id", "streamChildrenRoot");
    rootNode.appendChild(children);

    this._populateTreeRecursively(gCoop.get(FAVORITES_ROOT), children);
  },

  /**
   * Return CoopObj of the currently selected tree node.
   * @param aRow - Row index of tree
   */
  _getTreeRowResource: function mqeUI__getTreeRowResource(aRow) {
    try {
      var selectedNode = this.tree.contentView.getItemAtIndex(aRow);
      if (selectedNode) {
        return gCoop.get(selectedNode.firstChild.firstChild.id);
      } else {
        return gCoop.get(FAVORITES_ROOT);
      }
    } catch (ex) {
      // No coop object found
    }
  },

  _getTreeParentResource: function mqeUI__getTreeParentResource(aRow) {
    if (aRow != -1) {
      var parentIndex = this.tree.treeBoxObject.view.getParentIndex(aRow);
      return this._getTreeRowResource(parentIndex);
    }
    // Assume its parent is the root
    return null;
  },

  _ensureTreeHasSelection: function mqeUI__ensureTreeHasSelection() {
    if (this.tree &&
        this.tree.treeBoxObject &&
        this.tree.treeBoxObject.view &&
        this.tree.treeBoxObject.view.selection)
    {
      var rangeCount = this.tree.treeBoxObject.view.selection.getRangeCount();
      if (rangeCount) {
        return true;
      }
      return false;
    }
  },

  _checkForRootItem: function mqeUI__checkForRootItem() {
    // The check to see if the tree is selected
    var isTreeSelected = this._ensureTreeHasSelection();
    // If no tree selection and the root menuitem is selected return tree root
    var rootItem = document.getElementById(MEDIA_ROOT_STREAMS_ID);
    if ((this.menulist.selectedItem == rootItem) && !isTreeSelected) {
      return gCoop.get(FAVORITES_ROOT);
    } else {
      return this._getTreeRowResource(this.tree.currentIndex);
    }
  },

  /**
   * Update the selected menuitem based on the tree node currently selected
   * @param aEvent - Event from onselect
   */
  _onTreeSelect: function mqeUI__onTreeSelect(aEvent) {
    // Get the CoopObj of the currently selected tree node
    var coopObj = this._checkForRootItem();
    this._updateFolderMenuList(coopObj);
  },

  /**
   * Set the current index of the focused item
   */
  _onTreeFocus: function mqeUI__onTreeFocus(aEvent) {
    this.focusedItem = this.tree.currentIndex;
  },

  /**
   * Save the current label of the folder
   */
  _onTreeBlur: function mqeUI__onTreeBlur(aEvent) {
    try {
      var treeitem = this.tree.contentView.getItemAtIndex(this.focusedItem);
      if (treeitem) {
        var treecell = treeitem.firstChild.firstChild;
        var folderCoopObj = gCoop.get(treecell.getAttribute("id"));
        folderCoopObj.name = treecell.getAttribute("label");
        this.dontAllowTreeSelect = true;
        this._initFolderMenuList(FAVORITES_ROOT);
        this._updateFolderMenuList(folderCoopObj);
      }
    } catch (ex) {
      // Our coop object wasn't found.
    }
  },

  /**
   * Update the currently selected node of the tree based on a CoopId
   * @param aCoopId - Current Folder's CoopId
   * @param aTree - a Tree component
   */
  _updateTreeSelectedItem: function mqeUI__updateTreeSelectedItem(aCoopId,
                                                                 aTree)
  {
    // Return if attempting to focus on the current element while editing
    if (this.tree.getAttribute("editing")) {
      return;
    }

    var itemFound = false;
    if (aTree.view && aTree.view.rowCount) {
      for (var i = 0; i < aTree.view.rowCount; i++) {
        var currentItem = this._getTreeRowResource(i);
        if (currentItem.id() == aCoopId) {
          // Select current folder
          aTree.view.selection.select(i);
          aTree.focus();
          itemFound = true;
          break;
        }
      }
    }
    if (!itemFound && aTree.view) {
      // Clear tree selection if no folder has been found
      aTree.view.selection.clearSelection();
    }
  },

  /**
   * Helper function to expand all nodes within the tree
   */
  _expandTreeNodes: function mqeUI__expandTreeNodes() {
    if (this.tree && this.tree.treeBoxObject) {
      var treeView = this.tree.treeBoxObject.view;
      for (var i = 0; i < treeView.rowCount; i++) {
        if (treeView.isContainer(i) && !treeView.isContainerOpen(i)) {
          treeView.toggleOpenState(i);
        }
      }
      this.treeExpanded = true;
    }
   },

  /**
   * Toggle the state of the tree
   * @param aIsHidden - Override variable to hide the state of the tree & button
   */
  _toggleFolderTree: function mqeUI__toggleFolderTree(aIsHidden) {
    var toggleButton = document.getElementById("treeToggleButton");
    var addFolderButton = document.getElementById("addNewFolderButton");
    // Determine state of tree based on the current collapsed state
    if (this.tree.collapsed && !aIsHidden) {
      // Expanded tree nodes for visibility
      if (!this.treeExpanded) {
        this._expandTreeNodes();
      }

      // Select current tree folder node
      var currentFolder = this._getCurrentMediaQueryFolder();
      if (currentFolder) {
        this._updateTreeSelectedItem(currentFolder.id(), this.tree);
      }

      this.tree.collapsed = false;
      addFolderButton.setAttribute("hidden", "false");
      toggleButton.setAttribute("class", "expander-up");
      toggleButton.setAttribute("tooltiptext",
                                toggleButton.getAttribute("tooltiptextup"));
    } else {
      // Collapsed
      this.tree.collapsed = true;
      addFolderButton.setAttribute("hidden", "true");
      toggleButton.setAttribute("class", "expander-down");
      toggleButton.setAttribute("tooltiptext",
                                toggleButton.getAttribute("tooltiptextdown"));
    }
  },

  /**
   * Helper function for _initFolderMenuList
   * @param aName - Name of the menuitem to be appended
   * @param aId - Id of the menuitem to be appended
   * @param aClass - Class of the menuitem to be appended
   * @param aOnCommand - Execute this command when menuitem clicked
   * @param isSeparator - Define a separator rather than a menuitem
   */
  _appendFolderItemToMenupopup:
  function mqe_appendFolderItemToMenupopup(aName,
                                           aId,
                                           aClass,
                                           aOnCommand,
                                           isSeparator)
  {
    var menuitem;
    if (isSeparator) {
      menuitem = document.createElement("menuseparator");
    } else {
      menuitem = document.createElement("menuitem");
      menuitem.setAttribute("label", aName);
      menuitem.setAttribute("class", aClass);
    }
    if (aOnCommand) {
      menuitem.setAttribute("oncommand", aOnCommand);
    }
    if (aId) {
      menuitem.setAttribute("id", aId);
    }
    this.menulist.menupopup.appendChild(menuitem);
    return menuitem;
  },

  /**
   * Initialize the menulist with folders and alternative choices
   * @param aUrn - a URN of the root level of the datasource
   */
  _initFolderMenuList: function mqeUI__initFolderMenuList(aUrn) {
    // Clean up existing menulist
    var refNode = this.menulist.menupopup;
    while (refNode.childNodes.length > 0) {
      refNode.removeChild(refNode.lastChild);
    }

    var foldersExist = false;

    // Append all root level folders
    var mediaQueries = gCoop.get(aUrn);
    var childEnum = mediaQueries.children.enumerate();
    var folderClass = "menuitem-iconic folder-icon";
    while (childEnum.hasMoreElements()) {
      var coopObj = childEnum.getNext();
      if (coopObj.isA(gCoop.Folder)) {
        foldersExist = true;
        this._appendFolderItemToMenupopup(coopObj.name,
                                          coopObj.id(),
                                          folderClass,
                                          null,
                                          false);
      }
    }
    if (foldersExist) {
      // Append Separator
      this._appendFolderItemToMenupopup(null,
                                        MEDIA_FOLDER_SEPARATOR_ID,
                                        null,
                                        null,
                                        true);
    }
    // Append Media Stream Favorites menuitem
    var rootText = flockGetString("photo/mediabar",
                                     "flock.media.edit.folders.root");
    this._appendFolderItemToMenupopup(rootText,
                                      MEDIA_ROOT_STREAMS_ID,
                                      folderClass,
                                      null,
                                      false);
    // Append Separator
    this._appendFolderItemToMenupopup(null,
                                      MEDIA_ROOT_SEPARATOR_ID,
                                      null,
                                      null,
                                      true);
    // Append Choose Selector
    var chooseText = flockGetString("photo/mediabar", "flock.media.edit.choose");
    this._appendFolderItemToMenupopup(chooseText,
                                      CHOOSE_MENUITEM_ID,
                                      folderClass,
                                     "gMediaQueryEditUI._toggleFolderTree(false)",
                                      false);
   },

  /**
   * Update the selected tree node based on the currently selected menuitem
   * @param aEvent - Event from onselect
   */
  _onFolderMenuListSelect: function mqeUI__onFolderMenuListSelect(aEvent) {
    var folderObj = this.menulist.selectedItem;
    if (!this.dontAllowTreeSelect) {
      this._updateTreeSelectedItem(folderObj.id, this.tree);
    }
    this.dontAllowTreeSelect = false;
  },

  /**
   * Update the currently selected menuitem based on the current folder
   * @param aFolderCoopObj - Folder CoopObj of the current media query
   */
  _updateFolderMenuList: function mqeUI__updateFolderMenuList(aFolderCoopObj) {
    var itemFound = false;
    this.menulist.menupopup.lastChild.setAttribute("id", CHOOSE_MENUITEM_ID);
    var customItem = document.getElementById(CHOOSE_MENUITEM_ID);
    var menuNodes = this.menulist.menupopup.childNodes;
    for (var i = 0; i < menuNodes.length; i++) {
      if (aFolderCoopObj &&
          menuNodes[i].id == aFolderCoopObj.id())
      {
        // Select current folder
        this.menulist.selectedItem = menuNodes[i];
        itemFound = true;
        break;
      }
    }
    // Display subfolder title in custom menuitem
    if (!itemFound &&
        aFolderCoopObj &&
        aFolderCoopObj.id() != FAVORITES_ROOT)
    {
      customItem.setAttribute("label", aFolderCoopObj.name);
      customItem.setAttribute("id", aFolderCoopObj.id());
      this.menulist.selectedItem = customItem;
      itemFound = true;
    }
    if (!itemFound) {
      // If nothing found default choose to root favorites
      var rootItem = document.getElementById(MEDIA_ROOT_STREAMS_ID);
      this.menulist.selectedItem = rootItem;
    }
  },

  /**
   * Save the current folder to the Media Query CoopObj
   */
  _saveMediaQueryFolder: function mqeUI__saveMediaQueryFolder() {
    // CoopObj for current media query
    var mediaQueryCoopObj = gCoop.get(gPhotoDrawer.mediaQueryId);
    var streamName = document.getElementById("streamName");
    mediaQueryCoopObj.name = streamName.value;

    // CoopObj of currently saved folder
    var oldFolder = this._getCurrentMediaQueryFolder();

    // CoopObj of the new currently selected folder; check for root item
    // this will return the tree coopFolder if not selected to be the root
    var newFolder = this._checkForRootItem();

    if (oldFolder && newFolder && (oldFolder != newFolder)) {
      oldFolder.children.remove(mediaQueryCoopObj);
      newFolder.children.add(mediaQueryCoopObj);
    }

    // Close Panel
    this._hidePanel();
  },

  /**
   * Handle state of the delete menuitem within the context menu
   */
  contextOnPopupShowing: function mqeUI_contextOnPopupShowing() {
    var folderCoop = this._getTreeRowResource(this.tree.currentIndex);
    if (!this._ensureTreeHasSelection() ||
        (folderCoop.id() == FAVORITES_ROOT))
    {
      return false;
    }
    // Hide delete button if the folder has child elements
    var contextDeleteButton = document.getElementById("contextDeleteFolder");
    var childEnum = folderCoop.children.enumerate();
    var hasChildren = false;
    while (childEnum.hasMoreElements()) {
      hasChildren = true;
      break;
    }
    contextDeleteButton.setAttribute("hidden", hasChildren);
    return true;
  },

  /**
   * Confirm deletion of folder before deleting
   */
  confirmDeleteFolder: function mqeUI_confirmDeleteFolder() {
    var promptService = CC["@mozilla.org/embedcomp/prompt-service;1"]
                        .getService(CI.nsIPromptService);
    var sbs = CC["@mozilla.org/intl/stringbundle;1"]
              .getService(CI.nsIStringBundleService);
    var bundle = sbs.createBundle(MEDIABAR_PROPERTIES);

    var folderCoop = this._getTreeRowResource(this.tree.currentIndex);
    var titleMessage = flockGetString("photo/mediabar",
                                      "flock.media.edit.deleteFolderTitle");
    var dialogMessage =
          bundle.formatStringFromName("flock.media.edit.deleteFolderMessage",
                                      [folderCoop.name],
                                      1);
    // Hide panel first
    this._hidePanel();

    // Prompt confirm dialog
    var confirm = promptService.confirm(window, titleMessage, dialogMessage);
    if (confirm) {
      // Delete the current folder
      var parentFolder = folderCoop.getParent();
      parentFolder.children.remove(folderCoop);
      // Reinitialize the tree view
      this._initTreeView();
    }
    this._showPanel(document.getElementById("mediabar-watch"),
                    "after_start",
                    false);
  },

  /**
   * Add a new folder to the tree and focus on folder for renaming capabilities
   */
   _addFolder: function mqeUI__addFolder(aParent) {
    var folderName =
          flockGetString("photo/mediabar", "flock.media.edit.folders.new");
    var newFolder = new gCoop.Folder();
    newFolder.name = folderName;
    var nodeIndex;
    if (aParent) {
      aParent.children.addOnce(newFolder);

      // Add a new tree item to the currently selected node
      var currentTreeitem = this.tree.contentView
                                     .getItemAtIndex(this.tree.currentIndex);
      var nodeId = currentTreeitem.firstChild.firstChild.id;
      var selectedNode = document.getElementById(nodeId);
      selectedNode.setAttribute("properties", "container");
      selectedNode.setAttribute("open", "true");
      var parentTreeitem = selectedNode.parentNode.parentNode;
      var hasTreechildren = parentTreeitem.getElementsByTagName("treechildren");
      var treechildren;
      if (hasTreechildren.length) {
        treechildren = hasTreechildren[0];
      } else {
        treechildren = document.createElement("treechildren");
      }
      parentTreeitem.appendChild(treechildren);
      parentTreeitem.setAttribute("container", "true");
      parentTreeitem.setAttribute("open", "true");
      this._addRowTo(newFolder, treechildren, false);

      // Take all childnodes into account when determining the nodeIndex
      nodeIndex = this.tree.currentIndex + treechildren.childNodes.length;
    } else {
      gCoop.media_favorites.children.addOnce(newFolder);
      nodeIndex = this.tree.view.rowCount;
      this._addRowTo(newFolder,
                     document.getElementById("streamChildrenRoot"),
                     false);
    }
    this.tree.view.selection.select(nodeIndex);
    this.tree.startEditing(nodeIndex, this.tree.columns[0]);
  },

  /**
   * Add a to a specified node
   */
  _addFolderTo: function mqeUI__addSubFolder() {
    var parentFolder;
    if (this._ensureTreeHasSelection() && this.tree.currentIndex != 0) {
      parentFolder = this._getTreeRowResource(this.tree.currentIndex);
    } else {
      parentFolder = null;
    }
    this._addFolder(parentFolder);
  },

  /**
   * Fire the startEditing command for an edit in place treecell
   */
  _inPlaceEditFolder: function mqeUI__inPlaceEditFolder() {
    this.tree.startEditing(this.tree.currentIndex, this.tree.columns[0]);
  }

};
