"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.TreeSelector = exports.SELECTION_PARTIAL = exports.SELECTION_FULL = exports.SELECTION_EMPTY = void 0;

var R = _interopRequireWildcard(require("ramda"));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }

function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

// @enum {number}
var SELECTION_EMPTY = 0;
exports.SELECTION_EMPTY = SELECTION_EMPTY;
var SELECTION_FULL = 1;
exports.SELECTION_FULL = SELECTION_FULL;
var SELECTION_PARTIAL = -1;
exports.SELECTION_PARTIAL = SELECTION_PARTIAL;

var TreeSelector =
/*#__PURE__*/
function () {
  /** @param {{
   * rootId: function(Object):string,
   * parent: function(string, Object):string,
   * children: function(string, Object):Array<string>,
   * name: function(string, Object):string
   * isChild: function(string, Object):boolean,
   * all: function(function(string):boolean, Object):Generator<string, void, void>,
   * }} props */
  function TreeSelector(props) {
    var _this = this;

    _classCallCheck(this, TreeSelector);

    this.rootId = props.rootId || function () {
      return 'root';
    };

    this.parent = props.parent;

    this.children = function (id, context) {
      return props.children(id, context) || [];
    };

    this.name = props.name || function (nodeId) {
      return nodeId;
    };

    this.isChild = props.isChild || function (nodeId, context) {
      return _this.children(nodeId, context).length === 0;
    };

    this.all = props.all || function (predicate, context) {
      return _this._all(predicate, _this.rootId(context), context);
    };
  }

  _createClass(TreeSelector, [{
    key: "_all",
    value: function* _all(predicate, nodeId, context) {
      if (predicate(nodeId)) yield nodeId;
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = this.children(nodeId, context)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var child = _step.value;
          yield* this._all(predicate, child, context);
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return != null) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }
    }
  }, {
    key: "createMap",
    value: function createMap(fn) {
      var map = {};
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (var _iterator2 = this._all(R.T, this.rootId())[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
          var nodeId = _step2.value;
          map[nodeId] = fn(nodeId);
        }
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
            _iterator2.return();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }

      return map;
    }
  }, {
    key: "_reversedPath",
    value: function* _reversedPath(nodeId, context) {
      var p = this.parent(nodeId, context);

      while (p) {
        yield p;
        p = this.parent(p, context);
      }
    }
  }, {
    key: "_selectionBasedOnChildren",
    value: function _selectionBasedOnChildren(nodeId, selection, context) {
      var _this$children = this.children(nodeId, context),
          _this$children2 = _toArray(_this$children),
          first = _this$children2[0],
          other = _this$children2.slice(1);

      var selFirstChild = first === undefined ? undefined : selection[first];
      return selFirstChild !== undefined ? R.any(function (child) {
        return selection[child] !== selFirstChild;
      }, other) ? SELECTION_PARTIAL : selFirstChild : selection[nodeId];
    }
  }, {
    key: "_branchSelection",
    value: function _branchSelection(branchRoot, newState, context) {
      var _this2 = this;

      return R.reduce(function (acc, item) {
        return R.merge(acc, _this2._branchSelection(item, newState, context));
      }, R.objOf(branchRoot, newState), this.children(branchRoot, context));
    }
  }, {
    key: "select",
    value: function select(nodeId, newState, initialSelection, context) {
      var _this3 = this;

      var sel0 = R.merge(initialSelection, this._branchSelection(nodeId, newState, context));

      var newSel = function newSel(accumSelection, item) {
        var sel = _this3._selectionBasedOnChildren(item, accumSelection, context);

        return accumSelection[item] === sel ? R.reduced(accumSelection) : R.assoc(item, sel, accumSelection);
      };

      return R.reduce(newSel, sel0, this._reversedPath(nodeId, context));
    }
  }, {
    key: "selectAllLeaves",
    value: function selectAllLeaves(predicate, newState, initialSelection, context) {
      var _this4 = this;

      var children = this.all(R.allPass([function (x) {
        return _this4.isChild(x, context);
      }, function (x) {
        return predicate(x, context);
      }]), context);
      return R.reduce(function (acc, item) {
        return _this4.select(item, newState, acc, context);
      }, initialSelection, children);
    }
  }, {
    key: "toggle",
    value: function toggle(nodeId, initialSelection, context) {
      var newSelection = initialSelection[nodeId] === SELECTION_FULL ? SELECTION_EMPTY : SELECTION_FULL;
      return this.select(nodeId, newSelection, initialSelection, context);
    }
  }, {
    key: "_describe",
    value: function _describe(nodeId, selection, context, _inverse) {
      var _this5 = this;

      var selectionStatus = function selectionStatus(idArray) {
        var empty = 0;
        var partial = 0;
        var full = 0;
        var _iteratorNormalCompletion3 = true;
        var _didIteratorError3 = false;
        var _iteratorError3 = undefined;

        try {
          for (var _iterator3 = idArray[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
            var id = _step3.value;

            switch (selection[id]) {
              case SELECTION_EMPTY:
                empty += 1;
                break;

              case SELECTION_FULL:
                full += 1;
                break;

              default:
                partial += 1;
            }
          }
        } catch (err) {
          _didIteratorError3 = true;
          _iteratorError3 = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
              _iterator3.return();
            }
          } finally {
            if (_didIteratorError3) {
              throw _iteratorError3;
            }
          }
        }

        return {
          empty: empty,
          partial: partial,
          full: full
        };
      };

      var flatten = function flatten(array) {
        return array.length === 1 && Array.isArray(array[0]) ? array[0] : array;
      };

      var children = this.children(nodeId, context);
      var status = selectionStatus(children);
      var nodeName = this.parent(nodeId, context) == null ? 'Everything' : this.name(nodeId, context);

      if (_inverse) {
        if (selection[nodeId] === SELECTION_FULL) return ['not', nodeName];
        if (selection[nodeId] === SELECTION_EMPTY) return nodeName;

        if (status.full >= status.empty) {
          return flatten(children.filter(function (id) {
            return selection[id] !== SELECTION_FULL;
          }).map(function (id) {
            return _this5._describe(id, selection, context, true);
          }));
        } else {
          return [nodeName, 'except', flatten(children.filter(function (id) {
            return selection[id] !== SELECTION_EMPTY;
          }).map(function (id) {
            return _this5._describe(id, selection, context, false);
          }))];
        }
      } else {
        if (this.parent(nodeId, context) == null) {
          if (children.length === status.full) return nodeName;
          if (children.length === status.empty) return 'Nothing';
        } else {
          if (selection[nodeId] === SELECTION_EMPTY) return ['not', nodeName];
          if (selection[nodeId] === SELECTION_FULL) return nodeName;
        }

        if (status.full >= status.empty) {
          return [nodeName, 'except', flatten(children.filter(function (id) {
            return selection[id] !== SELECTION_FULL;
          }).map(function (id) {
            return _this5._describe(id, selection, context, true);
          }))];
        } else {
          return flatten(children.filter(function (id) {
            return selection[id] !== SELECTION_EMPTY;
          }).map(function (id) {
            return _this5._describe(id, selection, context, false);
          }));
        }
      }
    }
  }, {
    key: "describe",
    value: function describe(selection, context) {
      return this._describe(this.rootId(context), selection, context, false);
    }
  }, {
    key: "selectedIds",
    value: function selectedIds(selection, context) {
      var _this6 = this;

      var ids = [];

      var inner = function inner(nodeId) {
        if (_this6.parent(nodeId, context) == null) {
          switch (selection[nodeId]) {
            case 0:
              ids = null;
              return;

            case 1:
              return;

            default:
              _this6.children(nodeId, context).forEach(inner);

          }
        } else {
          switch (selection[nodeId]) {
            case 0:
              return;

            case 1:
              ids.push(nodeId);
              return;

            default:
              _this6.children(nodeId, context).forEach(inner);

          }
        }
      };

      inner(this.rootId(context));
      return ids;
    }
  }]);

  return TreeSelector;
}();

exports.TreeSelector = TreeSelector;