"use strict";

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

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.removeChoiceFromCategory = exports.removeCategory = exports.removeAllChoices = exports.rearrangeChoices = exports.moveChoiceToCategory = exports.moveChoiceToAlternate = exports.limitInArrays = exports.limitInArray = exports.limitChoices = exports.getBuiltCategories = exports.getAllPossibleResponses = exports.ensureNoExtraChoicesInAnswer = exports.ensureNoExtraChoicesInAlternate = exports.countInChoices = exports.countInAnswer = exports.countChosen = exports.buildState = exports.buildChoices = exports.buildCategories = exports.FooTwo = void 0;
Object.defineProperty(exports, "score", {
  enumerable: true,
  get: function get() {
    return _scoring.score;
  }
});
exports.verifyAllowMultiplePlacements = exports.stillSelectable = void 0;

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

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

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

var _clone = _interopRequireDefault(require("lodash/clone"));

var _remove = _interopRequireDefault(require("lodash/remove"));

var _scoring = require("./scoring");

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 FooTwo = 'foo-two';
exports.FooTwo = FooTwo;
var log = (0, _debug["default"])('@pie-lib:categorize');

var limitChoices = function limitChoices(choiceId, count, choices) {
  var out = choices.reduce(function (acc, id) {
    var foundCount = countInChoices(choiceId, acc.choices);

    if (id !== choiceId || foundCount < count) {
      acc.choices.push(id);
    }

    return acc;
  }, {
    choices: []
  });
  return out.choices;
};
/**
 * Limit in an array of arrays.
 * So for an array with [[1,1],[1,1]] if you call limitInArray(1, arr, 3) you'll get: [[1,1], [1]]
 * @param {*} id
 * @param {*} arrays
 * @param {*} limit
 */


exports.limitChoices = limitChoices;

var limitInArrays = function limitInArrays(id, arrays, limit) {
  arrays = arrays || [];
  var result = arrays.reduce(function (acc, array) {
    var l = limit - acc.count;
    var result = limitInArray(id, array, l);
    acc.out.push(result.array);
    acc.count = acc.count + (result.count || 0);
    return acc;
  }, {
    out: [],
    count: 0
  });
  return result.out;
};
/**
 * Limit the id in the array to the limit set.
 * So for an array with [1,1,1,1] if you call limitInArray(1, arr, 2) you'll get: [1,1]
 * @param {string} id
 * @param {string[]} arr
 * @param {number} limit
 */


exports.limitInArrays = limitInArrays;

var limitInArray = function limitInArray(id, arr, limit) {
  arr = arr || [];

  if (limit === 0) {
    var stripped = arr.filter(function (v) {
      return v !== id;
    });
    return {
      array: stripped,
      count: arr.length - stripped.length
    };
  } else {
    var result = arr.reduce(function (acc, v) {
      if (v === id) {
        if (acc.count < limit) {
          acc.count += 1;
          acc.out.push(v);
        }
      } else {
        acc.out.push(v);
      }

      return acc;
    }, {
      out: [],
      count: 0
    });
    return {
      array: result.out,
      count: result.count
    };
  }
};
/**
 * Ensure that there are no extra choices in answer, if a choice.categoryCount is > 0.
 * @param {{category: string, choices: string[]}[]} answer
 * @param {{id:string,categoryCount:number}} choices
 */


exports.limitInArray = limitInArray;

var ensureNoExtraChoicesInAnswer = function ensureNoExtraChoicesInAnswer(answer, choices) {
  choices = choices || [];
  var out = choices.reduce(function (answerArray, choice) {
    log('choice: ----> ', choice.id, 'categoryCount: ', choice.categoryCount);

    if (choice.categoryCount === undefined || choice.categoryCount === 0) {
      return answerArray;
    } else {
      var _choices = answerArray.map(function (a) {
        return a.choices;
      });

      var result = limitInArrays(choice.id, _choices, choice.categoryCount);
      var updatedArray = result.map(function (r, index) {
        return {
          category: answerArray[index].category,
          alternateResponses: answerArray[index].alternateResponses,
          choices: r
        };
      });
      return updatedArray;
    }
  }, answer);
  return out;
};
/**
 * Ensure that there are no extra choices in alternate responses, if a choice.categoryCount is > 0.
 * @param {{category: string, choices: string[]}[]} answer
 * @param {{id:string,categoryCount:number}} choices
 */


exports.ensureNoExtraChoicesInAnswer = ensureNoExtraChoicesInAnswer;

var ensureNoExtraChoicesInAlternate = function ensureNoExtraChoicesInAlternate(answer, choices) {
  choices = choices || [];
  var out = choices.reduce(function (answerArray, choice) {
    log('choice: ----> ', choice.id, 'categoryCount: ', choice.categoryCount);

    if (choice.categoryCount === undefined || choice.categoryCount === 0) {
      return answerArray;
    } else {
      var alternatePair = {};
      answerArray.forEach(function (answer) {
        return (answer.alternateResponses || []).forEach(function (alternate, index) {
          if (index in alternatePair) {
            alternatePair[index].push(alternate);
          } else {
            alternatePair[index] = [];
            alternatePair[index].push(alternate);
          }
        });
      });
      var recreatedAlternate = [];
      Object.keys(alternatePair).forEach(function (k) {
        var limitAlternate = limitInArrays(choice.id, alternatePair[k], choice.categoryCount);
        alternatePair[k] = limitAlternate;
        alternatePair[k].forEach(function (item, index) {
          if (!recreatedAlternate[index]) {
            recreatedAlternate[index] = [];
            recreatedAlternate[index].push(item);
          } else {
            recreatedAlternate[index].push(item);
          }
        });
      });
      var updatedArray = answerArray.map(function (answer, index) {
        return {
          category: answer.category,
          alternateResponses: recreatedAlternate[index] || answer.alternateResponses,
          choices: answer.choices
        };
      });
      return updatedArray;
    }
  }, answer);
  return out;
};
/**
 * Count the number of choice ids in a given answer array
 * @param {string} choiceId
 * @param {{category: string, choices: string[]}[]} answer
 */


exports.ensureNoExtraChoicesInAlternate = ensureNoExtraChoicesInAlternate;

var countInAnswer = function countInAnswer(choiceId, answer) {
  var out = answer.reduce(function (acc, a) {
    return acc + countInChoices(choiceId, a.choices);
  }, 0);
  log('[countInAnswer] choiceId:', choiceId, answer);
  return out;
};

exports.countInAnswer = countInAnswer;

var countInChoices = function countInChoices(choiceId, choices) {
  return (choices || []).filter(function (c) {
    return c === choiceId;
  }).length;
};
/**
 * Count the number of times a choice has been selected in categories.
 * @param {*} choice
 * @param {*} categories
 */


exports.countInChoices = countInChoices;

var countChosen = function countChosen(choice, categories) {
  if (!choice || !choice.id) {
    return 0;
  }

  if (!Array.isArray(categories)) {
    return 0;
  }

  return categories.reduce(function (acc, c) {
    var count = (c.choices || []).filter(function (h) {
      return h && h.id === choice.id;
    }).length;
    return acc + count;
  }, 0);
};
/**
 * @param {string?} categoryId
 * @param {{category:string, choices: string[]}} answers
 */


exports.countChosen = countChosen;

var removeCategory = function removeCategory(categoryId, answers) {
  return answers.filter(function (a) {
    return a.category !== categoryId;
  });
};
/**
 * @param {string} choiceId
 * @param {{category:string, choices: string[]}} answers
 * @param {string?} categoryId - optional categegory id
 */


exports.removeCategory = removeCategory;

var removeAllChoices = function removeAllChoices(choiceId, answers, categoryId) {
  return answers.map(function (a) {
    if (!categoryId || a.category === categoryId) {
      var cloned = (0, _clone["default"])(a.choices);
      (0, _remove["default"])(cloned, function (v) {
        return v === choiceId;
      });
      return _objectSpread(_objectSpread({}, a), {}, {
        choices: cloned
      });
    } else {
      return a;
    }
  });
};

exports.removeAllChoices = removeAllChoices;

var rearrangeChoices = function rearrangeChoices(choices, indexFrom, indexTo) {
  if (choices.length === 0) {
    return choices;
  }

  choices.splice(indexFrom, 1, choices.splice(indexTo, 1, choices[indexFrom])[0]);
  return choices;
};

exports.rearrangeChoices = rearrangeChoices;

var verifyAllowMultiplePlacements = function verifyAllowMultiplePlacements(choice, categoryId, answers) {
  return answers.map(function (a) {
    if (a.category !== categoryId) {
      a.choices = (a.choices || []).filter(function (c) {
        return c !== choice.id;
      });
      return a;
    } else {
      a.choices = a.choices.reduce(function (acc, currentValue) {
        if (currentValue.id === choice.id) {
          var foundIndex = acc.findIndex(function (c) {
            return c.id === choice.id;
          });

          if (foundIndex === -1) {
            acc.push(currentValue);
          }
        } else {
          acc.push(currentValue);
        }

        return acc;
      }, []);
      return a;
    }
  });
};

exports.verifyAllowMultiplePlacements = verifyAllowMultiplePlacements;

var removeChoiceFromCategory = function removeChoiceFromCategory(choiceId, categoryId, choiceIndex, answers) {
  log('[removeChoiceFromCategory] choiceIndex:', choiceIndex);
  return answers.map(function (a) {
    if (a.category === categoryId) {
      var cloned = (0, _clone["default"])(a.choices);
      var index = cloned.findIndex(function (v, index) {
        return v === choiceId && index >= choiceIndex;
      });

      if (index !== -1) {
        cloned.splice(index, 1);
      }

      return _objectSpread(_objectSpread({}, a), {}, {
        choices: cloned
      });
    } else {
      return a;
    }
  });
};

exports.removeChoiceFromCategory = removeChoiceFromCategory;

var moveChoiceToCategory = function moveChoiceToCategory(choiceId, from, to, choiceIndex, answers) {
  log('[moveChoiceToCategory] choice: ', choiceId, 'from: ', from, 'to: ', to, 'answers: ', answers);

  if (from === to) {
    return answers;
  }

  if (from) {
    answers = removeChoiceFromCategory(choiceId, from, choiceIndex, answers);
  }

  var index = answers.findIndex(function (a) {
    return a.category === to;
  });

  if (index === -1) {
    answers.push({
      category: to,
      choices: [choiceId]
    });
    return answers;
  } else {
    return answers.map(function (a) {
      if (a.category === to) {
        a.choices = a.choices || [];
        a.choices.push(choiceId);
        return a;
      }

      return a;
    });
  }
};

exports.moveChoiceToCategory = moveChoiceToCategory;

var moveChoiceToAlternate = function moveChoiceToAlternate(choiceId, from, to, choiceIndex, answers, alternateIndex, categoryCount) {
  log('[moveChoiceToAlternate] choice: ', choiceId, 'from: ', from, 'to: ', to, 'answers: ', answers, 'chocieIndex: ', choiceIndex);

  if (from === to) {
    return answers;
  }

  return answers.map(function (a) {
    if (a.category === to) {
      if (categoryCount !== 0 && a.alternateResponses) {
        a.alternateResponses[alternateIndex] = (a.alternateResponses[alternateIndex] || []).filter(function (resp) {
          return resp !== choiceId;
        });
      }

      if (!a.alternateResponses) {
        a.alternateResponses = [];
        a.alternateResponses[alternateIndex] = [];
      }

      a.alternateResponses[alternateIndex].push(choiceId);
    }

    if (a.category === from && categoryCount !== 0 && a.alternateResponses && a.alternateResponses[alternateIndex]) {
      a.alternateResponses[alternateIndex] = a.alternateResponses[alternateIndex].filter(function (resp) {
        return resp !== choiceId;
      });
    }

    return a;
  });
};

exports.moveChoiceToAlternate = moveChoiceToAlternate;

var stillSelectable = function stillSelectable(h, builtCategories) {
  if (h.categoryCount > 0) {
    var count = countChosen(h, builtCategories);
    return count < h.categoryCount;
  } else {
    return true;
  }
};
/**
 * returns all choices with property 'correct' set to boolean
 * @param {string[]} possibleResponseChoices
 * @param {Object[]} builtCategoryChoices
 *
 * @returns {Object}, returns builtChoices: BuiltChoice[] and allChoicesAreCorrect: boolean
 * BuiltChoice = { id: choiceId, content: choiceContent, correct: boolean }
 */


exports.stillSelectable = stillSelectable;

var buildChoices = function buildChoices(possibleResponseChoices, builtCategoryChoices) {
  return builtCategoryChoices.reduce(function (acc, builtChoice) {
    // set correct value on each choice that was selected by user
    var index = acc.copyOfPossibleResponse.findIndex(function (cRC) {
      return builtChoice && cRC === builtChoice.id;
    }); // if the choice exists in the correct response
    // set the correct: true

    if (index >= 0) {
      acc.builtChoices.push(_objectSpread(_objectSpread({}, builtChoice), {}, {
        correct: true
      }));
      acc.copyOfPossibleResponse = [].concat((0, _toConsumableArray2["default"])(acc.copyOfPossibleResponse.slice(0, index)), (0, _toConsumableArray2["default"])(acc.copyOfPossibleResponse.slice(index + 1)));
    } else {
      acc.builtChoices.push(_objectSpread(_objectSpread({}, builtChoice), {}, {
        correct: false
      }));
      acc.allChoicesAreCorrect = false;
    }

    return acc;
  }, {
    builtChoices: [],
    copyOfPossibleResponse: (0, _toConsumableArray2["default"])(possibleResponseChoices),
    allChoicesAreCorrect: true
  });
};
/**
 * returns corrected answer using this possible correct response
 * @param {Object} possibleResponse, has form: { CategoryId: ChoiceId[] }
 * @param {Object[]} builtCategories
 *
 * @returns {Object}, returns builtCategories: BuiltCategory[] and correct: boolean
 * BuiltCategory = { id: categoryId, label: categoryLabel, correct: boolean, choices: BuiltChoice[] }
 * BuiltChoice = { id: choiceId, content: choiceContent, correct: boolean }
 */


exports.buildChoices = buildChoices;

var getBuiltCategories = function getBuiltCategories(possibleResponse, builtCategories) {
  return builtCategories.reduce(function (acc, builtCategory) {
    var possibleResponseChoices = possibleResponse[builtCategory.id] || []; // the correct choices for this category

    var builtCategoryChoices = builtCategory.choices || []; // the choices in the answer

    var _buildChoices = buildChoices(possibleResponseChoices, builtCategoryChoices),
        builtChoices = _buildChoices.builtChoices,
        allChoicesAreCorrect = _buildChoices.allChoicesAreCorrect;

    var allChoicesAreInAnswer = builtCategoryChoices.length === possibleResponseChoices.length;
    acc.builtCategories.push(_objectSpread(_objectSpread({}, builtCategory), {}, {
      correct: allChoicesAreCorrect && allChoicesAreInAnswer,
      choices: builtChoices
    }));
    acc.correct = acc.correct && allChoicesAreCorrect && allChoicesAreInAnswer;
    return acc;
  }, {
    builtCategories: [],
    correct: true
  });
};
/**
 * returns all the possible responses by combining the proper alternate responses
 * @param {Object[]} correctResponse
 *
 * @returns {Object[]}, returns Response[]
 * Response = { CategoryId: ChoiceId[] },
 */


exports.getBuiltCategories = getBuiltCategories;

var getAllPossibleResponses = function getAllPossibleResponses(correctResponse) {
  if (!correctResponse) {
    return [];
  }

  return correctResponse.reduce(function (acc, cR) {
    // main response
    if (acc[0]) {
      acc[0][cR.category] = cR.choices;
    } else {
      acc.push((0, _defineProperty2["default"])({}, cR.category, cR.choices));
    } // alternate responses


    if (cR.alternateResponses) {
      cR.alternateResponses.forEach(function (aR, index) {
        if (acc[index + 1]) {
          acc[index + 1][cR.category] = aR;
        } else {
          acc.push((0, _defineProperty2["default"])({}, cR.category, aR));
        }
      });
    }

    return acc;
  }, []);
};
/**
 * build the categories to be displayed, where each category has its own list of choices
 * @param {Object[]} categories
 * @param {{id:string}[]} choices
 * @param {{category: string, choices:string[]}[]} answers
 *
 * @returns {Object[]}, returns BuiltCategory[]
 * BuiltCategory = { id: categoryId, label: categoryLabel, choices: BuiltChoice[]},
 * BuiltChoice = { id: choiceId, content: choiceContent }
 */


exports.getAllPossibleResponses = getAllPossibleResponses;

var buildCategories = function buildCategories(categories, choices, answers) {
  categories = categories || [];
  answers = answers || [];
  choices = choices || [];
  return categories.map(function (category) {
    var answer = answers.find(function (answer) {
      return answer.category === category.id;
    });

    var _ref = answer || {},
        _ref$choices = _ref.choices,
        answerChoices = _ref$choices === void 0 ? [] : _ref$choices;

    return _objectSpread(_objectSpread({}, category), {}, {
      choices: answerChoices.map(function (chId) {
        return choices.find(function (ch) {
          return ch.id === chId;
        });
      })
    });
  });
};
/**
 * build the choice and category state
 * @param {Object[]} categories
 * @param {{id:string}[]} choices
 * @param {{category: string, choices:string[]}[]} answers
 *
 * @param correctResponse
 * @returns { categories: Category[], choices: Choice[], correct: boolean, bestResponse: Response }, where Response: { categoryId: choiceId[] }
 */


exports.buildCategories = buildCategories;

var buildState = function buildState(categories, choices, answers, correctResponse) {
  categories = categories || [];
  choices = choices || [];
  answers = answers || [];
  var allResponses = getAllPossibleResponses(correctResponse) || [];
  var builtCategories = buildCategories(categories, choices, answers);
  var bestResponse;
  var entirelyCorrect; // at least one defined correct response

  if (allResponses.length) {
    var builtData = allResponses.reduce(function (acc, possibleResponse) {
      // set correctness for each possible response
      var _getBuiltCategories = getBuiltCategories(possibleResponse, builtCategories),
          categoriesWithChoices = _getBuiltCategories.builtCategories,
          correct = _getBuiltCategories.correct; // if bestResponse not found yet OR no correct response found yet and this one is the correct one


      if (acc.bestResponse === null || !acc.entirelyCorrect && correct) {
        return {
          bestResponse: possibleResponse,
          builtCategories: categoriesWithChoices,
          entirelyCorrect: correct
        };
      }

      return acc;
    }, {
      bestResponse: null,
      builtCategories: [],
      entirelyCorrect: false
    });
    builtCategories = builtData.builtCategories;
    bestResponse = builtData.bestResponse;
    entirelyCorrect = builtData.entirelyCorrect;
  }

  var filteredChoices = choices.map(function (ch) {
    return stillSelectable(ch, builtCategories) ? ch : {
      empty: true
    };
  });
  return {
    choices: filteredChoices,
    categories: builtCategories,
    correct: entirelyCorrect,
    bestResponse: bestResponse
  };
};

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