<commit_before>package com.cherokeelessons.cll2ev1.screens;

import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.ListIterator;
import java.util.Random;
import java.util.Set;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Stack;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.Scaling;
import com.cherokeelessons.cll2ev1.AbstractGame;
import com.cherokeelessons.cll2ev1.CLL2EV1;
import com.cherokeelessons.cll2ev1.models.CardData;
import com.cherokeelessons.deck.CardStats;
import com.cherokeelessons.deck.CardUtils;
import com.cherokeelessons.deck.Deck;
import com.cherokeelessons.deck.DeckStats;
import com.cherokeelessons.deck.ICard;
import com.cherokeelessons.util.SlotFolder;

public class LearningSession extends AbstractScreen implements Screen {
	private static final String DING = "audio/ding.mp3";
	private static final String BUZZER = "audio/buzzer2.mp3";
	private static final String XMARK = "images/2716_white.png";
	private static final String CHECKMARK = "images/2714_white.png";
	private static final String IMAGES_OVERLAY = "images/overlay-border.png";
	private static final String IMAGES_BACKDROP = "images/white-backdrop.png";
	private final int session;
	private final Deck<CardData> masterDeck;
	private final Deck<CardData> activeDeck;
	private final Deck<CardData> discardsDeck = new Deck<CardData>();
	private final Deck<CardData> completedDeck = new Deck<CardData>();

	public LearningSession(AbstractGame game, int session, Deck<CardData> masterDeck, Deck<CardData> activeDeck) {
		super(game);
		this.session = session;
		this.masterDeck = masterDeck;
		this.activeDeck = activeDeck;
		setBackdrop(CLL2EV1.BACKDROP);
		setSkin(CLL2EV1.SKIN);

		log("Session: " + session);
		log("Master Deck Size: " + masterDeck.size());
		log("Active Deck Size: " + activeDeck.size());
		log("First Time: " + (activeDeck.size() == 0));

		if (activeDeck.size() == 0) {
			stage.addAction(actionFirstTime());
		} else {
			stage.addAction(actionLoadNextChallengeQuick());
		}

		stage.addAction(actionUpdateTimeLeft());

		lblCountdown = new Label("0:00", skin);
		lblCountdown.setFontScale(.75f);

		challengeText = new Label("", skin);
		choice1 = new Stack();
		choice2 = new Stack();

		choice1.setTouchable(Touchable.childrenOnly);
		choice2.setTouchable(Touchable.childrenOnly);

		Gdx.app.postRunnable(init1);
		Gdx.app.postRunnable(init2);

		assets.load(CHECKMARK, Texture.class);
		assets.load(XMARK, Texture.class);
		assets.finishLoadingAsset(CHECKMARK);
		assets.finishLoadingAsset(XMARK);
		checkmark = assets.get(CHECKMARK, Texture.class);
		xmark = assets.get(XMARK, Texture.class);
		imgCheckmark = new Image(checkmark);
		imgCheckmark.setScaling(Scaling.fit);
		imgCheckmark.setColor(new Color(Color.FOREST));
		imgCheckmark.getColor().a = .75f;
		imgXmark = new Image(xmark);
		imgXmark.setScaling(Scaling.fit);
		imgXmark.setColor(new Color(Color.FIREBRICK));
		imgXmark.getColor().a = .75f;

		assets.load(BUZZER, Sound.class);
		assets.load(DING, Sound.class);
	}

	private float maxTime_secs = 5.0f * 60f;

	private void updateTimeleft() {
		float timeleft_secs = maxTime_secs - totalElapsed;
		if (timeleft_secs < 0f) {
			timeleft_secs = 0f;
		}
		int minutes = (int) (timeleft_secs / 60f);
		int seconds = ((int) (timeleft_secs)) - 60 * minutes;
		String tmp = minutes + ":" + ((seconds < 10) ? "0" : "") + seconds;
		lblCountdown.setText(tmp);
		lblCountdown.pack();
	}

	private Action actionUpdateTimeLeft() {
		return Actions.sequence(Actions.delay(.1f), Actions.run(new Runnable() {
			@Override
			public void run() {
				stage.addAction(actionUpdateTimeLeft());
				updateTimeleft();
			}
		}));
	}

	private Texture checkmark;
	private Texture xmark;

	private Image imgCheckmark;
	private Image imgXmark;

	private Table uiTable;
	private Table gameTable;
	private Table challengeTable;
	private Table answersTable;

	private Music challengeAudio;

	private final Label challengeText;
	private final Stack choice1;
	private final Stack choice2;
	protected Runnable init1 = new Runnable() {
		@Override
		public void run() {
			prepDecks();
		}
	};
	protected Runnable init2 = new Runnable() {
		@Override
		public void run() {
			initUi();
		}
	};

	// private float sinceLastDeckShuffle_elapsed = 0f;
	private String previousCardId = null;
	private String activeAudioFile;
	private String activeImageFile1;
	private String activeImageFile2;
	private int correct = 1;
	private ICard<CardData> activeCard;
	private CardData activeCardData;
	private CardStats activeCardStats;

	@Override
	protected void act(float delta) {
		// sinceLastDeckShuffle_elapsed += delta;
	}

	private void loadNextChallenge() {
		ICard<CardData> card;
		card = getNextCard();
		activeCard = card;
		activeCardData = card.getData();
		activeCardStats = card.getCardStats();
		loadNewChallengeImages(activeCardData);
		loadNewChallengeAudio(activeCardData.nextRandomAudioFile());
		// reset for each card being displayed
		currentElapsed = 0f;
	}

	private void showFinalStats() {
		log("SHOWING FINAL STATS");
		userPause();
		pausedStage.getRoot().clearChildren();
		Dialog finalStats = new Dialog("FINAL STATS", skin) {
			@Override
			protected void result(Object object) {
				userResume();
				game.previousScreen();
			}
		};
		finalStats.setModal(true);
		finalStats.setFillParent(true);
		finalStats.getTitleLabel().setAlignment(Align.center);
		TextButton howa = new TextButton("ᎰᏩ", skin);
		howa.setDisabled(true);
		finalStats.button(howa);

		DeckStats stats = DeckStats.calculateStats(activeDeck);
		StringBuilder txt = new StringBuilder();
		txt.append("LEVEL: ");
		txt.append(stats.level.getEnglish());
		txt.append("\n");
		txt.append("Active cards: ");
		txt.append(stats.activeCards);
		txt.append("\n");
		txt.append("Score: ");
		txt.append(stats.lastScore);
		txt.append("\n");
		txt.append("Proficiency: ");
		txt.append(stats.proficiency);
		txt.append("%");

		Table contentTable = finalStats.getContentTable();
		contentTable.row();
		Table noticeTable = new Table(skin);
		noticeTable.defaults().expand().fill();
		contentTable.add(noticeTable).expand().fill();
		Label message = new Label(txt.toString(), skin);
		message.setAlignment(Align.center);
		message.setWrap(true);
		message.setFontScale(1.2f);
		noticeTable.add(message);
		finalStats.show(pausedStage);

		pausedStage.addAction(actionSaveActiveDeck(howa));
	}

	private void endSessionCleanup() {
		/*
		 * Check and see how many discards can be marked as "completed". <br>
		 * Those that can be marked are those with shown >= triesRemaining
		 */
		ListIterator<ICard<CardData>> idiscard = discardsDeck.cardsIterator();
		while (idiscard.hasNext()) {
			ICard<CardData> card = idiscard.next();
			CardStats cardStats = card.getCardStats();
			if (cardStats.getShown() < cardStats.getTriesRemaining()) {
				continue;
			}
			log("Marking " + card.getData().text + " as completed this session.");
			if (cardStats.isCorrect()) {
				// if correct, the card should get moved to the next
				// leitner box
				cardStats.leitnerBoxInc();
			} else {
				// if wrong, it should get moved to the previous
				// leitner box
				cardStats.leitnerBoxDec();
			}
			cardStats.setNextSessionShow(CardUtils.getNextSessionIntervalDays(cardStats.getLeitnerBox()));
			idiscard.remove(); // remove before add
			completedDeck.add(card);
			log("- Moved to Leitner box: " + cardStats.getLeitnerBox());
			log("- Sessions to show again: " + cardStats.getNextSessionShow());
		}
		/*
		 * Combine discards and active together. These were in play at session
		 * end.
		 */
		while (discardsDeck.hasCards()) {
			activeDeck.add(discardsDeck.topCard());
		}
		/*
		 * Add in completed so all cards are now in the activedeck.
		 */
		while (completedDeck.hasCards()) {
			activeDeck.add(completedDeck.topCard());
		}
	}

	private Action actionShowCompletedDialog() {
		return Actions.run(new Runnable() {
			@Override
			public void run() {
				endSessionCleanup();
				showFinalStats();
			}
		});
	}

	private Action actionSaveActiveDeck(final Button howa) {
		return Actions.run(new Runnable() {
			@Override
			public void run() {
				saveActiveDeck();
				howa.setDisabled(false);
			}
		});
	}
<commit_msg>try and fix issue with lost cards<commit_after>package com.cherokeelessons.cll2ev1.screens;

import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.ListIterator;
import java.util.Random;
import java.util.Set;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Stack;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.Scaling;
import com.cherokeelessons.cll2ev1.AbstractGame;
import com.cherokeelessons.cll2ev1.CLL2EV1;
import com.cherokeelessons.cll2ev1.models.CardData;
import com.cherokeelessons.deck.CardStats;
import com.cherokeelessons.deck.CardUtils;
import com.cherokeelessons.deck.Deck;
import com.cherokeelessons.deck.DeckStats;
import com.cherokeelessons.deck.ICard;
import com.cherokeelessons.util.SlotFolder;

public class LearningSession extends AbstractScreen implements Screen {
	private static final String DING = "audio/ding.mp3";
	private static final String BUZZER = "audio/buzzer2.mp3";
	private static final String XMARK = "images/2716_white.png";
	private static final String CHECKMARK = "images/2714_white.png";
	private static final String IMAGES_OVERLAY = "images/overlay-border.png";
	private static final String IMAGES_BACKDROP = "images/white-backdrop.png";
	private final int session;
	private final Deck<CardData> masterDeck;
	private final Deck<CardData> activeDeck;
	private final Deck<CardData> discardsDeck = new Deck<CardData>();
	private final Deck<CardData> completedDeck = new Deck<CardData>();

	public LearningSession(AbstractGame game, int session, Deck<CardData> masterDeck, Deck<CardData> activeDeck) {
		super(game);
		this.session = session;
		this.masterDeck = masterDeck;
		this.activeDeck = activeDeck;
		setBackdrop(CLL2EV1.BACKDROP);
		setSkin(CLL2EV1.SKIN);

		log("Session: " + session);
		log("Master Deck Size: " + masterDeck.size());
		log("Active Deck Size: " + activeDeck.size());
		log("First Time: " + (activeDeck.size() == 0));

		stage.addAction(actionUpdateTimeLeft());

		lblCountdown = new Label("0:00", skin);
		lblCountdown.setFontScale(.75f);

		challengeText = new Label("", skin);
		choice1 = new Stack();
		choice2 = new Stack();

		choice1.setTouchable(Touchable.childrenOnly);
		choice2.setTouchable(Touchable.childrenOnly);

		Gdx.app.postRunnable(init1);
		Gdx.app.postRunnable(init2);
		Gdx.app.postRunnable(firstPlay);

		assets.load(CHECKMARK, Texture.class);
		assets.load(XMARK, Texture.class);
		assets.finishLoadingAsset(CHECKMARK);
		assets.finishLoadingAsset(XMARK);
		checkmark = assets.get(CHECKMARK, Texture.class);
		xmark = assets.get(XMARK, Texture.class);
		imgCheckmark = new Image(checkmark);
		imgCheckmark.setScaling(Scaling.fit);
		imgCheckmark.setColor(new Color(Color.FOREST));
		imgCheckmark.getColor().a = .75f;
		imgXmark = new Image(xmark);
		imgXmark.setScaling(Scaling.fit);
		imgXmark.setColor(new Color(Color.FIREBRICK));
		imgXmark.getColor().a = .75f;

		assets.load(BUZZER, Sound.class);
		assets.load(DING, Sound.class);
	}

	private float maxTime_secs = 5.0f * 60f;

	private void updateTimeleft() {
		float timeleft_secs = maxTime_secs - totalElapsed;
		if (timeleft_secs < 0f) {
			timeleft_secs = 0f;
		}
		int minutes = (int) (timeleft_secs / 60f);
		int seconds = ((int) (timeleft_secs)) - 60 * minutes;
		String tmp = minutes + ":" + ((seconds < 10) ? "0" : "") + seconds;
		lblCountdown.setText(tmp);
		lblCountdown.pack();
	}

	private Action actionUpdateTimeLeft() {
		return Actions.sequence(Actions.delay(.1f), Actions.run(new Runnable() {
			@Override
			public void run() {
				stage.addAction(actionUpdateTimeLeft());
				updateTimeleft();
			}
		}));
	}

	private Texture checkmark;
	private Texture xmark;

	private Image imgCheckmark;
	private Image imgXmark;

	private Table uiTable;
	private Table gameTable;
	private Table challengeTable;
	private Table answersTable;

	private Music challengeAudio;

	private final Label challengeText;
	private final Stack choice1;
	private final Stack choice2;
	protected Runnable init1 = new Runnable() {
		@Override
		public void run() {
			prepDecks();
		}
	};
	protected Runnable init2 = new Runnable() {
		@Override
		public void run() {
			initUi();
		}
	};
	
	protected Runnable firstPlay = new Runnable() {
		@Override
		public void run() {
			if (activeDeck.size() == 0) {
				stage.addAction(actionFirstTime());
			} else {
				stage.addAction(actionLoadNextChallengeQuick());
			}			
		}
	};

	// private float sinceLastDeckShuffle_elapsed = 0f;
	private String previousCardId = null;
	private String activeAudioFile;
	private String activeImageFile1;
	private String activeImageFile2;
	private int correct = 1;
	private ICard<CardData> activeCard;
	private CardData activeCardData;
	private CardStats activeCardStats;

	@Override
	protected void act(float delta) {
		// sinceLastDeckShuffle_elapsed += delta;
	}

	private void loadNextChallenge() {
		ICard<CardData> card;
		card = getNextCard();
		activeCard = card;
		activeCardData = card.getData();
		activeCardStats = card.getCardStats();
		loadNewChallengeImages(activeCardData);
		loadNewChallengeAudio(activeCardData.nextRandomAudioFile());
		// reset for each card being displayed
		currentElapsed = 0f;
	}

	private void showFinalStats() {
		log("SHOWING FINAL STATS");
		userPause();
		pausedStage.getRoot().clearChildren();
		Dialog finalStats = new Dialog("FINAL STATS", skin) {
			@Override
			protected void result(Object object) {
				userResume();
				game.previousScreen();
			}
		};
		finalStats.setModal(true);
		finalStats.setFillParent(true);
		finalStats.getTitleLabel().setAlignment(Align.center);
		TextButton howa = new TextButton("ᎰᏩ", skin);
		howa.setDisabled(true);
		finalStats.button(howa);

		DeckStats stats = DeckStats.calculateStats(activeDeck);
		StringBuilder txt = new StringBuilder();
		txt.append("LEVEL: ");
		txt.append(stats.level.getEnglish());
		txt.append("\n");
		txt.append("Active cards: ");
		txt.append(stats.activeCards);
		txt.append("\n");
		txt.append("Score: ");
		txt.append(stats.lastScore);
		txt.append("\n");
		txt.append("Proficiency: ");
		txt.append(stats.proficiency);
		txt.append("%");

		Table contentTable = finalStats.getContentTable();
		contentTable.row();
		Table noticeTable = new Table(skin);
		noticeTable.defaults().expand().fill();
		contentTable.add(noticeTable).expand().fill();
		Label message = new Label(txt.toString(), skin);
		message.setAlignment(Align.center);
		message.setWrap(true);
		message.setFontScale(1.2f);
		noticeTable.add(message);
		finalStats.show(pausedStage);

		pausedStage.addAction(actionSaveActiveDeck(howa));
	}

	private void endSessionCleanup() {
		log("End Session Cleanup");
		log("- Active Deck: "+activeDeck.size());
		log("- Discards Deck: "+discardsDeck.size());
		log("- Completed Deck: "+completedDeck.size());
		/*
		 * Check and see how many discards can be marked as "completed". <br>
		 * Those that can be marked are those with shown >= triesRemaining
		 */
		ListIterator<ICard<CardData>> idiscard = discardsDeck.cardsIterator();
		while (idiscard.hasNext()) {
			ICard<CardData> card = idiscard.next();
			CardStats cardStats = card.getCardStats();
			if (cardStats.getShown() < cardStats.getTriesRemaining()) {
				continue;
			}
			log("Marking " + card.getData().text + " as completed this session.");
			if (cardStats.isCorrect()) {
				// if correct, the card should get moved to the next
				// leitner box
				cardStats.leitnerBoxInc();
			} else {
				// if wrong, it should get moved to the previous
				// leitner box
				cardStats.leitnerBoxDec();
			}
			cardStats.setNextSessionShow(CardUtils.getNextSessionIntervalDays(cardStats.getLeitnerBox()));
			idiscard.remove(); // remove before add
			completedDeck.add(card);
			log("- Moved to Leitner box: " + cardStats.getLeitnerBox());
			log("- Sessions to show again: " + cardStats.getNextSessionShow());
		}
		/*
		 * Combine discards and active together. These were in play at session
		 * end.
		 */
		log("  combining discards and active deck");
		while (discardsDeck.hasCards()) {
			activeDeck.add(discardsDeck.topCard());
		}
		log("- Active Deck: "+activeDeck.size());
		log("- Discards Deck: "+discardsDeck.size());
		log("- Completed Deck: "+completedDeck.size());
		/*
		 * Add in completed so all cards are now in the activedeck.
		 */
		log("  combining completed and active deck");
		while (completedDeck.hasCards()) {
			activeDeck.add(completedDeck.topCard());
		}
		log("- Active Deck: "+activeDeck.size());
		log("- Discards Deck: "+discardsDeck.size());
		log("- Completed Deck: "+completedDeck.size());
	}

	private Action actionShowCompletedDialog() {
		return Actions.run(new Runnable() {
			@Override
			public void run() {
				endSessionCleanup();
				showFinalStats();
			}
		});
	}

	private Action actionSaveActiveDeck(final Button howa) {
		return Actions.run(new Runnable() {
			@Override
			public void run() {
				saveActiveDeck();
				howa.setDisabled(false);
			}
		});
	}

{'fraction_non_alphanumeric': 0.12799662055127256, 'fraction_numerical': 0.008343014045833774, 'mean_word_length': 4.623218527315914, 'pattern_counts': {'":': 2, '<': 37, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5530287', 'n_tokens_mistral': 7056, 'n_tokens_neox': 6324, 'n_words': 1305}