/*
 * Copyright 2007 - 2016 the original author or authors.
 *
 * 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.
 */
package net.sf.jailer.ui;

import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;

import net.sf.jailer.datamodel.Column;
import net.sf.jailer.datamodel.Table;
import net.sf.jailer.ui.scrollmenu.JScrollPopupMenu;

/**
 * Editor for multi-line SQL conditions with parameter support.
 * 
 * @author Ralf Wisser
 */
public class ConditionEditor extends javax.swing.JDialog {
    
	private boolean ok;
	private ParameterSelector parameterSelector;
	
    /** Creates new form ConditionEditor */
    public ConditionEditor(java.awt.Frame parent, ParameterSelector.ParametersGetter parametersGetter) {
        super(parent, true);
        initComponents();
        setLocation(400, 150);
        setSize(600, 400);
        
        if (parametersGetter != null) {
        	paramsPanel.add(parameterSelector = new ParameterSelector(this, editorPane, parametersGetter));
        } else {
        	paramsPanel.setVisible(false);
        }
        
        editorPane.setContentType("text/sql");
		table1dropDown.setText(null);
		table1dropDown.setIcon(dropDownIcon);
		table2dropDown.setText(null);
		table2dropDown.setIcon(dropDownIcon);
		table1dropDown.addMouseListener(new java.awt.event.MouseAdapter() {
			public void mousePressed(java.awt.event.MouseEvent evt) {
				openColumnDropDownBox(table1dropDown, table1alias, table1);
			}
			
			public void mouseEntered(java.awt.event.MouseEvent evt) {
				table1dropDown.setEnabled(false);
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
            	table1dropDown.setEnabled(true);
           }
        });
		table2dropDown.addMouseListener(new java.awt.event.MouseAdapter() {
			public void mousePressed(java.awt.event.MouseEvent evt) {
				openColumnDropDownBox(table2dropDown, table2alias, table2);
			}
			
			public void mouseEntered(java.awt.event.MouseEvent evt) {
				table2dropDown.setEnabled(false);
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
            	table2dropDown.setEnabled(true);
           }
        });
    }
    
    /**
     * Opens a drop-down box which allows the user to select columns for restriction definitions.
     */
	private void openColumnDropDownBox(JLabel label, String alias, Table table) {
		JPopupMenu popup = new JScrollPopupMenu();
		List<String> columns = new ArrayList<String>();
		
		for (Column c: table.getColumns()) {
			columns.add(alias + "." + c.name);
		}
		if (addPseudoColumns) {
			columns.add("");
			columns.add(alias + ".$IS_SUBJECT");
			columns.add(alias + ".$DISTANCE");
			columns.add("$IN_DELETE_MODE");
			columns.add("NOT $IN_DELETE_MODE");
		}
		
		for (final String c: columns) {
			if (c.equals("")) {
				popup.add(new JSeparator());
				continue;
			}
			JMenuItem m = new JMenuItem(c);
			m.addActionListener(new ActionListener () {
				public void actionPerformed(ActionEvent e) {
					if (editorPane.isEnabled()) {
						if (editorPane.isEditable()) {
							editorPane.replaceSelection(c);
						}
					}
				}
			});
			popup.add(m);
		}
		UIUtil.fit(popup);
		popup.show(label, 0, label.getHeight());
	}
	
	/** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        jPanel1 = new javax.swing.JPanel();
        paramsPanel = new javax.swing.JPanel();
        jPanel2 = new javax.swing.JPanel();
        table1label = new javax.swing.JLabel();
        table1name = new javax.swing.JLabel();
        table1dropDown = new javax.swing.JLabel();
        jLabel1 = new javax.swing.JLabel();
        table2label = new javax.swing.JLabel();
        table2name = new javax.swing.JLabel();
        table2dropDown = new javax.swing.JLabel();
        jPanel3 = new javax.swing.JPanel();
        okButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();
        jScrollPane2 = new javax.swing.JScrollPane();
        editorPane = new javax.swing.JEditorPane();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        getContentPane().setLayout(new java.awt.GridBagLayout());

        jPanel1.setLayout(new java.awt.GridBagLayout());

        paramsPanel.setMinimumSize(new java.awt.Dimension(150, 0));
        paramsPanel.setLayout(new javax.swing.BoxLayout(paramsPanel, javax.swing.BoxLayout.LINE_AXIS));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 20;
        gridBagConstraints.gridy = 10;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 1.0;
        jPanel1.add(paramsPanel, gridBagConstraints);

        jPanel2.setLayout(new java.awt.GridBagLayout());

        table1label.setText(" Table ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 3, 0);
        jPanel2.add(table1label, gridBagConstraints);

        table1name.setFont(new java.awt.Font("DejaVu Sans", 0, 12));
        table1name.setText("jLabel1");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 3, 0);
        jPanel2.add(table1name, gridBagConstraints);

        table1dropDown.setFont(new java.awt.Font("DejaVu Sans", 0, 12));
        table1dropDown.setText("jLabel1");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(4, 0, 3, 0);
        jPanel2.add(table1dropDown, gridBagConstraints);

        jLabel1.setText(" ");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 10;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.weightx = 1.0;
        jPanel2.add(jLabel1, gridBagConstraints);

        table2label.setText("jLabel2");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0);
        jPanel2.add(table2label, gridBagConstraints);

        table2name.setFont(new java.awt.Font("DejaVu Sans", 0, 12));
        table2name.setText("jLabel2");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0);
        jPanel2.add(table2name, gridBagConstraints);

        table2dropDown.setFont(new java.awt.Font("DejaVu Sans", 0, 12));
        table2dropDown.setText("jLabel2");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0);
        jPanel2.add(table2dropDown, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 10;
        gridBagConstraints.gridy = 5;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        jPanel1.add(jPanel2, gridBagConstraints);

        okButton.setText(" Ok ");
        okButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                okButtonActionPerformed(evt);
            }
        });
        jPanel3.add(okButton);

        cancelButton.setText(" Cancel ");
        cancelButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cancelButtonActionPerformed(evt);
            }
        });
        jPanel3.add(cancelButton);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 10;
        gridBagConstraints.gridy = 20;
        gridBagConstraints.gridwidth = 20;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        jPanel1.add(jPanel3, gridBagConstraints);

        jScrollPane2.setViewportView(editorPane);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 10;
        gridBagConstraints.gridy = 10;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel1.add(jScrollPane2, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 10;
        gridBagConstraints.gridy = 10;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        getContentPane().add(jPanel1, gridBagConstraints);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
        ok = true;
        setVisible(false);
    }//GEN-LAST:event_okButtonActionPerformed

    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
        ok = false;
        setVisible(false);
    }//GEN-LAST:event_cancelButtonActionPerformed

    private Table table1, table2;
    private String table1alias, table2alias;
    private boolean addPseudoColumns;
    
    /**
     * Edits a given condition.
     * 
     * @param condition the condition
     * @return new condition or <code>null</code>, if user canceled the editor
     */
	public String edit(String condition, String table1label, String table1alias, Table table1, String table2label, String table2alias, Table table2, boolean addPseudoColumns) {
		condition = toMultiLine(condition);
		this.table1 = table1;
		this.table2 = table2;
		this.table1alias = table1alias;
		this.table2alias = table2alias;
		this.addPseudoColumns = addPseudoColumns;
		if (table1 != null) {
			this.table1label.setText(" " + table1label + " ");
			this.table1name.setText("  " + table1.getName());
			this.table1label.setVisible(true);
			this.table1name.setVisible(true);
			this.table1dropDown.setVisible(true);
		} else {
			this.table1label.setVisible(false);
			this.table1name.setVisible(false);
			this.table1dropDown.setVisible(false);
		}
		if (table2 != null) {
			this.table2label.setText(" " + table2label + " ");
			this.table2name.setText("  " + table2.getName());
			this.table2label.setVisible(true);
			this.table2name.setVisible(true);
			this.table2dropDown.setVisible(true);
		} else {
			this.table2label.setVisible(false);
			this.table2name.setVisible(false);
			this.table2dropDown.setVisible(false);
		}
		ok = false;
		editorPane.setText(condition);
		editorPane.setCaretPosition(0);
		if (parameterSelector != null) {
			parameterSelector.updateParameters();
		}
		setVisible(true);
		return ok? editorPane.getText() : null;
	}
    
    /**
     * Converts multi-line text into single line presentation.
     */
    public static String toSingleLine(String s) {
    	StringBuilder sb = new StringBuilder();
    	for (int i = 0; i < s.length(); ++i) {
    		char c = s.charAt(i);
    		if (c == '\\') {
        		sb.append("\\\\");
    		} else if (c == '\n') {
        		sb.append("\\n");
    		} else if (c == '\r') {
    			sb.append("\\r");
            } else {
        		sb.append(c);
        	}
    	}
    	return sb.toString();
    }

    /**
     * Converts single line presentation into multi-line text.
     */
    public static String toMultiLine(String s) {
    	StringBuilder sb = new StringBuilder();
    	boolean esc = false;
    	for (int i = 0; i < s.length(); ++i) {
    		char c = s.charAt(i);
    		if (c == '\\') {
    			if (esc) {
    				esc = false;
    			} else {
    				esc = true;
    				continue;
    			}
    		}
			if (esc && c == 'n') {
				c = '\n';
			} else if (esc && c == 'r') {
				c = '\r';
			}
			sb.append(c);
    		esc = false;
    	}
    	return sb.toString();
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cancelButton;
    private javax.swing.JEditorPane editorPane;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JButton okButton;
    private javax.swing.JPanel paramsPanel;
    private javax.swing.JLabel table1dropDown;
    private javax.swing.JLabel table1label;
    private javax.swing.JLabel table1name;
    private javax.swing.JLabel table2dropDown;
    private javax.swing.JLabel table2label;
    private javax.swing.JLabel table2name;
    // End of variables declaration//GEN-END:variables
    
    private Icon dropDownIcon;
    {
		String dir = "/net/sf/jailer/resource";
		
		// load images
		try {
			dropDownIcon = new ImageIcon(getClass().getResource(dir + "/dropdown.png"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
    
    private static final long serialVersionUID = -5169934807182707970L;
}
