"use strict";

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

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

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

var _immutable = require("immutable");

var _keyBy = _interopRequireDefault(require("lodash/keyBy"));

var _isArrayLike = _interopRequireDefault(require("lodash/isArrayLike"));

var _constants = require("@instructure/quiz-common/lib/constants");

var addQuizEntries = function addQuizEntries(state, quizEntries) {
  var quizEntriesHash = (0, _keyBy.default)(quizEntries, 'id');
  return (0, _immutable.fromJS)(quizEntriesHash);
};

var addQuizEntry = function addQuizEntry(state, quizEntry) {
  // This is sad, but because of how we handle our data in the
  // getPersistenceActions/CallHandlers all of our data will be
  // arrays coming in to the store from fetcher, even if it
  // _should_ be a single object.
  var qe = (0, _isArrayLike.default)(quizEntry) ? (0, _keyBy.default)(quizEntry, 'id') : (0, _defineProperty2.default)({}, quizEntry.id, quizEntry);
  return state.mergeDeep((0, _immutable.fromJS)(qe));
};

var deleteQuizEntry = function deleteQuizEntry(state, _ref2) {
  var id = _ref2.id;
  var deletedEntry = state.get(id);
  var stateWithoutDeletedEntry = state.delete(id); // Adjust the positions of subsequent quiz entries, to fill in the gap left
  // by the one we're deleting.

  var moved = stateWithoutDeletedEntry.filter(function (entry) {
    return entry.get('position') > deletedEntry.get('position');
  }).map(function (entry) {
    return entry.set('position', entry.get('position') - 1);
  });
  return stateWithoutDeletedEntry.merge(moved);
};

var deleteStimulusEntry = function deleteStimulusEntry(state, _ref3) {
  var id = _ref3.id,
      _ref3$childQuizEntryI = _ref3.childQuizEntryIds,
      childQuizEntryIds = _ref3$childQuizEntryI === void 0 ? [] : _ref3$childQuizEntryI;
  var stimulusEntry = state.get(id); // TODO: use deleteAll here after upgrading to immutable 4:
  // http://facebook.github.io/immutable-js/docs/#/Map/deleteAll

  var stateWithoutDeletedEntries = childQuizEntryIds.reduce(function (reduced, childQuizEntryId) {
    return reduced.delete(childQuizEntryId);
  }, state.delete(id)); // Disassociate any quiz entries that were associated with the stimulus, but
  // not deleted along with it.

  var disassociated = stateWithoutDeletedEntries.map(function (entry) {
    return entry.get('stimulusQuizEntryId') === id ? entry.set('stimulusQuizEntryId', '') : entry;
  }); // Adjust the positions of all the quiz entries that were associated with the
  // removed stimulus and were not deleted along with it, as well as all
  // subsequent entries, to fill in the gap left by the removed stimulus.

  var moved = disassociated.valueSeq().filter(function (entry) {
    return entry.get('position') > stimulusEntry.get('position');
  }).sortBy(function (entry) {
    return entry.get('position');
  }).map(function (entry, index) {
    return entry.set('position', stimulusEntry.get('position') + index);
  }).reduce(function (entries, entry) {
    return entries.set(entry.get('id'), entry);
  }, (0, _immutable.Map)());
  return disassociated.merge(moved);
};

var moveQuizEntry = function moveQuizEntry(state, _ref4) {
  var id = _ref4.id,
      position = _ref4.position,
      stimulusQuizEntryId = _ref4.stimulusQuizEntryId;
  var originalQuizEntry = state.get(id);
  var originalPosition = originalQuizEntry.get('position');
  var originalIsStimulus = originalQuizEntry.get('entryType') === 'Stimulus'; // We shouldn't be trying to move a stimulus into itself, this should
  // not happen, but let's be safe.

  if (originalIsStimulus && stimulusQuizEntryId.length != 0) {
    return state;
  }

  var updatedQuizEntry = originalQuizEntry.merge({
    stimulusQuizEntryId: stimulusQuizEntryId
  });
  var updatedState = state.set(id, updatedQuizEntry); // If the position hasn't changed, we might still be moving the entry into or
  // out of a stimulus.

  if (position === originalPosition) {
    return updatedState;
  } // Now we need to rearrange positions to account for the move
  // If the quizEntry is a stimulus,  we'll need to move its children as well


  var nestedEntries = updatedQuizEntry.get('entryType') === 'Stimulus' ? updatedState.filter(function (entry) {
    return entry.get('stimulusQuizEntryId') === id;
  }) : (0, _immutable.Map)();
  var movedEntries = nestedEntries.set(id, updatedQuizEntry);
  var maxOriginalPosition = movedEntries.map(function (entry) {
    return entry.get('position');
  }).max();

  if (position > originalPosition && position <= maxOriginalPosition) {
    // trying to move to a position currently inside itself
    // and we can't do that
    return updatedState;
  } // We're really just interested in the affected range:
  // ..., (a, b, c), x, y, ...
  // ..., x, y, (a, b, c), ...
  // where (a, b, c) are the entries we're trying to *move*, and x, y are the entries
  // that are *displaced*
  //  - The moved entries increase their index by the number of entries that are displaced
  //  - The displaced entries decrease their index by the number of entries that are moved
  // The operation is symmetric (just swap increase/decrease in the lines above)
  // Ranges
  //  - moving farther to the right in the list:
  //      moved:     [originalPosition, maxOriginalPosition]
  //      displaced: [maxOriginalPosition + 1, position]
  //      affected:  [originalPosition, position]
  //  - moving farther to the left the list:
  //      moved:     [originalPosition, maxOriginalPosition]
  //      displaced: [position, originalPosition - 1]
  //      affected:  [position, maxOriginalPosition]


  var start = Math.min(originalPosition, position);
  var end = Math.max(maxOriginalPosition, position);
  var numMoved = maxOriginalPosition - originalPosition + 1;
  var numDisplaced = end - start - numMoved + 1;
  var direction = position > originalPosition ? 1 : -1;
  return updatedState.map(function (entry) {
    var pos = entry.get('position');

    if (pos >= start && pos <= end) {
      if (pos >= originalPosition && pos <= maxOriginalPosition) {
        // moved entries
        return entry.set('position', pos + numDisplaced * direction);
      } else {
        // displaced entries
        return entry.set('position', pos - numMoved * direction);
      }
    } else {
      // out of range entries
      return entry;
    }
  });
};

var _default = function _default() {
  var state = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : (0, _immutable.Map)();
  var action = arguments.length > 1 ? arguments[1] : void 0;

  switch (action.type) {
    case _constants.ADD_QUIZ_ENTRIES:
      return addQuizEntries(state, action.payload);

    case _constants.ADD_QUIZ_ENTRY:
      return addQuizEntry(state, action.payload);

    case _constants.DELETE_QUIZ_ENTRY:
      return deleteQuizEntry(state, action.payload);

    case _constants.DELETE_STIMULUS_ENTRY:
      return deleteStimulusEntry(state, action.payload);

    case _constants.MOVE_QUIZ_ENTRY:
      return moveQuizEntry(state, action.payload);

    default:
      return state;
  }
};

exports.default = _default;