package info.opencards.core;
import info.opencards.Utils;
import info.opencards.learnstrats.leitner.LeitnerItem;
import info.opencards.learnstrats.leitner.LeitnerSystem;
import info.opencards.learnstrats.ltm.LTMCollection;
import info.opencards.learnstrats.ltm.LTMItem;
import info.opencards.ui.preferences.LeitnerSettings;
import java.util.*;
/**
* A collection of flashcard along with some properties. Each flashcard may contain learn-items of differnt kind
* according to different learn-modes applied to this card.
* <p/>
* Instances of this class will be serialized to the flash-card-file. It is therefore necessary that it does not contain
* any non-transient references to the outer OpenCards/OpenOffice-ecosystem.
*
* @author Holger Brandl
*/
public class FlashCardCollection extends ArrayList<FlashCard> {
public static final String REVERSE_POLICY = "reversePolicy";
private HashMap<String, Object> properties = new HashMap<String, Object>();
private final Map<Class<? extends Item>, ItemCollection> itColls = new HashMap<Class<? extends Item>, ItemCollection>();
/**
* The sync state of all <code>ItemCollection</code>s maintained by this flashcard collection.
*/
private final Map<Class<? extends Item>, Boolean> syncStates = new HashMap<Class<? extends Item>, Boolean>();
public ItemCollection getItems(Class<? extends Item> itemType) {
ItemCollection items = itColls.get(itemType);
if (items == null) {
if (itemType.equals(LTMItem.class)) {
items = new LTMCollection();
} else if (itemType.equals(LeitnerItem.class)) {
int numBoxes = Utils.getPrefs().getInt(LeitnerSettings.NUM_LEITNER_BOXES, LeitnerSettings.NUM_LEITNER_BOXES_DEFAULT);
int initBox = Utils.getPrefs().getInt(LeitnerSettings.INIT_LEITNER_BOXES, LeitnerSettings.INIT_LEITNER_BOXES_DEFAULT);
items = new LeitnerSystem(numBoxes, initBox - 1);
}
itColls.put(itemType, items);
syncStates.put(itemType, false);
}
ItemCollection itemCollection = itColls.get(itemType);
//update the item collection to contain all current flashcards
if (!syncStates.containsKey(itemType))
syncStates.put(itemType, false);
if (!syncStates.get(itemType)) {
syncStates.put(itemType, true);
for (FlashCard flashCard : this) {
if (itemCollection.findItem(flashCard) == null) {
itemCollection.addItem(flashCard);
}
}
}
return itemCollection;
}
/**
* @throw IllegalArgumentException if this collection already contains a flashcard with the same ID
*/
public boolean add(FlashCard flashCard) {
// if (getByID(flashCard.getCardID()) != null)
// throw new IllegalArgumentException("flashcard addition failed because flashcard ID is already in use.");
if (getByID(flashCard.getCardID()) != null)
return false;
for (Class<? extends Item> aClass : itColls.keySet()) {
itColls.get(aClass).addItem(flashCard);
}
flagUnsyncAl();
// set the common reversing policy for the new flashcard
ReversePolicy filePolicy = (ReversePolicy) getProperty(FlashCardCollection.REVERSE_POLICY, ReversePolicy.NORMAL);
flashCard.setRevPolicy(filePolicy);
return super.add(flashCard);
}
public FlashCard remove(int index) {
FlashCard removeCard = get(index);
remove(removeCard);
return removeCard;
}
public boolean remove(FlashCard flashCard) {
ArrayList<Class<? extends Item>> arrayList = new ArrayList<Class<? extends Item>>(itColls.keySet());
for (int i = 0; i < arrayList.size(); i++) { // for each is not applicable here because of remoove-operation
Class<? extends Item> aClass = arrayList.get(i);
itColls.get(aClass).removeItem(flashCard);
}
flagUnsyncAl();
return super.remove(flashCard);
}
private void flagUnsyncAl() {
for (Class<? extends Item> aClass : syncStates.keySet()) {
syncStates.put(aClass, false);
}
}
/**
* Returns all items which belong to a certain flashcard.
*
* @return An empty collection if the argument card is not part of
*/
public Collection<Item> getCardItems(FlashCard card) {
assert contains(card) : "card not contained in collection";
Collection<Item> cardItems = new ArrayList<Item>();
for (ItemCollection itemCollection : itColls.values()) {
Item cardItem = itemCollection.findItem(card);
if (cardItem != null)
cardItems.add(cardItem);
}
return cardItems;
}
/**
* A simple shortcut for <code>(LTMCollection) getItems(LTMItem.class)</code>.
*/
public LTMCollection getLTMItems() {
return (LTMCollection) getItems(LTMItem.class);
}
/**
* A simple shortcut for <code>(LeitnerSystem) getItems(LeitnerItem.class)</code>.
*/
public LeitnerSystem getLeitnerItems() {
return (LeitnerSystem) getItems(LeitnerItem.class);
}
public Object getProperty(String propName, Object defaultProperty) {
if (getProps().containsKey(propName))
return getProps().get(propName);
else
return defaultProperty;
}
public Object setProperty(String propName, Object propValue) {
return getProps().put(propName, propValue);
}
/**
* Returns the property-value for the specified <code>propName</code> or the given <code>defaultProperty </code> if
* the <code>propName</code> was not a property-key. In the latter case the properties are not updated.
*/
public HashMap<String, Object> getProps() {
if (properties == null)
properties = new HashMap<String, Object>();
return properties;
}
public FlashCard getByIndex(int searchIndex) {
for (FlashCard card : this) {
if (card.getCardIndex() == searchIndex) {
return card;
}
}
return null;
}
public List<FlashCard> getByName(String searchTerm) {
List<FlashCard> matchingCards = new ArrayList<FlashCard>();
for (FlashCard card : this) {
if (card.getCardTitle().contains(searchTerm)) {
matchingCards.add(card);
}
}
return matchingCards;
}
public Object clone() {
FlashCardCollection cloneFCC = (FlashCardCollection) super.clone();
cloneFCC.clear();
for (FlashCard card : this) {
try {
cloneFCC.add((FlashCard) card.clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
return cloneFCC;
}
FlashCard getByID(long slideID) {
for (FlashCard card : this) {
if (card.getCardID() == slideID)
return card;
}
return null;
}
}
///** An API which allows to process change events of a <code>FlashCardCollection</code>. */
//interface FlashCardCollChangeListner {
//
// public void flashCardCollChanged();
//}