import { PlayTTSService } from "src/app/services/play-tts.service";
import { GlobalService } from "src/app/services/global.service";
import { addWeeks, format, parseISO } from "date-fns";
import { Subscription } from "rxjs";
import { Fraction } from "../app-utils";
import { JeuJustePointPage, justePointActivity } from "../page/jeu-juste-point/jeu-juste-point.page";
import { CabriDataService } from "../services/cabri-data.service";
import { LmsService } from "../services/lms.service";
import { CabriIntegrationJeuJustePoint, ResultOnTheRoad, UserAnswer } from "./cabri-integration-jeujustepoint";
import { LrsUtils } from "./lrs/lrsUtils";
import { ScenarioJeuJustePoint } from "./scenario-jeu-juste-point";
import { AnswerNeedsHelp, AnswerStatus } from "./activity-answer";
import { AwardsType } from "./enums/awards";
import { BabylonGabaritIntegrationDroiteGraduee } from "./cabri-integration-droitegraduee";
import { DroiteGradueePage } from "../pages/droite-graduee/droite-graduee.page";
import { ScenarioDroiteGraduee } from "./scenario-droite-graduee";
declare var window: {
store: any;
document: any;
innerWidth: any;
innerHeight: any;
outerWidth;
outerHeight;
dispatchEvent: any;
CabriSceneBuilder: any;
SceneUpdater: any;
Hud: any;
Globals: any;
params: any;
webGLCleanup: any;
URL: any;
};
export enum ResultType {
success,
successOnRetry,
fail,
failOnFirstAttempt
}
export class ExercicesDroiteGraduee {
exoType: justePointActivity;
public answerSubscription: Subscription;
spacedRepetitonDate: any;
consigneTTS: string;
constructor(
public cabri: BabylonGabaritIntegrationDroiteGraduee,
public page: DroiteGradueePage,
public scenario: ScenarioDroiteGraduee,
public cabriService: CabriDataService,
public lmsService: LmsService,
public globalService: GlobalService,
public ttsService: PlayTTSService
) {}
updateVariables()
{
this.cabriService.currentActivity.updateVariables(this.cabriService.getCurrentExercice());
this.page.variable = this.cabriService.currentActivity.currentVariables;
}
getConsigneTTS(secondTry?: boolean) {
const virgule = $localize`virgule`;
const kilometre = $localize`kilomètre`;
switch (this.exoType) {
case justePointActivity.basket:
this.consigneTTS = this.cabri
.getConsigneExerciceBasketTTS(secondTry)
.reduce((p, n) => p + "!" + n)
.replace(/\./g, " "+ virgule +" ");
break;
case justePointActivity.onTheRoad:
// injected in cabri.buildElementActivityOnTheRoad()
break;
}
}
/**
* Scenario of the first bad response
*/
firstBadResponseScenario(answerStatus: AnswerStatus, responseTimeInSeconds: number, award?: string, help?: AnswerNeedsHelp) {
let currentScenario: Promise<void>;
if (Number(this.cabriService.currentActivity.variables["v-cursor-mod"]) === 2) {
// MODE DéFI
currentScenario = this.scenario.eliminationChallengeMode();
} else {
currentScenario = this.scenario.firstTryFailed();
}
return currentScenario;
}
checkChallengeModeRules() {
if (this.page.currentTeam.length === 1) {
this.page.endActivity();
} else if (this.page.currentTeam.length > 1) {
this.page.currentEliminatedPlayerId = Number(this.page.currentUser.playerId);
this.page.newQuestion();
}
}
get isChallengeMode() {
return Number(this.cabriService.currentActivity.variables["v-cursor-mod"]) === 2;
}
/**
* Promises of scenario and mascotte animation
*/
execScenarioMathiaAnim(promiseAnim: Promise<any>, promiseScenario: Promise<any>, callback?): Promise<void> {
return new Promise<void>(resolve => {
Promise.all([promiseAnim, promiseScenario]).then(async () => {
if (callback) {
await callback();
}
resolve();
});
});
}
/**
* Response time foor LRS
*/
calculateResponseTime() {
const timestampDiff = Date.now() - LrsUtils.currentUserResponseTime;
const millis = Math.floor(timestampDiff / 1000);
LrsUtils.responseTime = millis;
}
/**
* Show or hide mascotte (if show scene is not displayed)
*/
mathiaDisplay(show: boolean) {
if (show) {
this.cabri.displayMeshes(false);
this.page.displayExceptedResult = false;
this.page.displayCM = true;
this.cabriService.hideMathia = false;
} else {
this.cabri.displayMeshes(true);
this.page.displayExceptedResult = true;
this.page.displayCM = false;
this.cabriService.hideMathia = false;
}
this.page.detectChanges();
}
async readConsigne(){
await this.scenario.readConsigne(this.cabri.getCurrentOperation());
}
async runExercice() {
this.page.checkIfSonicMode();
this.cabri.buildscene();
// poser la question
await this.readConsigne();
this.answerSubscription = this.cabri.waitUserAnswer.subscribe(async (userAnswer: UserAnswer) => {
await this.ttsService.killSpeech();
this.cabriService.participantAnswer = userAnswer?.response;
if (userAnswer.success) {
let scenario;
const animation = this.page.runAnimSuccess();
if (this.page.failed) {
// SUCCESS 2ND TRY
this.manageLRS(ResultType.successOnRetry);
scenario = this.scenario.playSoundWithAward(AwardsType.normal);
// if (this.page.sonicAward) {
// } else {
// this.page.audioService.playStarSound();
// scenario = this.scenario.dynamicFeedback(AnswerStatus.VALID2ND, LrsUtils.responseTime, AwardsType.normal);
// }
await this.execScenarioMathiaAnim(animation, scenario);
} else {
// SUCCESS 1ST TRY
this.manageLRS(ResultType.success);
scenario = this.scenario.playSoundWithAward(AwardsType.shooting);
// if (this.page.sonicAward) {
// } else {
// this.page.audioService.playStarSound();
// scenario = this.scenario.dynamicFeedback(AnswerStatus.VALID1ST, LrsUtils.responseTime, AwardsType.shooting);
// }
await this.execScenarioMathiaAnim(animation, scenario);
}
this.page.failed = false;
this.page.detectChanges();
this.page.newQuestion();
} else {
this.page.audioService.playAwardMoonSound();
if (!this.page.failed) {
// ERROR 1ST TRY - NEED HELP
this.manageLRS(ResultType.failOnFirstAttempt);
// const animation = this.page.runAnimError();
// const scenario = this.firstBadResponseScenario(AnswerStatus.ERROR1ST, LrsUtils.responseTime, null, AnswerNeedsHelp.YES);
// await this.execScenarioMathiaAnim(animation, scenario);
if (this.isChallengeMode) {
this.checkChallengeModeRules();
this.page.failed = false;
} else {
// await this.runRemediationBasket();
this.page.failed = true;
}
if(!this.isChallengeMode){
this.page.detectChanges();
await this.scenario.readConsigne(this.cabri.getCurrentOperation());
}
} else {
// ERROR 2ND TRY
this.page.failed = false;
this.manageLRS(ResultType.fail);
// const animation = this.page.runAnimError();
// const scenario = this.page.scenario.badResponseMoonWithResultBasketDynamic(
// AnswerStatus.ERROR2ND,
// LrsUtils.responseTime,
// AwardsType.moon
// );
// await this.execScenarioMathiaAnim(animation, scenario);
this.page.displayActivityElements(true);
this.page.newQuestion();
}
}
})
}
manageLRS(result: ResultType, toggleStarboard = true) {
this.calculateResponseTime();
switch (result) {
case ResultType.success:
this.page.manageLrsOperation("passed");
this.page.updatePlayerAndTeamStarboards(this.getAward(AwardsType.shooting));
break;
case ResultType.successOnRetry:
this.page.manageLrsOperation("passed-with-help");
this.page.updatePlayerAndTeamStarboards(this.getAward(AwardsType.normal));
break;
case ResultType.fail:
// this.page.manageLrsOperation("failed");
this.page.updatePlayerAndTeamStarboards(this.getAward(AwardsType.moon));
break;
case ResultType.failOnFirstAttempt:
const isEliminated = this.checkRulesLrsElimination();
if (isEliminated) {
this.page.manageLrsOperation("failed-on-first-attempt", true, true);
} else {
this.page.manageLrsOperation("failed-on-first-attempt");
}
break;
}
if (result !== ResultType.failOnFirstAttempt && toggleStarboard) {
this.page.showHideAward();
}
}
/**
* Check if any user eliminated after bad response before end of the activity
*/
checkRulesLrsElimination(): boolean {
let anyUserEliminated = false;
if (this.isChallengeMode) {
anyUserEliminated = this.page.currentTeam.length > 1 && !this.page.endOfActivity();
}
return anyUserEliminated;
}
getAward(award: AwardsType): string {
let currentAward = award;
// exceptions
if (Number(this.cabriService.currentActivity.variables["v-cursor-mod"]) === 0) {
// MODE DECOUVERTE
if (award === AwardsType.shooting) {
currentAward = AwardsType.normal;
}
}
return currentAward;
}
async updateExercice(firstTime = false) {
return new Promise((resolve, reject) => {
// console.error("update exercice");
const previousConsigne = this.cabri.getCurrentOperation();
const exoType = this.updateVariables();
// force numpad mode for certain exercises
// update time for spaced repetition
const date = new Date();
const seconds = (date.getTime() - this.spacedRepetitonDate.getTime()) / 1000;
if (this.foundQuestionValid(seconds, previousConsigne)) {
this.spacedRepetitonDate = new Date();
resolve(exoType);
} else {
setTimeout(async () => {
resolve(await this.updateExercice());
}, 30);
}
});
}
foundQuestionValid(seconds, previousConsigne) {
if (this.lmsService.currentUserJourney) {
// mode parcours
if (!this.spacedStudentRepetition() && seconds <= 3) {
return false;
} else {
if (seconds >= 3 && this.lmsService.currentUserJourney.allAskedQuestions) {
this.lmsService.currentUserJourney.allAskedQuestions = new Array();
}
}
} else {
// mode exercice
const posInd = this.page.indexAlreadyAskedQuestion(this.cabri.getCurrentOperation(), previousConsigne);
if (posInd > -1 && seconds <= 3) {
return false;
} else if (seconds >= 3 && this.page.questionAskedDuringSession) {
this.page.questionAskedDuringSession = new Array();
}
}
return true;
}
/**
* @returns true if the current question could be asked
*/
spacedStudentRepetition() {
let spacedRepetition = true;
const countCorrectAnswers = this.page.checkQuestionAskedDuringSession(this.cabri.getCurrentOperation());
if (countCorrectAnswers !== 0) {
const questionAwards = this.lmsService.currentUserJourney.allAskedQuestions.filter(questionAward => {
return (
questionAward.question &&
(questionAward.question.trim() === this.cabri.getCurrentOperation().trim() ||
this.cabri.getCurrentOperation().trim() === questionAward.question.trim().split("").reverse().join("")) &&
typeof questionAward.award === "string"
);
});
const lastTimePassedQuestion = questionAwards[questionAwards.length - 1];
if (!lastTimePassedQuestion) {
spacedRepetition = true;
} else {
const lastAnswerDate = lastTimePassedQuestion.timestamp;
const weekCount = Math.pow(2, countCorrectAnswers - 1);
const recurrenceWeek: string = format(addWeeks(parseISO(lastAnswerDate), weekCount), "T");
spacedRepetition = Number(format(new Date(), "T")) > Number(recurrenceWeek);
}
return spacedRepetition;
} else {
// question never been asked
return true;
}
}
private debug(message, on = true) {
if (on) {
console.error(message);
}
}
}