content: /******************************************************************************
  * Copyright 2013-2016 LASIGE                                                  *
  *                                                                             *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may     *
  * not use this file except in compliance with the License. You may obtain a   *
  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0           *
  *                                                                             *
  * Unless required by applicable law or agreed to in writing, software         *
  * distributed under the License is distributed on an "AS IS" BASIS,           *
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.    *
  * See the License for the specific language governing permissions and         *
  * limitations under the License.                                              *
  *                                                                             *
  *******************************************************************************
  * Alignment search dialog box for the GUI.                                    *
  *                                                                             *
  * @author Daniel Faria, Catarina Martins                                      *
  ******************************************************************************/
  package aml.ui;
  
  import java.awt.CardLayout;
  import java.awt.Dialog;
  import java.awt.Dimension;
  import java.awt.Font;
  import java.awt.GraphicsEnvironment;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.KeyEvent;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Set;
  
  import javax.swing.AbstractAction;
  import javax.swing.Action;
  import javax.swing.BoxLayout;
  import javax.swing.JButton;
  import javax.swing.JComboBox;
  import javax.swing.JComponent;
  import javax.swing.JLabel;
  import javax.swing.JOptionPane;
  import javax.swing.JPanel;
  import javax.swing.JTextArea;
  import javax.swing.JDialog;
  import javax.swing.KeyStroke;
  import javax.swing.border.EmptyBorder;
  
  import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
  
  import aml.AML;
  import aml.match.Alignment;
  import aml.match.Mapping;
  import aml.util.MapSorter;
  
  public class SearchAlignment extends JDialog implements ActionListener
  {
  	
  //Attributes
  	
  	private static final long serialVersionUID = -3901206021275961468L;
  	private AML aml;
  	private JPanel dialogPanel, searchPanel, resultsPanel;
  	private CardLayout cl;
  	private JButton cancel, find, back, select, quit;
  	private JTextArea searchField;
  	private JComboBox<String> mappingSelector;
  	private ArrayList<String> mappings;
  	private String[] results;
  	private Font boldFont;
  	
  //Constructor
  	
  	public SearchAlignment()
  	{
  		super();
  		
  		this.setTitle("Search Alignment");
  		this.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
  		this.setPreferredSize(new Dimension(700,140));
  		
  		aml = AML.getInstance();
  		
  		//The containing panel
          dialogPanel = new JPanel();
          cl = new CardLayout();
          dialogPanel.setLayout(cl);
  		
          //The search panel
  		JLabel desc = new JLabel("Enter Search Term: (min 3 characters)");
  		Font font = desc.getFont();
  		boldFont = new Font(font.getFontName(), Font.BOLD, font.getSize());
  		desc.setFont(boldFont);
  		JPanel labelPanel = new JPanel();
  		labelPanel.add(desc);
  		Alignment a = AML.getInstance().getAlignment();
  		int total = a.size();
  		mappings = new ArrayList<String>(total);
  		for(int i = 0; i < total; i++)
  		{
  			Mapping m = a.get(i);
  			String map = aml.getSource().getName(m.getSourceId());
  			map += " = ";
  			map += aml.getTarget().getName(m.getTargetId());
  			mappings.add(map);
  		}
  		searchField = new JTextArea(1,60);
  		searchField.setEditable(true);    
  	    KeyStroke keyStroke = KeyStroke.getKeyStroke("ENTER");
          Object actionKey = searchField.getInputMap(
                  JComponent.WHEN_FOCUSED).get(keyStroke);
  		searchField.getActionMap().put(actionKey, wrapper);
          AutoCompleteDecorator.decorate(searchField,mappings,false);
  		JPanel selectionPanel = new JPanel();
  		selectionPanel.add(searchField);
  		cancel = new JButton("Cancel");
  		cancel.setPreferredSize(new Dimension(70,28));
  		cancel.addActionListener(this);
  		find = new JButton("Find");
  		find.setPreferredSize(new Dimension(70,28));
  		find.addActionListener(this);
  		JPanel buttonPanel = new JPanel();
  		buttonPanel.add(cancel);
  		buttonPanel.add(find);
  		
  		searchPanel = new JPanel();
  		searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.PAGE_AXIS));
  		searchPanel.add(labelPanel);
  		searchPanel.add(selectionPanel);
  		searchPanel.add(buttonPanel);
  		
         	dialogPanel.add(searchPanel, "Search");
          cl.show(dialogPanel, "Search");
  
  		this.add(dialogPanel);
  		
          this.pack();
  		GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment();
  		int left = g.getCenterPoint().x - (int)(this.getPreferredSize().width / 2);
  		this.setLocation(left, 0);
          this.setVisible(true);
  	}
  	
  //Public Methods
  	
  	@Override	
  	public void actionPerformed(ActionEvent e)
  	{
  		Object o = e.getSource();
  		if(o == cancel || o == quit)
  		{
  			this.dispose();
  		}
  		else if(o == back)
  		{
  			cl.show(dialogPanel, "Search");
  		}
  		else if(o == select)
  		{
  			this.dispose();
  			aml.goTo(mappings.indexOf(results[mappingSelector.getSelectedIndex()]));
  			new ViewMapping();
  		}
  		else if(o == find)
  		{
  			find();
  		}
  	}
  	
  	public void keyPressed(KeyEvent e)
  	{
  	    int key = e.getKeyCode();
  
  	    if(key == KeyEvent.VK_ENTER)
  	    {
  	    	find();
  	    }
  	}
  
  //Private Methods
  	
  	private void find()
  	{
  		//Get the entered text
  		String text = searchField.getText();
  		//Check that the user entered a valid input (at least 3 consecutive characters)
  		if(text.matches(".*[a-zA-Z0-9]{3,}.*"))
  		{
  			//If so, clean the input
  			text = text.toLowerCase().trim();
  			text = text.replaceAll("\t\n\f\r", " ");
  			//If the input is a listed mapping go there
  			int index = mappings.indexOf(text);
  			if(index > -1)
  			{
  				this.dispose();
  				aml.goTo(index);
  				new ViewMapping();
  			}
  			//Otherwise, search for it
  			else
  			{
  				search(text);
  				buildResultsPanel();
  		       	dialogPanel.add(resultsPanel, "Results");
  		        cl.show(dialogPanel, "Results");
  			}
  		}
  		else
  		{
  			JOptionPane.showMessageDialog(this, "Invalid input!\n" + 
  					"Please enter a word with at least three characters!",
  					"Error", JOptionPane.ERROR_MESSAGE); 
  		}
  	}
  	
  	private void search(String query)
  	{
  		HashMap<String,Double> hits = new HashMap<String,Double>();
  		for(String m : mappings)
  		{
  			if(m.contains(query))
  				hits.put(m, similarity(m,query));
  		}
  		Set<String> orderedHits = MapSorter.sortDescending(hits).keySet();
  		results = new String[hits.size()];
  		results = orderedHits.toArray(results);
  	}
  
  	private Double similarity(String mapping, String query)
  	{
  		return 1.0*query.length()/mapping.length();
  	}
  	
  	private void buildResultsPanel()
  	{
  		if(results.length == 0)
  		{
  			JLabel desc = new JLabel("No Results Found!");
  			desc.setFont(boldFont);
  			JPanel labelPanel = new JPanel();
  			labelPanel.setBorder(new EmptyBorder(20,0,18,0));
  			labelPanel.add(desc);
  			
  			back = new JButton("Back");
  			back.setPreferredSize(new Dimension(70,28));
  			back.addActionListener(this);
  			quit = new JButton("Quit");
  			quit.setPreferredSize(new Dimension(70,28));
  			quit.addActionListener(this);
  			JPanel buttonPanel = new JPanel();
  			buttonPanel.add(back);
  			buttonPanel.add(quit);
  			
  			resultsPanel = new JPanel();
  			resultsPanel.setLayout(new BoxLayout(resultsPanel, BoxLayout.PAGE_AXIS));
  			resultsPanel.add(labelPanel);
  			resultsPanel.add(buttonPanel);
  		}
  		else
  		{
  			JLabel desc = new JLabel("Select a Mapping from the Search Results:");
  			desc.setFont(boldFont);
  			JPanel labelPanel = new JPanel();
  			labelPanel.add(desc);
  			
  			mappingSelector = new JComboBox<String>(results);
  			mappingSelector.setPreferredSize(new Dimension(650,28));
  			mappingSelector.setSelectedIndex(0);
  			JPanel selectionPanel = new JPanel();
  			selectionPanel.add(mappingSelector);
  			
  			back = new JButton("Back");
  			back.setPreferredSize(new Dimension(70,28));
  			back.addActionListener(this);
  			select = new JButton("Select");
  			select.setPreferredSize(new Dimension(70,28));
  			select.addActionListener(this);
  			JPanel buttonPanel = new JPanel();
  			buttonPanel.add(back);
  			buttonPanel.add(select);
  			
  			resultsPanel = new JPanel();
  			resultsPanel.setLayout(new BoxLayout(resultsPanel, BoxLayout.PAGE_AXIS));
  			resultsPanel.add(labelPanel);
  			resultsPanel.add(selectionPanel);
  			resultsPanel.add(buttonPanel);
  		}
  	}
  	
  	private Action wrapper = new AbstractAction()
  	{
  		private static final long serialVersionUID = 424525650588931780L;
  
  		@Override
          public void actionPerformed(ActionEvent ae)
          {
              find.doClick();
          }
      };
  }