"use strict";

var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard");

var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");

require("core-js/modules/es.function.name");

require("core-js/modules/es.object.to-string");

require("core-js/modules/es.regexp.to-string");

_Object$defineProperty(exports, "__esModule", {
  value: true
});

exports.default = void 0;

var _getIterator2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js/get-iterator"));

var _getIteratorMethod2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js/get-iterator-method"));

var _symbol = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/symbol"));

var _from = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/array/from"));

var _typeof2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/typeof"));

var _find = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find"));

var _keys = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/keys"));

var _map = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/map"));

var _values = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/values"));

var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/toConsumableArray"));

var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));

var _every = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/every"));

var _isArray = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/array/is-array"));

var _slice = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/slice"));

var _keys2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/keys"));

var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));

var _sort = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/sort"));

var _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify"));

var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/defineProperty"));

var _assign = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/assign"));

var _filter = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/filter"));

var _map2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/map"));

var _react = _interopRequireWildcard(require("react"));

var _styles = require("@material-ui/core/styles");

var _List = _interopRequireDefault(require("@material-ui/core/List"));

var _reactIntl = require("react-intl");

var _isEqual = _interopRequireDefault(require("lodash/isEqual"));

var _omit = _interopRequireDefault(require("lodash/omit"));

var _dateFns = _interopRequireDefault(require("@date-io/date-fns"));

var _pickers = require("@material-ui/pickers");

var _index = require("../lib/data_analysis/index");

var _DiscreteFilter = _interopRequireDefault(require("./DiscreteFilter"));

var _DateFilter = _interopRequireDefault(require("./DateFilter"));

var _stats = _interopRequireDefault(require("../stats"));

var _FormattedFieldname = _interopRequireDefault(require("../internal/FormattedFieldname"));

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof _symbol.default === "undefined" || (0, _getIteratorMethod2.default)(o) == null) { if ((0, _isArray.default)(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = (0, _getIterator2.default)(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { var _context9; if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = (0, _slice.default)(_context9 = Object.prototype.toString.call(o)).call(_context9, 8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return (0, _from.default)(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

var m = (0, _reactIntl.defineMessages)({
  // Button text to change which fields are shown and filterable in the filter pane
  editFilters: {
    "id": "eO0bIg==",
    "defaultMessage": 'Edit Filters…'
  },
  // Label for filter by date observation was created
  created: {
    "id": "pxFasw==",
    "defaultMessage": 'Date of observation'
  },
  // Label for filter by date observation was modified (e.g. edited by a user)
  modified: {
    "id": "eykPgw==",
    "defaultMessage": 'Modified'
  },
  // Label for filter by category (e.g. the preset)
  preset: {
    "id": "KdbzkA==",
    "defaultMessage": 'Category'
  }
});
var memoizedStats = (0, _index.createMemoizedStats)(); // Stats just for the created and modified fields. The other stat instance we
// use is for memoized stats of observation tags which does not include these
// top-level props

var getTimestampStats = function getTimestampStats(observations
/*: Observation[]*/
)
/*: Statistics*/
{
  return memoizedStats((0, _map2.default)(observations).call(observations, function (o) {
    return {
      $created: o.created_at,
      $modified: o.timestamp
    };
  }));
};

var FilterPanel = function FilterPanel(_ref) {
  var _context2, _context3;

  var filter = (0, _filter.default)(_ref),
      onChangeFilter = _ref.onChangeFilter,
      _ref$observations = _ref.observations,
      observations = _ref$observations === void 0 ? [] : _ref$observations,
      _ref$fields = _ref.fields,
      fields = _ref$fields === void 0 ? [] : _ref$fields,
      _ref$presets = _ref.presets,
      presets = _ref$presets === void 0 ? [] : _ref$presets;
  var cx = useStyles();

  var _useIntl = (0, _reactIntl.useIntl)(),
      t = _useIntl.formatMessage;

  var filterByField
  /*: { [fieldId: string]: Filter | null }*/
  = {};
  var filterError;

  try {
    filterByField = parseFilter(filter);
  } catch (e) {
    console.log(filter);
    console.warn(e);
    filterError = false;
  }

  var timestampStats = (0, _react.useMemo)(function () {
    return getTimestampStats(observations);
  }, [observations]);
  var stats = (0, _react.useMemo)(function () {
    return (0, _stats.default)(observations);
  }, [observations]);

  var handleChangeFilter = function handleChangeFilter(fieldId) {
    return function (filter) {
      var newFilterByField = filter == null ? (0, _omit.default)(filterByField, fieldId) : (0, _assign.default)({}, filterByField, (0, _defineProperty2.default)({}, fieldId, filter));
      var newFilter = compileFilter(newFilterByField);
      onChangeFilter(newFilter);
    };
  }; // Filters are shown for: date the observation was created, date modified
  // (edited), the category (preset), and any fields defined in the preset which
  // are select_one, date or date_time. Currently we don't show presets for
  // free-form text fields (which could have too many values, or too many ways
  // of spelling things to makes sense).


  var filterFields = (0, _react.useMemo)(function () {
    var _context;

    var $createdId = (0, _stringify.default)(['$created']);
    var $presetId = (0, _stringify.default)(['$preset']);
    var filterFields
    /*: { [fieldId: string]: Field }*/
    = (0, _defineProperty2.default)({}, $createdId, {
      id: $createdId,
      key: ['$created'],
      label: t(m.created),
      type: 'datetime',
      min_value: timestampStats[$createdId] && timestampStats[$createdId].datetime.min,
      max_value: timestampStats[$createdId] && timestampStats[$createdId].datetime.max
    });
    var presetOptions = (0, _map2.default)(_context = (0, _sort.default)(presets // .filter(
    //   preset =>
    //     stats['categoryId'] &&
    //     stats['categoryId'].string.values.has(preset.id)
    // )
    ).call(presets, presetCompare)).call(_context, function (preset) {
      return {
        value: preset.id,
        label: preset.name
      };
    });
    filterFields[$presetId] = {
      id: $presetId,
      key: ['$preset'],
      label: t(m.preset),
      type: 'select_one',
      options: presetOptions
    }; // Enable filtering by any select_one, date or date_time field that is
    // defined in the preset, but add in options (for select_one) and min/max
    // (for dates) from the actual data, since the data could include values
    // outside the range defined in the preset

    (0, _forEach.default)(fields).call(fields, function (field) {
      var fieldId = (0, _stringify.default)(field.key);
      var fieldStats = stats[fieldId];

      if (field.type === 'select_one') {
        // $FlowFixMe
        filterFields[fieldId] = (0, _assign.default)({}, field, {
          options: combineOptionsWithStats(field.options, fieldStats)
        });
      } else if (field.type === 'date' || field.type === 'datetime') {
        // $FlowFixMe
        filterFields[fieldId] = (0, _assign.default)({}, field, {
          min_value: fieldStats && fieldStats[field.type].min,
          max_value: fieldStats && fieldStats[field.type].max
        });
      }
    });
    return filterFields;
  }, [t, timestampStats, presets, fields, stats]);
  (0, _react.useEffect)(function () {
    if (!filterError) return;
    onChangeFilter(null);
  }, [filterError, onChangeFilter]);
  if (filterError) return null;
  return /*#__PURE__*/_react.default.createElement(_pickers.MuiPickersUtilsProvider, {
    utils: _dateFns.default
  }, /*#__PURE__*/_react.default.createElement(_List.default, {
    className: cx.list
  }, (0, _filter.default)(_context2 = (0, _map2.default)(_context3 = (0, _keys2.default)(filterFields)).call(_context3, function (id) {
    var field = filterFields[id];
    if (!field) return;
    var fieldId = (0, _stringify.default)(field.key);

    switch (field.type) {
      case 'select_one':
        return /*#__PURE__*/_react.default.createElement(_DiscreteFilter.default, {
          key: field.id,
          fieldKey: field.key,
          label: /*#__PURE__*/_react.default.createElement(_FormattedFieldname.default, {
            field: field
          }),
          filter: filterByField[fieldId],
          options: field.options,
          onChangeFilter: handleChangeFilter(fieldId)
        });

      case 'date':
      case 'datetime':
        return /*#__PURE__*/_react.default.createElement(_DateFilter.default, {
          key: field.id,
          fieldKey: field.key,
          label: /*#__PURE__*/_react.default.createElement(_FormattedFieldname.default, {
            field: field
          }),
          filter: filterByField[fieldId],
          min: field.min_value || '2001-01-01',
          max: field.max_value || new Date().toISOString(),
          onChangeFilter: handleChangeFilter(fieldId)
        });
    }
  })).call(_context2, Boolean)));
};

var _default = FilterPanel;
exports.default = _default;
var comparisonOps = ['<=', '>='];
var membershipOps = ['in', '!in']; // Parse a filter and return filter expressions by field id

function parseFilter(filter
/*: Filter | null*/
)
/*: { [fieldId: string]: Filter | null }*/
{
  var _context4;

  var filterByField = {};
  if (filter == null) return filterByField;
  if (!isValidFilter(filter)) throw new Error('Unsupported filter expression');
  (0, _forEach.default)(_context4 = (0, _slice.default)(filter).call(filter, 1)).call(_context4, function (subFilter) {
    if (!(0, _isArray.default)(subFilter)) return;

    if (subFilter[0] === 'all') {
      var _fieldId = (0, _stringify.default)(subFilter[1][1]);

      filterByField[_fieldId] = subFilter;
    } else {
      var _fieldId2 = (0, _stringify.default)(subFilter[1]);

      filterByField[_fieldId2] = subFilter;
    }
  });
  return filterByField;
}

function compileFilter(filterByField
/*: {
  [fieldId: string]: Filter
}*/
)
/*: Filter | null*/
{
  var _context5;

  var filter = ['all'];
  (0, _forEach.default)(_context5 = (0, _keys2.default)(filterByField)).call(_context5, function (fieldId) {
    return filter.push(filterByField[fieldId]);
  });
  if (filter.length === 1) return null; // $FlowFixMe

  return filter;
} // Currently we only support a very specific filter structure


function isValidFilter(filter)
/*: boolean*/
{
  var _context6;

  if (!(0, _isArray.default)(filter)) return false;
  if (filter[0] !== 'all') return false;
  return (0, _every.default)(_context6 = (0, _slice.default)(filter).call(filter, 1)).call(_context6, function (subFilter) {
    if (!(0, _isArray.default)(subFilter)) return false;

    if (subFilter[0] === 'all') {
      var _context7;

      if (subFilter.length < 2) return false;
      var key;
      return (0, _every.default)(_context7 = (0, _slice.default)(subFilter).call(subFilter, 1)).call(_context7, function (subFilter) {
        if (!(0, _isArray.default)(subFilter)) return false;
        if (!(0, _includes.default)(comparisonOps).call(comparisonOps, subFilter[0])) return false;
        if (subFilter.length !== 3) return false;
        if (key && !(0, _isEqual.default)(key, subFilter[1])) return false;
        key = subFilter[1];
        return true;
      });
    }

    if (!(0, _includes.default)(membershipOps).call(membershipOps, subFilter[0])) return false;
    if (subFilter.length < 3) return false;
    return true;
  });
} // Takes a list of options for a select_one field defined in a preset, and adds
// in any values that exist in the data that are not already listed in the field
// definition. Mapeo Desktop (currently) allows you to add new option to a
// select_one field which aren't defined in the preset, and this allows you to
// filter by these new values


function combineOptionsWithStats(fieldOptions
/*: SelectOptions*/
, fieldStats
/*: FieldStatistic*/
)
/*: SelectOptions*/
{
  var _context8;

  if (!fieldStats) return fieldOptions;
  var optionsWithStats = (0, _toConsumableArray2.default)(fieldOptions);
  (0, _forEach.default)(_context8 = (0, _keys2.default)(fieldStats)).call(_context8, function (valueType) {
    if (!fieldStats[valueType] || !(0, _values.default)(fieldStats[valueType])) return;
    var values = (0, _values.default)(fieldStats[valueType]);
    if (!(values instanceof _map.default)) return;

    var _iterator = _createForOfIteratorHelper((0, _keys.default)(values).call(values)),
        _step;

    try {
      var _loop = function _loop() {
        var value = _step.value;
        if ((0, _find.default)(optionsWithStats).call(optionsWithStats, function (o) {
          return o === value || (0, _typeof2.default)(o) === 'object' && o !== null && o.value === value;
        })) return "continue";
        optionsWithStats.push(value);
      };

      for (_iterator.s(); !(_step = _iterator.n()).done;) {
        var _ret = _loop();

        if (_ret === "continue") continue;
      }
    } catch (err) {
      _iterator.e(err);
    } finally {
      _iterator.f();
    }
  });
  return optionsWithStats;
}

var useStyles = (0, _styles.makeStyles)(function (theme) {
  return {
    list: {
      paddingTop: 0,
      paddingBottom: 0,
      overflowY: 'scroll'
    },
    settingsItem: {
      paddingTop: 8,
      paddingBottom: 8
    },
    listIcon: {
      minWidth: 40
    }
  };
}); // Sort presets by sort property and then by name, then filter only point presets

function presetCompare(a, b) {
  if (typeof (0, _sort.default)(a) !== 'undefined' && typeof (0, _sort.default)(b) !== 'undefined') {
    // If sort value is the same, then sort by name
    if ((0, _sort.default)(a) === (0, _sort.default)(b)) return compareStrings(a.name, b.name); // Lower sort numbers come before higher numbers
    else return (0, _sort.default)(a) - (0, _sort.default)(b);
  } else if (typeof (0, _sort.default)(a) !== 'undefined') {
    // If a has a sort field but b doesn't, a comes first
    return -1;
  } else if (typeof (0, _sort.default)(b) !== 'undefined') {
    // if b has a sort field but a doesn't, b comes first
    return 1;
  } else {
    // if neither have sort defined, compare by name
    return compareStrings(a.name, b.name);
  }
}

function compareStrings() {
  var a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
  return a.toLowerCase().localeCompare(b.toLowerCase());
}
//# sourceMappingURL=FilterPanel.js.map