package beast.app.beauti;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import beast.app.draw.BEASTObjectPanel;
import beast.app.draw.BooleanInputEditor;
import beast.app.draw.InputEditor;
import beast.app.draw.SmallButton;
import beast.core.BEASTInterface;
import beast.core.Distribution;
import beast.core.Input;
import beast.core.Operator;
import beast.core.State;
import beast.core.util.CompoundDistribution;
import beast.core.util.Log;
import beast.evolution.alignment.Taxon;
import beast.evolution.alignment.TaxonSet;
import beast.evolution.operators.TipDatesRandomWalker;
import beast.evolution.tree.Tree;
import beast.math.distributions.MRCAPrior;
import beast.math.distributions.OneOnX;
public class MRCAPriorInputEditor extends InputEditor.Base {
private static final long serialVersionUID = 1L;
public MRCAPriorInputEditor(BeautiDoc doc) {
super(doc);
}
@Override
public Class<?> type() {
return MRCAPrior.class;
}
@Override
public void init(Input<?> input, BEASTInterface beastObject, final int listItemNr, ExpandOption isExpandOption, boolean addButtons) {
m_bAddButtons = addButtons;
m_input = input;
m_beastObject = beastObject;
this.itemNr= listItemNr;
Box itemBox = Box.createHorizontalBox();
MRCAPrior prior = (MRCAPrior) beastObject;
String text = prior.getID();
JButton taxonButton = new JButton(text);
// taxonButton.setMinimumSize(Base.PREFERRED_SIZE);
// taxonButton.setPreferredSize(Base.PREFERRED_SIZE);
itemBox.add(taxonButton);
taxonButton.addActionListener(e -> {
List<?> list = (List<?>) m_input.get();
MRCAPrior prior2 = (MRCAPrior) list.get(itemNr);
try {
TaxonSet taxonset = prior2.taxonsetInput.get();
List<Taxon> originalTaxa = new ArrayList<>();
originalTaxa.addAll(taxonset.taxonsetInput.get());
Set<Taxon> candidates = getTaxonCandidates(prior2);
TaxonSetDialog dlg = new TaxonSetDialog(taxonset, candidates, doc);
if (dlg.showDialog()) {
if (dlg.taxonSet.taxonsetInput.get().size() == 0) {
JOptionPane.showMessageDialog(doc.beauti, "At least one taxon should be included in the taxon set",
"Error specifying taxon set", JOptionPane.ERROR_MESSAGE);
taxonset.taxonsetInput.get().addAll(originalTaxa);
return;
}
prior2.taxonsetInput.setValue(dlg.taxonSet, prior2);
int i = 1;
String id = dlg.taxonSet.getID();
while (doc.pluginmap.containsKey(dlg.taxonSet.getID()) && doc.pluginmap.get(dlg.taxonSet.getID()) != dlg.taxonSet) {
dlg.taxonSet.setID(id + i);
i++;
}
BEASTObjectPanel.addPluginToMap(dlg.taxonSet, doc);
prior2.setID(dlg.taxonSet.getID() + ".prior");
}
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
refreshPanel();
});
if (prior.distInput.getType() == null) {
try {
prior.distInput.setValue(new OneOnX(), prior);
prior.distInput.setValue(null, prior);
} catch (Exception e) {
// TODO: handle exception
}
}
List<BeautiSubTemplate> availableBEASTObjects = doc.getInputEditorFactory().getAvailableTemplates(prior.distInput, prior, null, doc);
JComboBox<BeautiSubTemplate> comboBox = new JComboBox<>(availableBEASTObjects.toArray(new BeautiSubTemplate[]{}));
comboBox.setName(text+".distr");
if (prior.distInput.get() != null) {
String id = prior.distInput.get().getID();
//id = BeautiDoc.parsePartition(id);
id = id.substring(0, id.indexOf('.'));
for (BeautiSubTemplate template : availableBEASTObjects) {
if (template.classInput.get() != null && template.shortClassName.equals(id)) {
comboBox.setSelectedItem(template);
}
}
} else {
comboBox.setSelectedItem(BeautiConfig.NULL_TEMPLATE);
}
comboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
@SuppressWarnings("unchecked")
JComboBox<BeautiSubTemplate> comboBox = (JComboBox<BeautiSubTemplate>) e.getSource();
BeautiSubTemplate template = (BeautiSubTemplate) comboBox.getSelectedItem();
List<?> list = (List<?>) m_input.get();
MRCAPrior prior = (MRCAPrior) list.get(itemNr);
//System.err.println("PRIOR" + beastObject2);
// try {
// prior.m_distInput.setValue(beastObject2, prior);
// } catch (Exception e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
try {
//BEASTObject beastObject2 =
template.createSubNet(new PartitionContext(""), prior, prior.distInput, true);
} catch (Exception e1) {
e1.printStackTrace();
}
refreshPanel();
}
});
itemBox.add(comboBox);
JCheckBox isMonophyleticdBox = new JCheckBox(doc.beautiConfig.getInputLabel(prior, prior.isMonophyleticInput.getName()));
isMonophyleticdBox.setName(text+".isMonophyletic");
isMonophyleticdBox.setSelected(prior.isMonophyleticInput.get());
isMonophyleticdBox.setToolTipText(prior.isMonophyleticInput.getHTMLTipText());
isMonophyleticdBox.addActionListener(new MRCAPriorActionListener(prior));
itemBox.add(isMonophyleticdBox);
JButton deleteButton = new SmallButton("-", true);
deleteButton.setToolTipText("Delete this calibration");
deleteButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Log.warning.println("Trying to delete a calibration");
List<?> list = (List<?>) m_input.get();
MRCAPrior prior = (MRCAPrior) list.get(itemNr);
doc.disconnect(prior, "prior", "distribution");
doc.disconnect(prior, "tracelog", "log");
if (prior.onlyUseTipsInput.get()) {
disableTipSampling(m_beastObject, doc);
}
doc.unregisterPlugin(prior);
refreshPanel();
}
});
itemBox.add(Box.createGlue());
itemBox.add(deleteButton);
add(itemBox);
}
public static void customConnector(BeautiDoc doc) {
Object o0 = doc.pluginmap.get("prior");
if (o0 != null && o0 instanceof CompoundDistribution) {
CompoundDistribution p = (CompoundDistribution) o0;
for (Distribution p0 : p.pDistributions.get()) {
if (p0 instanceof MRCAPrior) {
MRCAPrior prior = (MRCAPrior) p0;
if (prior.treeInput.get() != null) {
boolean isInState = false;
for (BEASTInterface o : prior.treeInput.get().getOutputs()) {
if (o instanceof State) {
isInState = true;
break;
}
}
if (!isInState) {
doc.disconnect(prior, "prior", "distribution");
doc.disconnect(prior, "tracelog", "log");
if (prior.onlyUseTipsInput.get()) {
disableTipSampling(prior, doc);
}
doc.unregisterPlugin(prior);
return;
}
}
}
}
}
}
Set<Taxon> getTaxonCandidates(MRCAPrior prior) {
Set<Taxon> candidates = new HashSet<>();
Tree tree = prior.treeInput.get();
String [] taxa = null;
if (tree.m_taxonset.get() != null) {
try {
TaxonSet set = tree.m_taxonset.get();
set.initAndValidate();
taxa = set.asStringList().toArray(new String[0]);
} catch (Exception e) {
taxa = prior.treeInput.get().getTaxaNames();
}
} else {
taxa = prior.treeInput.get().getTaxaNames();
}
for (String taxon : taxa) {
candidates.add(doc.getTaxon(taxon));
}
return candidates;
}
/**
* class to deal with toggling monophyletic flag on an MRCAPrior *
*/
class MRCAPriorActionListener implements ActionListener {
MRCAPrior m_prior;
MRCAPriorActionListener(MRCAPrior prior) {
m_prior = prior;
}
@Override
public void actionPerformed(ActionEvent e) {
try {
m_prior.isMonophyleticInput.setValue(((JCheckBox) e.getSource()).isSelected(), m_prior);
refreshPanel();
} catch (Exception ex) {
Log.warning.println("PriorListInputEditor " + ex.getMessage());
}
}
}
InputEditor tipsonlyEditor;
public InputEditor createTipsonlyEditor() throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
BooleanInputEditor e = new BooleanInputEditor (doc) {
private static final long serialVersionUID = 1L;
@Override
public void init(Input<?> input, BEASTInterface beastObject, int itemNr, ExpandOption isExpandOption,
boolean addButtons) {
super.init(input, beastObject, itemNr, isExpandOption, addButtons);
// hack to get to JCheckBox
Component [] components = getComponents();
((JCheckBox) components[0]).addActionListener(e -> {
JCheckBox src = (JCheckBox) e.getSource();
if (src.isSelected()) {
enableTipSampling();
} else {
disableTipSampling(m_beastObject, doc);
}
});
}
};
MRCAPrior prior = (MRCAPrior) m_beastObject;
Input<?> input = prior.onlyUseTipsInput;
e.init(input, prior, -1, ExpandOption.FALSE, false);
return e;
}
// add TipDatesRandomWalker (if not present) and add to list of operators
private void enableTipSampling() {
// First, create/find the operator
TipDatesRandomWalker operator = null;
MRCAPrior prior = (MRCAPrior) m_beastObject;
TaxonSet taxonset = prior.taxonsetInput.get();
taxonset.initAndValidate();
// see if an old operator still hangs around -- happens when toggling the TipsOnly checkbox a few times
for (BEASTInterface o : taxonset.getOutputs()) {
if (o instanceof TipDatesRandomWalker) {
operator = (TipDatesRandomWalker) o;
}
}
if (operator == null) {
operator = new TipDatesRandomWalker();
operator.initByName("tree", prior.treeInput.get(), "taxonset", taxonset, "windowSize", 1.0, "weight", 1.0);
}
operator.setID("tipDatesSampler." + taxonset.getID());
doc.mcmc.get().setInputValue("operator", operator);
}
// remove TipDatesRandomWalker from list of operators
private static void disableTipSampling(BEASTInterface m_beastObject, BeautiDoc doc) {
// First, find the operator
TipDatesRandomWalker operator = null;
MRCAPrior prior = (MRCAPrior) m_beastObject;
TaxonSet taxonset = prior.taxonsetInput.get();
// We cannot rely on the operator ID created in enableTipSampling()
// since the taxoneset name may have changed.
// However, if there is an TipDatesRandomWalker with taxonset as input, we want to remove it.
for (BEASTInterface o : taxonset.getOutputs()) {
if (o instanceof TipDatesRandomWalker) {
operator = (TipDatesRandomWalker) o;
}
}
if (operator == null) {
// should never happen
return;
}
// remove from list of operators
Object o = doc.mcmc.get().getInput("operator");
if (o instanceof Input<?>) {
Input<List<Operator>> operatorInput = (Input<List<Operator>>) o;
List<Operator> operators = operatorInput.get();
operators.remove(operator);
}
}
}