Source: user.js

const { CLAStage } = require('./constants');

/**
 * Class representing a user.  This is unrelated to the People table.
 */
class User {

  /**
   * Unique ID for the user
   * @type {string}
   * @default ''
   */
  _id = '';

  /**
   * User's common name
   * @type {string}
   * @default ''
   */
  name = '';

  /**
   * User's full name
   * @type {string}
   * @default ''
   */
  fullName = '';

  /**
   * Filename of a picture of the user
   * @type {string}
   * @default ''
   */
  photoFilename = '';

  /**
   * User's current overall unit in CLA (1-based).  There are 26 total units, including Unit 1 (Warmup) and Unit 26 (Wrapup).
   * @type {number}
   * @default 1
   */
  claUnit = 1;

  /**
   * Array of the last overall claUnit for each stage, from warmup (index 0) through the end of CLA
   */
  static lastUnits = [1, 5, 11, 17, 25, 26];

  /**
   * for a given stage enum value, returns an array of all unit numbers in that stage
   * @param {CLAStage} claStage
   * @returns {number[]}
   */
  static unitsForStage(claStage) {
    const stageNum = this.getClaStageNumber(claStage);

    if (stageNum === 0) return [1]; // warm-up has only unit #1

    const stageLastUnit = this.lastUnits[stageNum];
    const prevStageLastUnit = this.lastUnits[stageNum - 1];
    return Array.from(Array(stageLastUnit - prevStageLastUnit).keys(), u => u + prevStageLastUnit + 1);
  }

  /**
   * converts a CLA stage number, which is its index into this.lastUnits, to its CLAStage enum value
   * @param {number} stageNum
   * @returns {CLAStage|string}
   */
  static getClaStageEnum(stageNum) {
    switch (stageNum) {
      case 0:
        return CLAStage.WARMUP;
      case 1:
        return CLAStage.STAGE_1;
      case 2:
        return CLAStage.STAGE_2;
      case 3:
        return CLAStage.STAGE_3;
      case 4:
        return CLAStage.STAGE_4;
      case 5:
        return CLAStage.WRAPUP;
    }
  }

  /**
   * convert the CLAStage enum value to its stage number, which is its index into this.lastUnits
   * @param {CLAStage} userClaStage
   * @returns {number}
   */
  static getClaStageNumber(userClaStage) {
    switch (userClaStage) {
      case CLAStage.WARMUP:
        return 0;
      case CLAStage.STAGE_1:
        return 1;
      case CLAStage.STAGE_2:
        return 2;
      case CLAStage.STAGE_3:
        return 3;
      case CLAStage.STAGE_4:
        return 4;
      case CLAStage.WRAPUP:
        return 5;
    }
  }

  /**
   * convert a stage enum and unit within that stage to the overall claUnit
   * @param {CLAStage|string} stageEnum
   * @param {number} stageUnit
   * @returns {number}
   */
  static getOverallClaUnit(stageEnum, stageUnit) {
    const stageNumber = this.getClaStageNumber(stageEnum);
    return stageNumber === 0 ? stageUnit : this.lastUnits[stageNumber - 1] + stageUnit;
  }

  /**
   * Given a claUnit (overall unit number), computes:
   * - the unit within the stage.  This number resets to 1 when a user moves to a new stage.
   * - the stage number
   * - the CLAStage enum string that corresponds to the stage number
   * @param {number} claUnit
   * @returns {{stageEnum: (CLAStage|string), stageNumber: number, stageUnit: number}}
   */
  static getClaStage(claUnit) {
    const stageNumber = this.lastUnits.findIndex(u => u >= claUnit);
    const stageEnum = this.getClaStageEnum(stageNumber);
    const prevStageLastUnit = stageNumber === 0 ? 0 : this.lastUnits[stageNumber - 1];
    const stageUnit = claUnit - prevStageLastUnit;

    return { stageEnum, stageNumber, stageUnit };
  }

  /**
   * Gets the maximum stage unit (not the overall cla unit) for the stage specified by the enum.  The minimum is always 1 for every stage.
   * @param {CLAStage|string} stageEnum
   * @returns {number}
   */
  static getMaxStageUnit(stageEnum) {
    const stageNumber = this.getClaStageNumber(stageEnum);
    const prevStageMaxClaUnit = stageEnum === CLAStage.WARMUP ? 0 : this.lastUnits[stageNumber - 1];
    const maxClaUnit = this.lastUnits[stageNumber];
    return maxClaUnit - prevStageMaxClaUnit;
  }

  /**
   * Creates a user object
   * @param {User|Object} data
   */
  constructor(data = {}) {
    for(const key of Object.keys(data)) {
      this[key] = data[key];
    }
  }

  /**
   * Creates an updated user object
   * @param {User|Object} data
   * @returns {User}
   */
  set(data) {
    return new User({
      ...this,
      ...data,
    });
  }
}

module.exports = User;