package com.opendoorlogistics.components.barchart.basechart;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.tables.ODLTableReadOnly;
import com.opendoorlogistics.api.ui.Disposable;
import com.opendoorlogistics.codefromweb.JCheckBoxTree;
import com.opendoorlogistics.codefromweb.JCheckBoxTree.CheckChangeEvent;
import com.opendoorlogistics.core.utils.StringIdTreeNode;
import com.opendoorlogistics.core.utils.ui.JCheckBoxTreeExt;
import com.opendoorlogistics.core.utils.ui.JCheckBoxTreeExt.CheckBoxTreeState;
public abstract class BaseChartPanel extends JPanel implements Disposable, JCheckBoxTree.CheckChangeEventListener {
private final ChartPanel chartPanel;
private final JCheckBoxTreeExt tree;
private ODLTableReadOnly table;
protected BaseConfig config;
protected final ODLApi api;
@Override
public void checkStateChanged(CheckChangeEvent event) {
// create filtered chart
int [] filtered = getFilteredRows(tree.saveCheckedState(), table);
chartPanel.setChart(createChart(table,filtered));
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
public void update(BaseConfig config,ODLTableReadOnly table){
this.config =config;
this.table = table;
// update tree
CheckBoxTreeState state = tree.saveState();
tree.setModel(createTreeModel());
tree.restoreState(state);
// refresh the chart from the checked state
checkStateChanged(null);
}
public BaseChartPanel(ODLApi api, BaseConfig config,ODLTableReadOnly table ) {
this.config = config;
this.table = table;
this.api = api;
JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
setLayout(new BorderLayout());
add(splitter,BorderLayout.CENTER);
setPreferredSize(new Dimension(500, 270));
chartPanel = new ChartPanel(createChart(table,null));
chartPanel.setFillZoomRectangle(true);
chartPanel.setMouseWheelEnabled(true);
splitter.setRightComponent(chartPanel);
JPanel westPanel = new JPanel();
westPanel.setLayout(new BorderLayout());
JLabel label = new JLabel("Categories");
label.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
westPanel.add(label,BorderLayout.NORTH);
add(westPanel,BorderLayout.WEST);
tree = new JCheckBoxTreeExt();
JScrollPane scrollPane = new JScrollPane(tree);
westPanel.add(scrollPane,BorderLayout.CENTER);
westPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
tree.setModel(createTreeModel());
tree.checkAll(true);
tree.setRootVisible(false);
tree.addCheckChangeEventListener(this);
tree.setBorder(BorderFactory.createEmptyBorder(10, 0, 2, 5));
JPanel showHidePanel = new JPanel();
showHidePanel.setLayout(new GridLayout(1, 2));
showHidePanel.add(new JButton(new AbstractAction("Show all") {
@Override
public void actionPerformed(ActionEvent e) {
tree.checkAll(true);
}
}));
showHidePanel.add(new JButton(new AbstractAction("Hide all") {
@Override
public void actionPerformed(ActionEvent e) {
tree.checkAll(false);
}
}));
westPanel.add(showHidePanel,BorderLayout.SOUTH);
splitter.setLeftComponent(westPanel);
}
protected abstract JFreeChart createChart(ODLTableReadOnly table, int [] rowFilter);
protected TreeModel createTreeModel(){
StringIdTreeNode<Void> nodeTree = buildCategoryTree(table);
return new DefaultTreeModel(nodeTree.exportToJTree(null));
}
private StringIdTreeNode<Void> buildCategoryTree(ODLTableReadOnly table){
// build single level tree right now
int n = table.getRowCount();
StringIdTreeNode<Void> ret = new StringIdTreeNode<>("Visible categories", null);
for (int row = 0; row < n; row++) {
// loop over nb filter groups + 1 (as the last one is the category)
StringIdTreeNode<Void> parent = ret;
for(int groupIndx =0 ; groupIndx <= config.getNbFilterGroupLevels() ; groupIndx++){
String groupVal = readGroupVal(table, row, groupIndx);
StringIdTreeNode<Void> newParentTreeNode = parent.get(groupVal);
if(newParentTreeNode==null){
newParentTreeNode = parent.add(groupVal, null);
}
parent = newParentTreeNode;
}
}
return ret;
}
/**
* @param table
* @param row
* @param groupIndx
* @return
*/
protected String readGroupVal(ODLTableReadOnly table, int row, int groupIndx) {
String groupVal = (String) table.getValueAt(row, groupIndx);
if (groupVal == null) {
// treat null as blank
groupVal = "";
}
return groupVal;
}
private int[] getFilteredRows(StringIdTreeNode<Boolean> filter, ODLTableReadOnly table){
int n = table.getRowCount();
int [] ret = new int[n];
if(filter==null){
for(int row =0 ; row<n;row++){
ret[row] = row;
}
return ret;
}else{
int nf=0;
for(int row =0 ; row<n;row++){
// loop over nb filter groups + 1 (as the last one is the category)
boolean pass = true;
StringIdTreeNode<Boolean> parent = filter;
for(int groupIndx =0 ; groupIndx <= config.getNbFilterGroupLevels() && pass; groupIndx++){
String groupVal = readGroupVal(table, row, groupIndx);
StringIdTreeNode<Boolean> node = parent.get(groupVal);
if(node==null){
// Node unknown so show the category. This means the tree is out-of-date
// with the category data which should hopefully never happen
break;
}
Boolean val = node.getLeaf();
if(val == null){
// Should hopefully never happen
break;
}else{
pass = val;
}
parent = node;
}
if(pass){
ret[nf++] =row;
}
}
return Arrays.copyOf(ret, nf);
}
}
}