package org.systemsbiology.infocore.namegrabber;

import javax.swing.*;
import javax.xml.rpc.ServiceException;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.systemsbiology.SynonymServiceSoap;
import org.systemsbiology.SynonymServiceLocator;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.debug.FormDebugPanel;
import com.jgoodies.forms.debug.FormDebugUtils;
import com.jgoodies.forms.factories.ButtonBarFactory;
import cytoscape.Cytoscape;
import cytoscape.CyNode;
import cytoscape.visual.NodeAppearanceCalculator;
import cytoscape.visual.VisualStyle;
import cytoscape.visual.calculators.GenericNodeLabelCalculator;
import cytoscape.visual.mappings.PassThroughMapping;
import giny.model.Node;


/**
 * Swing GUI for connecting to IdService synonym server. A SynonymServiceSoap
 * object is required; see setSynonymService(...).
 *
 * @author cbare, dtenenba
 */
public class NameGrabberDialog extends JFrame {
    private static Logger log = Logger.getLogger(NameGrabberDialog.class);
    SynonymServiceSoap synonym;
    SynonymDbComboBoxModel sources = new SynonymDbComboBoxModel(new String[]{"SELECT SOURCE"});
    SynonymDbComboBoxModel targets = new SynonymDbComboBoxModel(new String[]{"SELECT TARGET"});
    private JComboBox sourceComboBox;
    private JPanel panel;
    private boolean debug = false; // set to true to see panel gridlines and debug info in console


    public NameGrabberDialog() throws HeadlessException {
        super("Get Aliases");
        createGui();
    }


    public NameGrabberDialog(GraphicsConfiguration gc) {
        super("Get Aliases", gc);
        createGui();
    }

    /**
     * @param synonym required SynonymServiceSoap instance
     */
    public void setSynonymService(SynonymServiceSoap synonym) {
        this.synonym = synonym;
        try {
            String[] databases = synonym.getDatabases();
            sources.clear();
            sources.addAll(databases);
            sources.setSelectedItem("SELECT SOURCE");
        }
        catch (RemoteException e) {
            JOptionPane.showMessageDialog(panel, "Error connecting to web service! " + e.getMessage());
            e.printStackTrace();
        }
    }

    private void createGui() {

        FormLayout layout = new FormLayout("pref, 3dlu, pref, 3dlu, pref, 3dlu, pref, 3dlu:g",
                "pref, 3dlu, pref");

        if (debug) {
            panel = new FormDebugPanel(layout);
        } else {
            panel = new JPanel(layout);
        }
        CellConstraints cc = new CellConstraints();
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        sourceComboBox = new JComboBox(sources);
        JComboBox targetComboBox = new JComboBox(targets);

        // col, row

        panel.add(new JLabel("Translate from "),
                cc.xy(1,1));
        panel.add(sourceComboBox, cc.xy(3, 1));
        panel.add(new JLabel(" to "),
                cc.xy(5,1));
        panel.add(targetComboBox, cc.xy(7, 1));


        sources.selectFirst();
        targets.selectFirst();

        sourceComboBox.addActionListener(new SourceSelectionListener());


        JButton translateButton = new JButton(new TranslateAction());
        JButton revertButton = new JButton(new RevertAction());
        JButton exitButton = new JButton(new ExitAction());

        JPanel buttonBar = ButtonBarFactory.buildLeftAlignedBar(translateButton, revertButton, exitButton);
        panel.add(buttonBar, cc.xyw(1,3,8));

        if (debug) FormDebugUtils.dumpAll(panel);

        getContentPane().add(panel);

        pack();
    }


    public void exit() {
        log.info("exiting...");
        setVisible(false);
        dispose();
    }

    public String[] getNodeListAsStringArray(String sourceName, List<CyNode> list) {
        List<String> nodeNames = new ArrayList<String>();
        for (CyNode node : list) {
            String sourceAttr = Cytoscape.getNodeAttributes().getStringAttribute(node.getIdentifier(),sourceName);
            if (sourceAttr == null || sourceAttr.trim().equals("")) {
                nodeNames.add(node.getIdentifier());
            } else {
                nodeNames.add(sourceAttr);
            }
        }
        return nodeNames.toArray(new String[0]);
    }

    @SuppressWarnings("unchecked")
    public List<CyNode> getCyNodesList() {
        List<CyNode> nodeList = new ArrayList<CyNode>();
        Iterator<Node> it = Cytoscape.getCurrentNetwork().nodesIterator();
        while (it.hasNext()) {
            nodeList.add((CyNode)it.next());
        }
        return nodeList;
    }

    public void translate() {
        String source = String.valueOf(sources.getSelectedItem());
        String target = String.valueOf(targets.getSelectedItem());
        List<CyNode> nodeList = getCyNodesList();
        String[] sourceTerms = getNodeListAsStringArray(source, nodeList);
        try {
            String[][] mappings = synonym.getSynonyms(sourceTerms, source, target);
            boolean foundMatches = false;
            if (mappings == null) {
                //log.warn("no mappings found");
            } else {
                for (String[] targetTerms : mappings) {
                    if (targetTerms == null) {
                        //log.info("no mapping found");
                    } else if (targetTerms.length == 0) {
                        //log.warn("no matches");
                    } else {
                        foundMatches = true;
                    }
                }
            }
            if (!foundMatches) {
                JOptionPane.showMessageDialog(Cytoscape.getDesktop(),
                  "No mappings from " + source + " to " + target + ", no action taken.");
                return;
            }
            changeNetwork(nodeList, source, target, mappings);
        }
        catch (RemoteException e) {
            JOptionPane.showMessageDialog(panel, "Error connecting to network! " + e.getMessage());
            e.printStackTrace();
        }
    }

    public void changeNetwork(List<CyNode> nodeList, String sourceName, String targetName, String[][] mappings) {
        for(int i = 0; i < nodeList.size(); i++) {
            String[] aliases = mappings[i];
            List<String> aliasesList = new ArrayList<String>();

            for(String alias : aliases) {
                aliasesList.add(alias);
            }

            CyNode node = nodeList.get(i);
            String newName = "";

            if (aliasesList.size()  > 0) {
                newName = aliasesList.get(0);
            }

            String sourceAttr = Cytoscape.getNodeAttributes().getStringAttribute(node.getIdentifier(),sourceName);
            if (sourceAttr == null  || sourceAttr.trim().equals("") ) {
                Cytoscape.getNodeAttributes().setAttribute(node.getIdentifier(),sourceName, node.getIdentifier());
            } else {
                Cytoscape.getNodeAttributes().setAttribute(node.getIdentifier(),sourceName,sourceAttr);
            }
            Cytoscape.getNodeAttributes().setListAttribute(node.getIdentifier(),targetName + " Aliases", aliasesList);
            Cytoscape.getNodeAttributes().setAttribute(node.getIdentifier(),targetName, newName);
        }
        createNodeLabel(targetName);
        Cytoscape.getCurrentNetworkView().redrawGraph(false,false);
    }

    private void createNodeLabel(String attributeName) {
        VisualStyle currentStyle = Cytoscape.getVisualMappingManager().getVisualStyle();
        NodeAppearanceCalculator nac = currentStyle.getNodeAppearanceCalculator();
        // this just needs the name of the attribute you're setting, first param can be any string
        PassThroughMapping passThroughMapping = new PassThroughMapping("",
                attributeName);

        // always show the commonName as a label
        GenericNodeLabelCalculator nodeLabelCalculator = new
                GenericNodeLabelCalculator("Calculator Name", passThroughMapping);
        nac.setCalculator(nodeLabelCalculator);
    }


    public void selectSource(String source) {
        if (source != null && !"SELECT SOURCE".equals(source) && !"".equals(source) && !"null".equals(source)) {
            log.info("updating target, source=: " + source);
            String[] targetDbs;
            try {
                targetDbs = synonym.getMappings(source);
                targets.clear();
                targets.addAll(targetDbs);
                targets.setSelectedItem("SELECT TARGET");
            }
            catch (RemoteException e) {
                JOptionPane.showMessageDialog(panel, "Error connecting to network! " + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();

        SynonymServiceLocator ss = new SynonymServiceLocator();
        ss.setSynonymServiceSoapEndpointAddress("http://monocle.systemsbiology.net/SynonymServiceYeast/services/SynonymServiceSoap");

        try {
            SynonymServiceSoap synonym = ss.getSynonymServiceSoap();
            NameGrabberDialog gui = new NameGrabberDialog(frame.getGraphicsConfiguration());
            gui.setSynonymService(synonym);
            gui.setVisible(true);
        }
        catch (ServiceException ex) {
            ex.printStackTrace();
        }

    }

    private class ExitAction extends AbstractAction {

        public ExitAction() {
            super("Exit");
        }

        public void actionPerformed(ActionEvent e) {
            dispose();
        }
    }

    private class TranslateAction extends AbstractAction {

        public TranslateAction() {
            super("Translate");
        }

        public void actionPerformed(ActionEvent e) {
            translate();
        }
    }

    public class RevertAction extends AbstractAction {
        public RevertAction() {
            super("Revert to Canonical Name");
        }

        public void actionPerformed(ActionEvent e) {
            createNodeLabel("canonicalName");
            Cytoscape.getCurrentNetworkView().redrawGraph(false,false);
        }
    }

    private class SourceSelectionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            log.info("source selected");
            selectSource(String.valueOf(sourceComboBox.getSelectedItem()));
        }

    }

}
