"use strict";

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

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getScore = exports.createCorrectResponseSession = void 0;
exports.model = model;
exports.normalize = void 0;
exports.outcome = outcome;
exports.validate = exports.prepareVal = exports.prepareChoice = void 0;

var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));

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

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

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

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

var _debug = _interopRequireDefault(require("debug"));

var _map = _interopRequireDefault(require("lodash/map"));

var _reduce = _interopRequireDefault(require("lodash/reduce"));

var _find = _interopRequireDefault(require("lodash/find"));

var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));

var _controllerUtils = require("@pie-lib/pie-toolbox/controller-utils");

var _translator = _interopRequireDefault(require("@pie-lib/pie-toolbox/translator"));

var _defaults = _interopRequireDefault(require("./defaults"));

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

var translator = _translator["default"].translator;
var log = (0, _debug["default"])('explicit-constructed-response:controller');

var prepareChoice = function prepareChoice(mode, defaultFeedback) {
  return function (choice) {
    var out = {
      label: choice.label,
      value: choice.value
    };

    if (mode === 'evaluate') {
      out.correct = true;
      var feedbackType = choice.feedback && choice.feedback.type || 'none';

      if (feedbackType === 'default') {
        out.feedback = defaultFeedback['correct'];
      } else if (feedbackType === 'custom') {
        out.feedback = choice.feedback.value;
      }
    }

    return out;
  };
};

exports.prepareChoice = prepareChoice;

var getFeedback = function getFeedback(value) {
  if (value) {
    return 'correct';
  }

  return 'incorrect';
}; // also used in configure/src/markupUtils.js


var getAdjustedLength = function getAdjustedLength(length) {
  if (length <= 2) {
    return length + 2;
  }

  if (length <= 4) {
    return length + 3;
  }

  if (length <= 6) {
    return length + 4;
  }

  return length + 5;
};

var normalize = function normalize(question) {
  return _objectSpread(_objectSpread({}, _defaults["default"]), question);
};
/**
 *
 * @param {*} question
 * @param {*} session
 * @param {*} env
 */


exports.normalize = normalize;

function model(question, session, env) {
  return new Promise( /*#__PURE__*/function () {
    var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(resolve) {
      var normalizedQuestion, defaultFeedback, prepareChoiceFn, choices, _ref2, _ref2$value, value, feedback, showNote, note, _normalizedQuestion$m, maxLengthPerChoice, maxLengthPerChoiceEnabled, undefinedLengths, out;

      return _regenerator["default"].wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              // this was added to treat an exception, when the model has choices without
              // the "value" property like: { label: 'test' }
              if (question.choices) {
                Object.keys(question.choices).forEach(function (key) {
                  question.choices[key] = (question.choices[key] || []).map(function (item, index) {
                    if (!item.value) {
                      log('Choice does not contain "value" property, which is required.', item);
                      return _objectSpread({
                        value: "".concat(index)
                      }, item);
                    }

                    return item;
                  });
                });
              }

              normalizedQuestion = normalize(question);
              defaultFeedback = Object.assign({
                correct: 'Correct',
                incorrect: 'Incorrect'
              }, normalizedQuestion.defaultFeedback);
              prepareChoiceFn = prepareChoice(env.mode, defaultFeedback);
              choices = (0, _reduce["default"])(normalizedQuestion.choices, function (obj, area, key) {
                obj[key] = (0, _map["default"])(area, prepareChoiceFn);
                return obj;
              }, {});
              _ref2 = session || {}, _ref2$value = _ref2.value, value = _ref2$value === void 0 ? {} : _ref2$value;
              feedback = env.mode === 'evaluate' ? (0, _reduce["default"])(normalizedQuestion.choices, function (obj, respArea, key) {
                var chosenValue = value && value[key];
                var val = !(0, _isEmpty["default"])(chosenValue) && (0, _find["default"])(respArea, function (c) {
                  return prepareVal(c.label) === prepareVal(chosenValue);
                });
                obj[key] = getFeedback(val);
                return obj;
              }, {}) : {}; // check if at least one choice has an alternate

              showNote = Object.values(choices).some(function (choice) {
                return (choice === null || choice === void 0 ? void 0 : choice.length) > 1;
              });
              note = normalizedQuestion.note || translator.t('common:commonCorrectAnswerWithAlternates', {
                lng: normalizedQuestion.language
              });
              _normalizedQuestion$m = normalizedQuestion.maxLengthPerChoice, maxLengthPerChoice = _normalizedQuestion$m === void 0 ? [] : _normalizedQuestion$m, maxLengthPerChoiceEnabled = normalizedQuestion.maxLengthPerChoiceEnabled;
              undefinedLengths = !maxLengthPerChoice.length; // calculate maxLengthPerChoice array if it is not defined or defined incorrectly

              Object.values(choices).forEach(function (choice, index) {
                var labelLengthsArr = (choice || []).map(function (choice) {
                  return (choice.label || '').length;
                });
                var length = Math.max.apply(Math, (0, _toConsumableArray2["default"])(labelLengthsArr));

                if (undefinedLengths || !maxLengthPerChoice[index] || maxLengthPerChoice[index] < length || maxLengthPerChoice[index] > length + 10) {
                  maxLengthPerChoice[index] = getAdjustedLength(length);
                }
              });
              out = {
                choices: choices,
                disabled: env.mode !== 'gather',
                displayType: normalizedQuestion.displayType,
                mode: env.mode,
                role: env.role,
                feedback: feedback,
                language: normalizedQuestion.language,
                markup: normalizedQuestion.markup,
                maxLengthPerChoice: maxLengthPerChoice,
                maxLengthPerChoiceEnabled: maxLengthPerChoiceEnabled,
                note: note,
                playerSpellCheckEnabled: normalizedQuestion.playerSpellCheckEnabled,
                prompt: normalizedQuestion.promptEnabled ? normalizedQuestion.prompt : _defaults["default"].prompt,
                rationale: _defaults["default"].rationale,
                responseCorrect: env.mode === 'evaluate' ? getScore(normalizedQuestion, session) === 1 : undefined,
                showNote: showNote,
                teacherInstructions: _defaults["default"].teacherInstructions
              };

              if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {
                out.rationale = normalizedQuestion.rationaleEnabled ? normalizedQuestion.rationale : _defaults["default"].rationale;
                out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled ? normalizedQuestion.teacherInstructions : _defaults["default"].teacherInstructions;
              }

              resolve(out);

            case 15:
            case "end":
              return _context.stop();
          }
        }
      }, _callee);
    }));

    return function (_x) {
      return _ref.apply(this, arguments);
    };
  }());
}

var prepareVal = function prepareVal(html) {
  return getInnerText(html).trim();
};

exports.prepareVal = prepareVal;

var getScore = function getScore(config, session) {
  var _ref3 = session || {},
      value = _ref3.value;

  if (!session || (0, _isEmpty["default"])(session) || !value) {
    return 0;
  }

  var responseAreas = config.markup && config.markup.match(/\{\{(.)\}\}/g);
  var maxScore = responseAreas ? responseAreas.length : 0;
  var correctCount = (0, _reduce["default"])(config.choices, function (total, respArea, key) {
    var chosenValue = value && value[key];

    if ((0, _isEmpty["default"])(chosenValue) || !(0, _find["default"])(respArea, function (c) {
      return prepareVal(c.label) === prepareVal(chosenValue);
    })) {
      return total;
    }

    return total + 1;
  }, 0);
  var str = maxScore ? (correctCount / maxScore).toFixed(2) : 0;
  return parseFloat(str);
};
/**
 * The score is partial by default for checkbox mode, allOrNothing for radio mode.
 * To disable partial scoring for checkbox mode you either set model.partialScoring = false or env.partialScoring =
 * false. the value in `env` will override the value in `model`.
 * @param {Object} model - the main model
 * @param {boolean} model.partialScoring - is partial scoring enabled (if undefined set to to true)
 * @param {*} session
 * @param {Object} env
 * @param {boolean} env.partialScoring - is partial scoring enabled (if undefined default to true) This overrides
 *   `model.partialScoring`.
 */


exports.getScore = getScore;

function outcome(model, session) {
  var env = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  return new Promise(function (resolve) {
    var partialScoringEnabled = _controllerUtils.partialScoring.enabled(model, env);

    var score = getScore(model, session);
    resolve({
      score: partialScoringEnabled ? score : score === 1 ? 1 : 0,
      empty: (0, _isEmpty["default"])(session)
    });
  });
}

var createCorrectResponseSession = function createCorrectResponseSession(question, env) {
  return new Promise(function (resolve) {
    if (env.mode !== 'evaluate' && env.role === 'instructor') {
      var choices = question.choices;
      var value = {};
      Object.keys(choices).forEach(function (key, i) {
        value[i] = choices[key][0].label;
      });
      resolve({
        id: '1',
        value: value
      });
    } else {
      resolve(null);
    }
  });
}; // remove all html tags
// const getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');


exports.createCorrectResponseSession = createCorrectResponseSession;

var getInnerText = function getInnerText(html) {
  if (typeof html !== 'string') {
    return '';
  }

  if (typeof html.replaceAll === 'function') {
    return html.replaceAll(/<[^>]*>/g, '');
  } else {
    // Polyfill for replaceAll using replace and a global regex
    return html.replace(/<[^>]*>/g, '');
  }
}; // remove all html tags except img and iframe


var getContent = function getContent(html) {
  return (html || '').replace(/(<(?!img|iframe)([^>]+)>)/gi, '');
};

var validate = function validate() {
  var model = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  var choices = model.choices,
      markup = model.markup;
  var maxResponseAreas = config.maxResponseAreas;
  var allChoicesErrors = {};
  var errors = {};
  ['teacherInstructions', 'prompt', 'rationale'].forEach(function (field) {
    var _config$field;

    if ((_config$field = config[field]) !== null && _config$field !== void 0 && _config$field.required && !getContent(model[field])) {
      errors[field] = 'This field is required.';
    }
  });
  Object.entries(choices || {}).forEach(function (_ref4) {
    var _ref5 = (0, _slicedToArray2["default"])(_ref4, 2),
        key = _ref5[0],
        values = _ref5[1];

    var reversedChoices = (0, _toConsumableArray2["default"])(values || []).reverse();
    var choicesErrors = {};
    reversedChoices.forEach(function (choice, index) {
      var value = choice.value,
          label = choice.label;

      if (label === '' || label === '<div></div>') {
        choicesErrors[value] = 'Content should not be empty.';
      } else {
        var identicalAnswer = reversedChoices.slice(index + 1).some(function (c) {
          return c.label === label;
        });

        if (identicalAnswer) {
          choicesErrors[value] = 'Content should be unique.';
        }
      }
    });

    if (!(0, _isEmpty["default"])(choicesErrors)) {
      allChoicesErrors[key] = choicesErrors;
    }
  });
  var nbOfResponseAreas = (markup.match(/\{\{(\d+)\}\}/g) || []).length;

  if (nbOfResponseAreas > maxResponseAreas) {
    errors.responseAreas = "No more than ".concat(maxResponseAreas, " response areas should be defined.");
  } else if (nbOfResponseAreas < 1) {
    errors.responseAreas = 'There should be at least 1 response area defined.';
  }

  if (!(0, _isEmpty["default"])(allChoicesErrors)) {
    errors.choices = allChoicesErrors;
  }

  return errors;
};

exports.validate = validate;
//# sourceMappingURL=index.js.map