package nars.gui.output;
import automenta.vivisect.Video;
import automenta.vivisect.swing.NPanel;
import java.awt.BorderLayout;
import static java.awt.BorderLayout.NORTH;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.border.MatteBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import nars.util.EventEmitter.EventObserver;
import nars.util.Events.FrameEnd;
import nars.util.Events.TaskAdd;
import nars.util.Events.TaskRemove;
import nars.NAR;
import nars.entity.Concept;
import nars.entity.Task;
import nars.entity.TaskLink;
import nars.entity.TruthValue;
import nars.gui.WrapLayout;
import nars.storage.Memory;
/**
*
* @author me
*/
public class TaskTree extends NPanel implements EventObserver, Runnable {
long updatePeriodMS = 250;
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Tasks");
private DefaultTreeModel model;
Map<Task, DefaultMutableTreeNode> nodes = new ConcurrentHashMap();
private final JTree tree;
private final NAR nar;
final WeakHashMap<Task, TaskLabel> components = new WeakHashMap<>();
float priorityThreshold = 0.01f;
final Set<TreeNode> needRefresh = Collections.synchronizedSet(new HashSet());
final ConcurrentLinkedDeque<Task> toAdd = new ConcurrentLinkedDeque<>();
final ConcurrentLinkedDeque<Task> toRemove = new ConcurrentLinkedDeque<>();
long lastUpdateTime = 0;
boolean needsRestart = false;
boolean showingJudgments = false;
boolean showingQuestions = true;
boolean showingGoals = true;
public TaskTree(NAR nar) {
super(new BorderLayout());
tree = new JTree();
model = new DefaultTreeModel(root);
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
tree.setModel(model);
tree.setCellRenderer(new CustomDefaultRenderer());
this.nar = nar;
JPanel menu = new JPanel(new WrapLayout(FlowLayout.LEFT));
JCheckBox showJudgments = new JCheckBox("Judgments");
showJudgments.setSelected(showingJudgments);
showJudgments.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
showingJudgments = showJudgments.isSelected();
reset();
}
});
menu.add(showJudgments);
JCheckBox showQuestions = new JCheckBox("Questions");
showQuestions.setSelected(showingQuestions);
showQuestions.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
showingQuestions = showQuestions.isSelected();
reset();
}
});
menu.add(showQuestions);
JCheckBox showGoals = new JCheckBox("Goals");
showGoals.setSelected(showingGoals);
showGoals.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
showingGoals = showGoals.isSelected();
reset();
}
});
menu.add(showGoals);
add(menu, NORTH);
add(new JScrollPane(tree), BorderLayout.CENTER);
reset();
}
protected void reset() {
components.clear();
nodes.clear();
toAdd.clear();
toRemove.clear();
root.removeAllChildren();
lastUpdateTime = 0;
needsRestart = true;
}
@Override
public void onShowing(boolean b) {
nar.memory.event.set(this, b, TaskAdd.class, FrameEnd.class /*, TaskRemove.class*/);
}
public void add(Task t) {
if (isActive(t))
toAdd.add(t);
}
public DefaultMutableTreeNode getNode(final Task t) {
return nodes.get(t);
}
public DefaultMutableTreeNode newNode(final Task t) {
DefaultMutableTreeNode existing = nodes.get(t);
if (existing != null) {
return existing;
}
//String key = t.name().toString();
DefaultMutableTreeNode d = new DefaultMutableTreeNode(t);
nodes.put(t, d);
return d;
}
protected boolean isActive(final Task t) {
if ((t.sentence.isJudgment()) && (!showingJudgments)) return false;
if ((t.sentence.isQuestion()) && (!showingQuestions)) return false;
if ((t.sentence.isGoal()) && (!showingGoals)) return false;
return t.getPriority() >= priorityThreshold;
}
/** get all tasks in the system by iterating all newTasks, novelTasks, Concept TaskLinks */
//not part of Memory.java anymore as this is not something nars_core needs!
public Set<Task> getTasks(Memory mem, boolean includeTaskLinks, boolean includeNewTasks, boolean includeNovelTasks) {
Set<Task> t = new HashSet();
if (includeTaskLinks) {
for (Concept c : mem) {
for (TaskLink tl : c.taskLinks) {
t.add(tl.targetTask);
}
}
}
if (includeNewTasks)
t.addAll(mem.newTasks);
if (includeNovelTasks)
for (Task n : mem.novelTasks)
t.add(n);
return t;
}
public void update() {
//TODO get existing Tasks at the next frame event by new method: memory.getTasks() which iterates all concepts tasklinks
if (needsRestart) {
Set<Task> tasks = getTasks(nar.memory, true, false, false);
for (Task t : tasks)
add(t);
}
//remove dead tasks
for (Task t : nodes.keySet()) {
if (!isActive(t))
toRemove.add(t);
}
long now = System.currentTimeMillis();
if (now - lastUpdateTime > updatePeriodMS) {
SwingUtilities.invokeLater(this);
}
}
@Override public void run() {
for (Task t : toRemove) {
DefaultMutableTreeNode node = nodes.get(t);
if (node!=null) {
TreeNode p = node.getParent();
node.removeFromParent();
needRefresh.add(p);
needRefresh.remove(node);
}
nodes.remove(t);
components.remove(t);
toAdd.remove(t);
}
for (Task t : toAdd) {
Task parent = t.getParentTask();
if (parent!=null && parent.equals(t)) {
//System.err.println(t + " has parentTask equal to itself");
parent = null;
}
DefaultMutableTreeNode tnode = getNode(t);
if (tnode != null) {
continue;
}
tnode = newNode(t);
if (parent == null) {
//System.out.println(tnode + " add to root");
root.add(tnode);
needRefresh.add(root);
} else {
DefaultMutableTreeNode pnode = getNode(parent);
if (pnode != null) {
//System.out.println(tnode + "Adding to: " + pnode);
if (tnode.getParent()!=null) { //pnode.isNodeAncestor(tnode)) {
tnode.removeFromParent();
}
pnode.add(tnode);
needRefresh.add(pnode);
} else {
//missing parent, reparent to root?
//System.out.println(tnode + " add to root by default");
root.add(tnode);
needRefresh.add(root);
}
}
}
for (TaskLabel t : components.values())
t.updateTask();
for (TreeNode t : needRefresh)
model.reload(t);
needRefresh.clear();
toAdd.clear();
toRemove.clear();
repaint();
lastUpdateTime = System.currentTimeMillis();
}
public class TaskLabel extends JLabel {
private final Task task;
public TaskLabel(Task t) {
this.task = t;
setOpaque(false);
setFont(Video.monofont);
updateTask();
}
protected void updateTask() {
final Task t = task;
Concept con = nar.memory.concept(t.getTerm());
float conPri = 0;
if (con == null) {
/*System.err.println("TaskTree: " + t + " missing concept. either memory was reset or concept should have been created but wasnt.");*/
//toRemove.add(t);
}
else {
conPri = con.getPriority();
}
float taskPri = t.getPriority();
TruthValue desire = t.getDesire();
Color iColor;
if (desire!=null) {
float confidence = t.getDesire().getConfidence();
iColor = new Color(0,confidence/1.5f,conPri/1.5f,0.75f + 0.25f * confidence);
}
else {
iColor = new Color(0,0,conPri/1.5f,1f);
}
setBorder(new MatteBorder(0,15,0,0,iColor));
setForeground(Color.WHITE);
setOpaque(true);
final float hue = 0.3f + 0.5f * conPri;
Color c = Color.getHSBColor(hue, 0.4f, conPri * 0.2f);
//setBackground(new Color(1f-taskPri/4f,1f,1f-taskPri/4f));
setBackground(c);
setFont(Video.monofont.deriveFont(14f + taskPri * 4f));
setText(t.toStringExternal2());
}
}
protected void updateComponent(Task t, JLabel c) {
if (c instanceof TaskLabel)
((TaskLabel)c).updateTask();
}
protected class CustomDefaultRenderer
extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
// Allow the original renderer to set up the label
/*Component c = super.getTreeCellRendererComponent(
tree, value, selected,
expanded, leaf, row,
hasFocus);*/
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
Object v = node.getUserObject();
if (v instanceof Task) {
TaskLabel c = new TaskLabel((Task)v);
components.put((Task)v, c);
return c;
}
return super.getTreeCellRendererComponent(
tree, value, selected,
expanded, leaf, row,
hasFocus);
}
}
@Override
public void event(Class channel, Object[] arguments) {
// if (channel == OUT.class) {
// Object o = arguments[0];
// if (o instanceof Task) {
// add((Task) o);
// }
// }
if (channel == TaskAdd.class) {
add((Task)arguments[0]);
}
else if (channel == TaskRemove.class) {
//..
}
else if (channel == FrameEnd.class) {
update();
}
}
}