/**
* VocabularyTrainer Copyright (C) 2015 André Schepers andreschepers81@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.vocabularyexercise.domain;
import eu.vocabularyexercise.domain.interfaces.VocabularyExerciseModel;
import eu.vocabularytrainer.vocabulary.DefaultRepresentative;
import eu.vocabularytrainer.vocabulary.DefaultVocabularyElementPair;
import eu.vocabularytrainer.vocabulary.interfaces.Iteration;
import eu.vocabularytrainer.vocabulary.interfaces.Representative;
import eu.vocabularytrainer.vocabulary.interfaces.Representative.Representation;
import eu.vocabularytrainer.vocabulary.interfaces.Vocabulary;
import eu.vocabularytrainer.vocabulary.interfaces.Vocabulary.Direction;
import static eu.vocabularytrainer.vocabulary.interfaces.Vocabulary.Direction.COLUMNONETOONE;
import static eu.vocabularytrainer.vocabulary.interfaces.Vocabulary.Direction.COLUMNONETOTWO;
import static eu.vocabularytrainer.vocabulary.interfaces.Vocabulary.Direction.COLUMNTWOTOONE;
import static eu.vocabularytrainer.vocabulary.interfaces.Vocabulary.Direction.COLUMNTWOTOTWO;
import eu.vocabularytrainer.vocabulary.interfaces.Vocabulary.UpdateType;
import eu.vocabularytrainer.vocabulary.interfaces.VocabularyElementPair;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Random;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
*
* @author andres81
*/
public class DefaultVocabularyExerciseModel extends Observable implements VocabularyExerciseModel {
// Logging
private static final Logger logger = LogManager.getLogger(DefaultVocabularyExerciseModel.class);
/**
*
*/
private Map<UUID,VocabularyElementPair> activePairs = null;
/**
*
*/
private Map<UUID,VocabularyElementPair> vocabularyPairs = null;
/**
*
*/
private Representative activeQuery = null;
/**
*
*/
private VocabularyElementPair activeQueryPair = null;
/**
*
*/
private Representative activeQueryOption = null;
/**
*
*/
private List<Representative> options = null;
/**
*
*/
private Direction direction = null;
/**
*
*/
private Representation queryRepresentation = null;
/**
*
*/
private Representation optionsRepresentation = null;
/**
*
*/
private int pairStartIndex = 1;
/**
*
*/
private int pairEndIndex = 1;
/**
*
*/
private List<Iteration> iterations = null;
private int iterationIndex;
/**
*
*/
public DefaultVocabularyExerciseModel() {
direction = Direction.COLUMNONETOTWO;
optionsRepresentation = Representation.STRING;
queryRepresentation = Representation.STRING;
}
/**
* s
* @param direction
*/
@Override
public void setDirection(Direction direction) {
if (direction == null) {
throw new NullPointerException();
}
this.direction = direction;
updateQueryAndQueryOption();
updateOptions();
setChanged();
notifyObservers(UpdateType.DIRECTION);
}
/**
*
* @param representation
*/
@Override
public void setQueryRepresentation(Representation representation) {
this.queryRepresentation = representation;
setChanged();
notifyObservers(UpdateType.QUERYINTERACTIONTYPE);
}
/**
*
* @param representation
*/
@Override
public void setOptionsRepresentation(Representation representation) {
this.optionsRepresentation = representation;
setChanged();
notifyObservers(UpdateType.OPTIONSINTERACTIONTYPE);
}
/**
*
* @return
*/
@Override
public Representation getQueryRepresentation() {
return queryRepresentation;
}
/**
*
* @return
*/
@Override
public Representation getOptionsRepresentation() {
return optionsRepresentation;
}
/**
*
* @param vocabulary
*/
@Override
public void setVocabulary(Vocabulary vocabulary) {
setVocabularyElementPairs(vocabulary.getPairs());
setIterations(vocabulary.getIterations());
iterationIndex = 0;
initIteration();
updateActivePairs();
updateOptions();
setRandomActiveQueryPairNoUpdate();
setChanged();
notifyObservers(UpdateType.PAIRS);
}
/**
*
*/
private void initIteration() {
Iteration iteration = iterations.get(iterationIndex);
direction = iteration.getColumnOrder();
queryRepresentation = iteration.getQueryType();
optionsRepresentation = iteration.getOptionType();
}
/**
*
* @param pairs
*/
private void setVocabularyElementPairs(List<VocabularyElementPair> pairs) {
if (pairs == null) {
throw new NullPointerException();
}
if (this.vocabularyPairs == null) {
this.vocabularyPairs = new LinkedHashMap<>();
}
this.vocabularyPairs.clear();
for (VocabularyElementPair pair : pairs) {
this.vocabularyPairs.put(pair.getUuid(), pair);
}
if (vocabularyPairs.size() < 1) return;
pairStartIndex = 0;
}
/**
*
*/
private void updateActivePairs() {
if (vocabularyPairs == null) return;
if (activePairs == null) {
activePairs = new LinkedHashMap<>();
}
pairEndIndex = pairStartIndex + 4;
int size = vocabularyPairs.size();
pairEndIndex = (pairEndIndex < size) ? pairEndIndex : (size - 1);
activePairs.clear();
LinkedList<UUID> keys = new LinkedList<>(vocabularyPairs.keySet());
for (int i = pairStartIndex;i<=pairEndIndex;i++) {
activePairs.put(keys.get(i), vocabularyPairs.get(keys.get(i)));
}
}
/**
*
*/
@Override
public void shiftToPreviousPairs() {
if (pairStartIndex == 0) {
if (iterationIndex == 0) return;
iterationIndex = iterationIndex - 1;
initIteration();
pairStartIndex = vocabularyPairs.size() - (vocabularyPairs.size() % 5);
} else {
pairStartIndex = pairStartIndex - 5;
if (pairStartIndex < 0) pairStartIndex = 0;
pairEndIndex = pairStartIndex + 4;
}
updateActivePairs();
updateOptions();
setRandomActiveQueryPairNoUpdate();
setChanged();
notifyObservers(UpdateType.PAIRS);
}
/**
*
*/
@Override
public void shiftToNextPairs() {
if (pairEndIndex == vocabularyPairs.size() - 1) {
// Next iteration
if (iterationIndex == iterations.size() - 1) return;
iterationIndex = iterationIndex + 1;
initIteration();
pairStartIndex = 0;
} else {
pairStartIndex = pairEndIndex + 1;
}
updateActivePairs();
updateOptions();
setRandomActiveQueryPairNoUpdate();
setChanged();
notifyObservers(UpdateType.PAIRS);
}
/**
*
* @return
*/
@Override
public List<VocabularyElementPair> getVocabularyElementPairs() {
return new ArrayList<>(activePairs.values());
}
/**
*
* @return
*/
@Override
public Representative getActiveQuery() {
if (activeQuery == null) {
activeQuery = new DefaultRepresentative();
}
return activeQuery;
}
/**
*
* @return
*/
@Override
public Representative getActiveQueryOption() {
if (activeQueryOption == null) {
activeQueryOption = new DefaultRepresentative();
}
return activeQueryOption;
}
/**
* Set a new random active query pair.
*/
@Override
public void setRandomActiveQueryPair() {
setRandomActiveQueryPairNoUpdate();
setChanged();
notifyObservers(UpdateType.ACTIVEPAIR);
}
/**
*
* @return
*/
@Override
public VocabularyElementPair getActiveQueryPair() {
if (activeQueryPair == null) {
activeQueryPair = new DefaultVocabularyElementPair(new DefaultRepresentative(), new DefaultRepresentative());
}
return activeQueryPair;
}
/**
*
* @param o
*/
@Override
public void addObserver(Observer o) {
super.addObserver(o);
}
/**
*
* @param uuid
*/
private void setActiveQueryPairNoUpdate(UUID uuid) {
if (uuid == null) {
throw new NullPointerException();
}
VocabularyElementPair pair = activePairs.get(uuid);
if (pair == null) return; // No pair found with given uuid!
activeQueryPair = pair;
updateQueryAndQueryOption();
}
/**
*
*/
private void updateQueryAndQueryOption() {
if (activeQueryPair == null) return;
Representative first = activeQueryPair.getFirst();
Representative second = activeQueryPair.getSecond();
switch(direction) {
case COLUMNONETOONE:
activeQuery = first;
activeQueryOption = first;
break;
case COLUMNONETOTWO:
activeQuery = first;
activeQueryOption = second;
break;
case COLUMNTWOTOONE:
activeQuery = second;
activeQueryOption = first;
break;
case COLUMNTWOTOTWO:
activeQuery = second;
activeQueryOption = second;
break;
}
}
/**
* Set a new random active query pair.
*/
private void setRandomActiveQueryPairNoUpdate() {
if (activePairs == null ||
activePairs.isEmpty()) {
activeQueryPair = null;
activeQuery = null;
activeQueryOption = null;
return;
}
List<VocabularyElementPair> temp = new ArrayList<>(activePairs.values());
if (activeQueryPair != null &&
temp.size() > 1) {
temp.remove(activeQueryPair);
}
Random r = new Random();
int newIndex = r.nextInt(temp.size());
setActiveQueryPairNoUpdate(temp.get(newIndex).getUuid());
}
/**
*
*/
private void updateOptions() {
if (activePairs == null) return;
options = new ArrayList<>();
for(VocabularyElementPair pair : activePairs.values()) {
if (direction == Direction.COLUMNONETOONE ||
direction == Direction.COLUMNTWOTOONE) {
options.add(pair.getFirst());
} else {
options.add(pair.getSecond());
}
}
}
/**
*
* @return
*/
@Override
public List<Representative> getOptions() {
return options;
}
/**
*
* @param iterations
*/
private void setIterations(List<Iteration> iterations) {
this.iterations = iterations;
Collections.sort(this.iterations, new Comparator<Iteration>() {
@Override
public int compare(Iteration t, Iteration t1) {
int ti1 = t.getIndex();
int ti2 = t.getIndex();
if (ti1 < ti2) return -1;
if (ti1 == ti2) return 0;
return 1;
}
});
}
}