// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('ui', function() {
  const Tree = cr.ui.Tree;
  const TreeItem = cr.ui.TreeItem;

  /**
   * Creates a new tree item for sites data.
   * @param {Object=} data Data used to create a cookie tree item.
   * @constructor
   * @extends {TreeItem}
   */
  function CookiesTreeItem(data) {
    var treeItem = new TreeItem({
      label: data.title,
      data: data
    });
    treeItem.__proto__ = CookiesTreeItem.prototype;

    if (data.icon)
      treeItem.icon = data.icon;

    treeItem.decorate();
    return treeItem;
  }

  CookiesTreeItem.prototype = {
    __proto__: TreeItem.prototype,

    /** @inheritDoc */
    decorate: function() {
      this.hasChildren = this.data.hasChildren;
    },

    /** @inheritDoc */
    addAt: function(child, index) {
      TreeItem.prototype.addAt.call(this, child, index);
      if (child.data && child.data.id)
        this.tree.treeLookup[child.data.id] = child;
    },

    /** @inheritDoc */
    remove: function(child) {
      TreeItem.prototype.remove.call(this, child);
      if (child.data && child.data.id)
        delete this.tree.treeLookup[child.data.id];
    },

    /**
     * Clears all children.
     */
    clear: function() {
      // We might leave some garbage in treeLookup for removed children.
      // But that should be okay because treeLookup is cleared when we
      // reload the tree.
      this.lastElementChild.textContent = '';
    },

    /**
     * The tree path id.
     * @type {string}
     */
    get pathId() {
      var parent = this.parentItem;
      if (parent instanceof CookiesTreeItem)
        return parent.pathId + ',' + this.data.id;
      else
        return this.data.id;
    },

    /** @inheritDoc */
    get expanded() {
      return TreeItem.prototype.__lookupGetter__('expanded').call(this);
    },
    set expanded(b) {
      if (b && this.expanded != b)
        chrome.send(this.tree.requestChildrenMessage, [this.pathId]);

      TreeItem.prototype.__lookupSetter__('expanded').call(this, b);
    }
  };

  /**
   * Creates a new cookies tree.
   * @param {Object=} opt_propertyBag Optional properties.
   * @constructor
   * @extends {Tree}
   */
  var CookiesTree = cr.ui.define('tree');

  CookiesTree.prototype = {
    __proto__: Tree.prototype,

    /**
     * Per-tree dict to map from data.id to tree node.
     */
    treeLookup_: null,
    get treeLookup() {
      if (!this.treeLookup_)
        this.treeLookup_ = {};
      return this.treeLookup_;
    },

    /** @inheritDoc */
    addAt: function(child, index) {
      Tree.prototype.addAt.call(this, child, index);
      if (child.data && child.data.id)
        this.treeLookup[child.data.id] = child;
    },

    /** @inheritDoc */
    remove: function(child) {
      Tree.prototype.remove.call(this, child);
      if (child.data && child.data.id)
        delete this.treeLookup[child.data.id];
    },

    /**
     * Add tree nodes by given parent.
     * @param {Object} parent Parent node.
     * @param {number} start Start index of where to insert nodes.
     * @param {Array} nodesData Nodes data array.
     */
    addByParent: function(parent, start, nodesData) {
      for (var i = 0; i < nodesData.length; ++i) {
        parent.addAt(new CookiesTreeItem(nodesData[i]), start + i);
      }

      cr.dispatchSimpleEvent(this, 'change');
    },

    /**
     * Add tree nodes by parent id.
     * @param {string} parentId Id of the parent node.
     * @param {int} start Start index of where to insert nodes.
     * @param {Array} nodesData Nodes data array.
     */
    addByParentId: function(parentId, start, nodesData) {
      var parent = parentId ? this.treeLookup[parentId] : this;
      this.addByParent(parent, start, nodesData);
    },

    /**
     * Removes tree nodes by parent id.
     * @param {string} parentId Id of the parent node.
     * @param {int} start Start index of nodes to remove.
     * @param {int} count Number of nodes to remove.
     */
    removeByParentId: function(parentId, start, count) {
      var parent = parentId ? this.treeLookup[parentId] : this;

      for (; count > 0 && parent.items.length; --count) {
        parent.remove(parent.items[start]);
      }

      cr.dispatchSimpleEvent(this, 'change');
    },

    /**
     * Clears the tree.
     */
    clear: function() {
      // Remove all fields without recreating the object since other code
      // references it.
      for (var id in this.treeLookup){
        delete this.treeLookup[id];
      }
      this.textContent = '';
    },

    /**
     * Unique 'requestChildren' callback message name to send request to
     * underlying CookiesTreeModelAdapter.
     * @type {string}
     */
    requestChildrenMessage_ : null,
    get requestChildrenMessage() {
      return this.requestChildrenMessage_;
    },

    /**
     * Set callback message name.
     * @param {string} loadChildren Message name for 'loadChildren' request.
     */
    doSetCallback: function(loadChildren) {
      this.requestChildrenMessage_  = loadChildren;
    },

    /**
     * Sets the immediate children of given parent node.
     * @param {string} parentId Id of the parent node.
     * @param {Array} children The immediate children of parent node.
     */
    doSetChildren: function(parentId, children) {
      var parent = parentId ? this.treeLookup[parentId] : this;

      parent.clear();
      this.addByParent(parent, 0, children);
    }
  };

  // CookiesTreeModelAdapter callbacks.
  CookiesTree.setCallback = function(treeId, message) {
    $(treeId).doSetCallback(message);
  }

  CookiesTree.onTreeItemAdded = function(treeId, parentId, start, children) {
    $(treeId).addByParentId(parentId, start, children);
  }

  CookiesTree.onTreeItemRemoved = function(treeId, parentId, start, count) {
    $(treeId).removeByParentId(parentId, start, count);
  }

  CookiesTree.setChildren = function(treeId, parentId, children) {
    $(treeId).doSetChildren(parentId, children);
  }

  return {
    CookiesTree: CookiesTree
  };
});