/*global imce:true*/
(function ($, Drupal, imce) {
  'use strict';

  /**
   * @file
   * Defines imce Folder object.
   */

  /**
   * Folder.
   */
  imce.Folder = function (name, conf) {
    this.construct(name, conf);
  };

  /**
   * Item prototype
   */
  var ItemProto = imce.Item.prototype;

  /**
   * Folder prototype extends Item prototype.
   */
  var Folder = $.extend(imce.Folder.prototype, ItemProto);

  /**
   * Constructs the Folder.
   */
  Folder.construct = function (name, conf) {
    var Folder = this;
    Folder.isFolder = true;
    Folder.type = 'folder';
    Folder.items = [];
    Folder.files = {};
    Folder.subfolders = {};
    ItemProto.construct.apply(Folder, arguments);
    Folder.setConf(conf);
  };

  /**
   * Creates folder elements.
   */
  Folder.createEl = function () {
    var nameEl;
    var toggleEl;
    var branchEl;
    var Folder = this;
    if (!Folder.el) {
      // Item elements.
      ItemProto.createEl.apply(Folder, arguments);
      Folder.el.className += ' folder';
      // Folder elements
      Folder.contentEl = imce.createEl('<div class="imce-folder-content clearfix"></div>');
      Folder.subtreeEl = imce.createEl('<div class="imce-subtree"></div>');
      branchEl = Folder.branchEl = imce.createEl('<div class="imce-branch"><span class="imce-branch-toggle"></span><span class="imce-branch-name imce-ficon"></span></div>');
      toggleEl = Folder.branchToggleEl = branchEl.firstChild;
      toggleEl.onclick = imce.eBranchToggleClick;
      nameEl = Folder.branchNameEl = branchEl.children[1];
      nameEl.onclick = imce.eBranchNameClick;
      branchEl.Folder = nameEl.Folder = toggleEl.Folder = Folder;
    }
  };

  /**
   * Sets the folder content.
   */
  Folder.setContent = function (content) {
    var i;
    var Item;
    var list;
    var Folder = this;
    var items = Folder.getItems();
    // Remove the items that no longer exist.
    for (i in items) {
      if (!imce.owns(items, i)) {
        continue;
      }
      Item = items[i];
      list = Item.isFolder ? content.subfolders : content.files;
      // Existing item is not in the list
      if (!list || !imce.owns(list, Item.name)) {
        // Make sure it's not (parent of) a predefined folder
        if (!Item.isFolder || !Item.hasPredefinedPath()) {
          Folder.removeItem(Item);
        }
      }
    }
    Folder.extend(content.props);
    Folder.addContent(content);
    Folder.content = content;
    Folder.updateSubtree();
  };

  /**
   * Adds new files and subfolders.
   */
  Folder.addContent = function (content, selectNew) {
    var Folder = this;
    var files = content.files;
    var subfolders = content.subfolders;
    if (!files && !subfolders) {
      return;
    }
    // Add items
    Folder.addItems(files, 'file');
    Folder.addItems(subfolders, 'folder');
    // Update sort
    if (Folder.active) {
      Folder.sortItems();
    }
    else {
      Folder.needSort = 1;
    }
    Folder.sortTree();
    // Select new items.
    if (selectNew && Folder.active) {
      var name;
      var fname;
      var sname;
      imce.deselectAll();
      if (files) {
        for (fname in files) {
          if (!imce.owns(files, fname)) {
            continue;
          }
          Folder.getItem(fname).select();
        }
      }
      if (subfolders) {
        for (sname in subfolders) {
          if (!imce.owns(subfolders, sname)) {
            continue;
          }
          Folder.getItem(sname).select();
        }
      }
      // Scroll the last item into view
      if (name = (fname || sname)) {
        Folder.getItem(name).scrollIntoView();
      }
    }
  };

  /**
   * Add a list of items of a specific type.
   */
  Folder.addItems = function (items, type) {
    var Item;
    var name;
    var Type = type === 'folder' ? imce.Folder : imce.File;
    if (items) {
      for (name in items) {
        // Update
        if (Item = this.getItem(name)) {
          Item.extend(items[name]);
          this.updateStatus();
        }
        // Insert
        else {
          Item = new Type(name);
          Item.extend(items[name]);
          this.appendItem(Item);
        }
      }
    }
  };

  /**
   * Returns a copy of items array.
   */
  Folder.getItems = function () {
    return this.items.slice(0);
  };

  /**
   * Append an item to the folder.
   */
  Folder.appendItem = function (Item) {
    var Folder = this;
    var name = Item.name;
    var existing;
    if (!Folder.validateAppend(Item)) {
      return;
    }
    // Remove the item from old parent
    Item.remove(true);
    // Remove existing item with the same name
    if (existing = Folder.getItem(name)) {
      existing.remove();
    }
    // Append item.
    Folder.items.push(Item);
    Item.parent = Folder;
    Folder.contentEl.appendChild(Item.el);
    // Append subfolder
    if (Item.isFolder) {
      Folder.prepareSubtree();
      Folder.subtreeEl.appendChild(Item.branchEl);
      Folder.subfolders[name] = Item;
      Item.setPath((Folder.parent ? Folder.path + '/' : '') + Item.name);
    }
    // Append file
    else {
      Folder.files[name] = Item;
    }
    // Update status.
    Folder.updateStatus();
  };

  /**
   * Remove an item from the folder.
   */
  Folder.removeItem = function (Item, shallow) {
    var name = Item.name;
    var Folder = this;
    // Check if the item is a child
    if (Item.parent !== Folder) {
      return;
    }
    // Deep removal
    if (!shallow) {
      // Remove all descendants of the subfolder.
      if (Item.isFolder) {
        for (var i in Item.items) {
          if (!imce.owns(Item.items, i)) {
            continue;
          }
          Item.removeItem(Item.items[i]);
        }
      }
    }
    // Set item free.
    Item.deselect();
    Item.setBusy(false);
    // Remove subfolder
    if (Item.isFolder) {
      if (Item.active) {
        Folder.activate();
      }
      Item.setPath(null);
      delete Folder.subfolders[name];
      imce.removeEl(Item.branchEl);
      Folder.updateSubtree();
    }
    // Remove file
    else {
      delete Folder.files[name];
    }
    // Remove item
    Folder.items.splice(Folder.indexOf(Item), 1);
    delete Item.parent;
    imce.removeEl(Item.el);
    Folder.updateStatus();
  };

  /**
   * Set folder path.
   * Register the folder to the tree.
   */
  Folder.setPath = function (newpath) {
    var i;
    var Folder = this;
    var oldpath = Folder.path;
    var subfolders = Folder.subfolders;
    if (oldpath !== newpath) {
      // Remove
      if (newpath == null) {
        for (i in subfolders) {
          if (!imce.owns(subfolders, i)) {
            continue;
          }
          subfolders[i].setPath(null);
        }
        delete imce.tree[oldpath];
        delete Folder.path;
      }
      // Add
      else {
        Folder.path = newpath;
        imce.tree[newpath] = Folder;
        Folder.setDisabled(!Folder.getConf());
        for (i in subfolders) {
          if (!imce.owns(subfolders, i)) {
            continue;
          }
          subfolders[i].setPath(newpath + '/' + subfolders[i].name);
        }
        Folder.updateStatus();
      }
    }
  };

  /**
   * Returns a permission value.
   */
  Folder.getPermission = function (name) {
    return imce.permissionInFolderConf(name, this.getConf());
  };

  /**
   * Returns folder configuration.
   */
  Folder.getConf = function () {
    var conf = this.conf;
    var parent;
    if (conf) {
      return conf;
    }
    if (parent = this.parent) {
      if (conf = parent.getConf()) {
        if (imce.permissionInFolderConf('browse_subfolders', conf)) {
          return $.extend({inherited: true}, conf);
        }
      }
    }
  };

  /**
   * Sets folder configuration.
   */
  Folder.setConf = function (conf) {
    if (this.conf !== conf) {
      this.conf = conf;
      this.setDisabled(!this.getConf());
    }
  };

  /**
   * Open folder.
   */
  Folder.open = function (refresh) {
    if (refresh || !this.content) {
      this.load();
    }
    this.activate();
  };

  /**
   * Dynamically load folder contents.
   */
  Folder.load = function () {
    if (this.isReady()) {
      this.setLoading(true);
      imce.ajax('browse', {
        activeFolder: this,
        customComplete: imce.xFolderLoadComplete
      });
    }
  };

  /**
   * Activate folder.
   */
  Folder.activate = function () {
    var Folder = this;
    var oldFolder = imce.activeFolder;
    var parent = Folder.parent;
    if (!Folder.active) {
      // Deactivate the old dir.
      if (oldFolder) {
        oldFolder.deactivate();
      }
      // Check sort
      if (Folder.needSort) {
        Folder.sortItems();
      }
      imce.activeFolder = Folder;
      Folder.active = true;
      $(Folder.branchEl).addClass('active');
      // Add the content to the dom if it is fully loaded.
      if (!Folder.loading) {
        Folder.addContentToDom();
      }
      Folder.setContentVisibility(true);
      // Expand parents if collapsed.
      while (parent) {
        parent.expand();
        parent = parent.parent;
      }
      // Update status and header
      Folder.updateHeader();
      Folder.updateStatus();
      // Trigger activateFolder handlers.
      imce.trigger('activateFolder', Folder, oldFolder);
    }
  };

  /**
   * Deactivate folder.
   */
  Folder.deactivate = function () {
    var Folder = this;
    if (Folder.active) {
      Folder.setContentVisibility(false);
      imce.deselectAll();
      imce.activeFolder = null;
      Folder.active = false;
      $(Folder.branchEl).removeClass('active');
    }
  };

  /**
   * Set loading state.
   */
  Folder.setLoading = function (state) {
    var Folder = this;
    if (state) {
      if (!Folder.loading) {
        Folder.setBusy(true);
        Folder.setState('loading');
        if (Folder.active) {
          imce.deselectAll();
        }
      }
    }
    else if (Folder.loading) {
      Folder.setBusy(false);
      Folder.unsetState('loading');
      if (Folder.active) {
        Folder.addContentToDom();
      }
    }
  };

  /**
   * Returns an item by name.
   */
  Folder.getItem = function (name) {
    var Folder = this;
    if (imce.owns(Folder.files, name)) {
      return Folder.files[name];
    }
    if (imce.owns(Folder.subfolders, name)) {
      return Folder.subfolders[name];
    }
  };

  /**
   * Returns an item by index.
   */
  Folder.getItemAt = function (i) {
    return this.items[i];
  };

  /**
   * Returns the index of an item.
   */
  Folder.indexOf = function (Item) {
    return $.inArray(Item, this.items);
  };

  /**
   * Selects all items.
   */
  Folder.selectAll = function () {
    for (var i in this.items) {
      if (!imce.owns(this.items, i)) {
        continue;
      }
      this.items[i].select();
    }
  };

  /**
   * Returns the number of items.
   */
  Folder.countItems = function () {
    return this.items.length;
  };

  /**
   * Returns the number of subfolders.
   */
  Folder.countSubfolders = function () {
    var i;
    var count = 0;
    for (i in this.subfolders) {
      if (!imce.owns(this.subfolders, i)) {
        continue;
      }
      count++;
    }
    return count;
  };

  /**
   * Name change handler.
   */
  Folder.onNameChange = function (oldval) {
    ItemProto.onNameChange.apply(this, arguments);
    this.branchNameEl.innerHTML = Drupal.checkPlain(this.name);
    this.branchNameEl.title = this.name;
  };

  /**
   * Item name change handler.
   * Triggered by imce.Item.onNameChange()
   */
  Folder.onItemNameChange = function (Item, oldname) {
    var Folder = this;
    var name = Item.name;
    var group = Item.isFolder ? Folder.subfolders : Folder.files;
    delete group[oldname];
    group[name] = Item;
    // Set folder path
    if (Item.isFolder) {
      Item.setPath((Folder.parent ? Folder.path + '/' : '') + name);
    }
  };

  /**
   * Double-click handler.
   */
  Folder.dblClick = function () {
    this.open();
  };

  /**
   * Inserts the content element into the main content area.
   */
  Folder.addContentToDom = function () {
    var el = this.contentEl;
    var parentEl = imce.contentEl;
    if (el.parentNode !== parentEl) {
      parentEl.appendChild(el);
    }
  };

  /**
   * Sets visibility of the content element.
   */
  Folder.setContentVisibility = function (show) {
    var el = this.contentEl;
    el.style.display = show ? '' : 'none';
    if (el.scrollTop) {
      el.scrollTop = 0;
    }
  };

  /**
   * Prepares for subfolder appending.
   */
  Folder.prepareSubtree = function () {
    var Folder = this;
    if (Folder.subtreeEl.parentNode !== Folder.branchEl) {
      Folder.branchEl.appendChild(Folder.subtreeEl);
      $(Folder.branchEl).removeClass('leaf');
      // Prevent expanding of inactive dirs except the first activated dir on init
      if (!Folder.active && imce.activeFolder) {
        Folder.expanded = true;
        Folder.shrink();
      }
      else {
        Folder.expand();
      }
    }
  };

  /**
   * Check and remove subtree element if it's empty.
   */
  Folder.updateSubtree = function () {
    if (!this.countSubfolders()) {
      this.shrink();
      imce.removeEl(this.subtreeEl);
      $(this.branchEl).addClass('leaf');
    }
  };

  /**
   * Expands the subtree.
   */
  Folder.expand = function () {
    if (!this.expanded) {
      this.expanded = true;
      $(this.branchEl).addClass('expanded');
      $(this.subtreeEl).show();
    }
  };

  /**
   * Shrinks the subtree.
   */
  Folder.shrink = function () {
    if (this.expanded) {
      this.expanded = false;
      $(this.branchEl).removeClass('expanded');
      $(this.subtreeEl).hide();
    }
  };

  /**
   * Update folder status.
   */
  Folder.updateStatus = function () {
    if (this.active) {
      imce.updateStatus();
    }
  };

  /**
   * Update header sort.
   */
  Folder.updateHeader = function () {
    if (this.active) {
      imce.updateHeader();
    }
  };

  /**
   * Sort folder items by an item property.
   */
  Folder.sortItems = function (key, desc) {
    var i;
    var sorter;
    var Folder = this;
    var items = Folder.items;
    var active = Folder.activeSort || imce.activeSort || imce.local.activeSort || {};
    if (key == null) {
      key = active.key || 'name';
    }
    if (desc == null) {
      desc = !!active.desc;
    }
    // Remove lazy sort flag.
    Folder.needSort = 0;
    // Check sorter
    if (sorter = imce.sorters[key]) {
      items.sort(sorter);
      if (desc) {
        items.reverse();
      }
      for (i in items) {
        if (!imce.owns(items, i)) {
          continue;
        }
        this.contentEl.appendChild(items[i].el);
      }
      Folder.activeSort = {key: key, desc: desc};
      Folder.updateHeader();
    }
  };

  /**
   * Sorts folder tree elements by name.
   */
  Folder.sortTree = function () {
    var i;
    var Folder = this;
    var subfolders = Folder.subfolders;
    var arr = [];
    for (i in subfolders) {
      if (!imce.owns(subfolders, i)) {
        continue;
      }
      arr.push(subfolders[i]);
    }
    if (arr.length > 1) {
      arr.sort(imce.sortBranchName);
      for (i in arr) {
        if (!imce.owns(arr, i)) {
          continue;
        }
        Folder.subtreeEl.appendChild(arr[i].branchEl);
      }
    }
  };

  /**
   * Check if the item can be appended to the folder.
   */
  Folder.validateAppend = function (Item, copy) {
    // Disallow self appending
    if (Item === this) {
      return false;
    }
    var parent = Item.parent;
    // Allow orphan appending
    if (!parent) {
      return true;
    }
    // Disallow re-appending children
    if (!copy && parent === this) {
      return false;
    }
    // Disallow (grand)parents appending
    if (Item.isFolder) {
      for (parent = this.parent; parent; parent = parent.parent) {
        if (parent === Item) {
          return false;
        }
      }
    }
    return true;
  };

  /**
   * Checks if the folder is predefined.
   */
  Folder.isPredefined = function () {
    return !!this.conf;
  };

  /**
   * Returns the first predefined descendent including itself.
   */
  Folder.hasPredefinedPath = function () {
    if (this.isPredefined()) {
      return this;
    }
    var i;
    var Folder;
    var subfolders = this.subfolders;
    for (i in subfolders) {
      if (Folder = subfolders[i].hasPredefinedPath()) {
        return Folder;
      }
    }
    return false;
  };

  /**
   * Returns status text.
   */
  Folder.formatStatus = function () {
    return '<div class="items">' + imce.formatItemsStatus(this.countItems(), this.getSize()) + '</div>';
  };

  /**
   * Returns the size of the folder.
   */
  Folder.getSize = function () {
    var i;
    var size = 0;
    var files = this.files;
    for (i in files) {
      if (!imce.owns(files, i)) {
        continue;
      }
      size += files[i].size || 0;
    }
    return size;
  };


  /**
   * Click event for branch name.
   */
  imce.eBranchNameClick = function (event) {
    this.Folder.open();
    return false;
  };


  /**
   * Click event for branch toggle.
   */
  imce.eBranchToggleClick = function (event) {
    var Folder = this.Folder;
    if (Folder.countSubfolders()) {
      if (Folder.expanded) {
        Folder.shrink();
      }
      else {
        Folder.expand();
      }
    }
    else {
      Folder.open();
    }
    return false;
  };

  /**
   * Ajax complete handler for folder loading.
   */
  imce.xFolderLoadComplete = function (xhr, status) {
    var content;
    var opt = this;
    var Folder = opt.activeFolder;
    var response = opt.response;
    if (response && (content = response.content)) {
      Folder.setContent(content);
    }
    Folder.setLoading(false);
    if (Folder.countSubfolders()) {
      Folder.expand();
    }
  };

})(jQuery, Drupal, imce);
