package info.opencards.ui.table;
import info.opencards.Utils;
import info.opencards.core.CardFile;
import info.opencards.core.Item;
import info.opencards.core.categories.Category;
import info.opencards.core.categories.CategoryChangeAdapter;
import info.opencards.core.categories.CategoryChangeListener;
import info.opencards.core.categories.CategoryUtils;
import info.opencards.learnstrats.ltm.*;
import info.opencards.ui.catui.CategoryTreeSelectionListener;
import javax.swing.table.DefaultTableModel;
import java.util.*;
/**
* A custom table model which shows card-files including some basic statistics in each row.
*
* @author Holger Brandl
*/
public class CardTableModel extends DefaultTableModel implements CategoryTreeSelectionListener {
private static final boolean[] columnEditable = new boolean[]{false, false, false, false};
private static final String[] columnNames = new String[]{Utils.getRB().getString("CardTableModel.fileName"), Utils.getRB().getString("CardTableModel.nextScheduled"), Utils.getRB().getString("CardTableModel.numNewCards"), Utils.getRB().getString("CardTableModel.numCards")};
private List<CardFile> curFiles = new ArrayList<CardFile>();
private List<Category> curCategories = new ArrayList<Category>();
private LTMProcessManager dummyLtmManager;
private CardSetTable cardSetTable;
public CardTableModel(CardSetTable cardSetTable) {
super(new Object[][]{}, columnNames);
// refreshTableData();
this.cardSetTable = cardSetTable;
}
public int getRowCount() {
if (curFiles == null) // this is the case only when the constructor is called
return 0;
else
return curFiles.size();
}
public int getColumnCount() {
return columnNames.length;
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnEditable[columnIndex];
}
public Object getValueAt(int rowIndex, int columnIndex) {
CardFile rowFile = curFiles.get(rowIndex);
if (!rowFile.getFileLocation().isFile())
return "can not find '" + rowFile + "'";
LTMCollection rowItems = rowFile.getFlashCards().getLTMItems();
switch (columnIndex) {
case 0:
// return rowFile.getFile().getName();
return rowFile.getFileLocation().getName();
case 1:
return getNextScheduleDate(rowItems, rowFile);
// case 1:
// Double avgRetention = SM2.getAvgRetention(rowItems);
// DecimalFormat df = new DecimalFormat("#.#");
// return avgRetention != null ? df.format(avgRetention) : "N/A";
// case 2:
// return ScheduleUtils.getNewReducedItCo(rowItems.getScheduledItems(), ScheduleUtils.getMaxCardToBeLearntToday()).size();
case 2:
return ScheduleUtils.getNewItems(rowItems).size();
case 3:
return rowItems.size();
case 50: // a special case to get the current file even from a resorted table
return rowFile;
}
return null;
}
/**
* package visible only to make referring components to use {@code CardTableModel.getSortedRowFile} instead.
*/
CardFile getRowFile(int rowIndex) {
// return the file with the index with respect to the current ordering of the table
return curFiles.get(rowIndex);
}
/**
* Clean up the current table model: remove no longer selected Categorys and add newly selected ones.
*/
public void categorySelectionChanged(List<CardFile> selectedFiles, Set<Category> selCategories) {
curCategories = new ArrayList<Category>(selCategories);
curFiles = new ArrayList<CardFile>(CategoryUtils.extractSelectedFiles(selCategories));
//sort files based on name
Collections.sort(curFiles, new Comparator<CardFile>() {
public int compare(CardFile o1, CardFile o2) {
return o1.getFileLocation().getName().compareTo(o2.getFileLocation().getName());
}
});
tableSelectionedChanged(curFiles);
cardSetTable.rebuildWithSelectionRestore();
// fireTableDataChanged();
}
public void tableSelectionedChanged(Collection<CardFile> selFiles) {
// instantiate a dummy process-manager which gives us a preleminary schedule
dummyLtmManager = new LTMProcessManager(new DummyValuator(), SM2.getFactory());
dummyLtmManager.setupSchedule(selFiles);
}
public CategoryChangeListener getCatListener() {
return new CategoryChangeAdapter() {
public void registeredCardset(Category category, CardFile cardSet) {
if (curFiles.contains(cardSet)) {
return;
}
if (curCategories.contains(category) && !curFiles.contains(cardSet)) {
curFiles.add(cardSet);
}
fireTableDataChanged();
}
public void unregisteredCardset(Category category, CardFile cardSet) {
if (!curFiles.contains(cardSet)) {
return;
}
boolean isStillValid = false;
for (Category curCategory : curCategories) {
if (CategoryUtils.recursiveCardFileCollect(curCategory).contains(cardSet)) {
isStillValid = true;
break;
}
}
if (!isStillValid) {
curFiles.remove(cardSet);
}
fireTableDataChanged();
}
};
}
public List<Category> getCurCatgories() {
return curCategories;
}
/**
* Returns the files to be contained currently within this model.
*/
public Collection<CardFile> getCurFiles() {
return new ArrayList<CardFile>(curFiles);
}
private StringifiedScheduleDate getNextScheduleDate(LTMCollection ltmCollection, CardFile rowFile) {
if (ltmCollection.isEmpty())
return new StringifiedScheduleDate(Utils.getRB().getString("CardTableModel.noValidSlides"), Integer.MAX_VALUE);
Item nextItem = Collections.min(ltmCollection, new Comparator<Item>() {
public int compare(Item o1, Item o2) {
return ((LTMItem) o1).getNextScheduledDate().compareTo(((LTMItem) o2).getNextScheduledDate());
}
});
Date nextScheduleDate = ((LTMItem) nextItem).getNextScheduledDate();
int dayDiff = ScheduleUtils.getDayDiff(nextScheduleDate, ScheduleUtils.getToday());
String msg;
if (dayDiff <= 0) {
msg = Utils.getRB().getString("CardTableModel.when.today");
String numNewItems;
Integer numAllItems;
if (dummyLtmManager.isScheduled(rowFile)) {
List<? extends Item> scheduledItems = dummyLtmManager.getScheduledItems(rowFile);
ArrayList<Item> newItems = ScheduleUtils.getNewItems(scheduledItems);
numAllItems = scheduledItems.size() - newItems.size();
numNewItems = newItems.size() > 0 ? " +" + newItems.size() : "";
} else {
Date predictedSchedDate = ScheduleUtils.getIncDate(ScheduleUtils.getToday(), 0);
List<? extends Item> scheduledItems = ltmCollection.predictItemsForDate(predictedSchedDate, true);
ArrayList<Item> newItems = ScheduleUtils.getNewItems(scheduledItems);
numAllItems = scheduledItems.size() - ScheduleUtils.getNewItems(scheduledItems).size();
numNewItems = newItems.size() > 0 ? " +?" : "";
}
msg += " (" + (numAllItems) + numNewItems + ")";
} else {
Date predictedSchedDate = ScheduleUtils.getIncDate(ScheduleUtils.getToday(), dayDiff < 0 ? 0 : dayDiff);
List<LTMItem> allItems = ltmCollection.predictItemsForDate(predictedSchedDate, true);
if (dayDiff == 1) {
msg = Utils.getRB().getString("CardTableModel.when.tomorrow");
} else {
msg = "in " + dayDiff + " " + Utils.getRB().getString("CardTableModel.when.days");
}
msg += " (" + allItems.size() + ")";
}
return new StringifiedScheduleDate(msg, dayDiff);
}
public LTMProcessManager getDummyLtmManager() {
return dummyLtmManager;
}
}