package jp.ac.osaka_u.sanken.sparql.plugin.compare;

import java.awt.GridBagLayout;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JButton;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

import javax.swing.ButtonGroup;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;

import jp.ac.osaka_u.sanken.sparql.SparqlAccessor;
import jp.ac.osaka_u.sanken.sparql.SparqlQueryListener;

public class ComparePanel extends JPanel {

	private static final long serialVersionUID = 1L;
	private JTextField endpointsTextField = null;  //  @jve:decl-index=0:visual-constraint="542,195"
	private JButton endpointsButton = null;
	private JTextField wordsTextField = null;
	private JButton wordsButton = null;
	private JScrollPane logScrollPane = null;
	private JTextArea logTextArea = null;
	private JLabel endpointsLabel = null;
	private JLabel wordsLabel = null;
	private JButton executeButton = null;
	private JPanel optPanel = null;
	private JPanel matchPanel = null;
	private JRadioButton matchFullRadioButton = null;
	private JRadioButton matchPartRadioButton = null;
	private JRadioButton findSubjectRadioButton = null;
	private JRadioButton findObjectRadioButton = null;
	private JRadioButton findLabelObjectRadioButton = null;
	private JSeparator separator = null;
	private JLabel outputLabel = null;
	private JTextField outputTextField = null;
	private JButton outputRefButton = null;
	private Component parent;

	/**
	 * This is the default constructor
	 */
	public ComparePanel(Component parent) {
		super();
		initialize();
		this.parent= parent;
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		GridBagConstraints gridBagConstraints111 = new GridBagConstraints();
		gridBagConstraints111.gridx = 2;
		gridBagConstraints111.insets = new Insets(0, 0, 0, 10);
		gridBagConstraints111.gridy = 3;
		GridBagConstraints gridBagConstraints10 = new GridBagConstraints();
		gridBagConstraints10.fill = GridBagConstraints.HORIZONTAL;
		gridBagConstraints10.gridy = 3;
		gridBagConstraints10.weightx = 1.0;
		gridBagConstraints10.insets = new Insets(5, 10, 5, 0);
		gridBagConstraints10.gridx = 1;
		GridBagConstraints gridBagConstraints9 = new GridBagConstraints();
		gridBagConstraints9.gridx = 0;
		gridBagConstraints9.gridy = 3;
		outputLabel = new JLabel("Output File");
		GridBagConstraints gridBagConstraints8 = new GridBagConstraints();
		gridBagConstraints8.gridx = 0;
		gridBagConstraints8.gridwidth = 3;
		gridBagConstraints8.fill = GridBagConstraints.HORIZONTAL;
		gridBagConstraints8.gridy = 4;
		GridBagConstraints gridBagConstraints5 = new GridBagConstraints();
		gridBagConstraints5.gridx = 0;
		gridBagConstraints5.insets = new Insets(0, 10, 0, 0);
		gridBagConstraints5.gridy = 2;
		wordsLabel = new JLabel();
		wordsLabel.setText("Word List File");
		GridBagConstraints gridBagConstraints4 = new GridBagConstraints();
		gridBagConstraints4.gridx = 0;
		gridBagConstraints4.insets = new Insets(0, 10, 0, 0);
		gridBagConstraints4.gridy = 0;
		endpointsLabel = new JLabel();
		endpointsLabel.setText("Endpoint List File");
		GridBagConstraints gridBagConstraints3 = new GridBagConstraints();
		gridBagConstraints3.fill = GridBagConstraints.BOTH;
		gridBagConstraints3.gridy = 5;
		gridBagConstraints3.weightx = 1.0;
		gridBagConstraints3.weighty = 1.0;
		gridBagConstraints3.insets = new Insets(10, 10, 10, 10);
		gridBagConstraints3.gridwidth = 3;
		gridBagConstraints3.gridx = 0;
		GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
		gridBagConstraints2.gridx = 2;
		gridBagConstraints2.insets = new Insets(0, 0, 0, 10);
		gridBagConstraints2.gridy = 2;
		GridBagConstraints gridBagConstraints11 = new GridBagConstraints();
		gridBagConstraints11.fill = GridBagConstraints.HORIZONTAL;
		gridBagConstraints11.gridy = 2;
		gridBagConstraints11.weightx = 1.0;
		gridBagConstraints11.insets = new Insets(5, 10, 5, 0);
		gridBagConstraints11.gridx = 1;
		GridBagConstraints gridBagConstraints1 = new GridBagConstraints();
		gridBagConstraints1.fill = GridBagConstraints.NONE;
		gridBagConstraints1.gridy = 0;
		gridBagConstraints1.insets = new Insets(10, 0, 0, 10);
		gridBagConstraints1.gridx = 2;
		GridBagConstraints gridBagConstraints = new GridBagConstraints();
		gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
		gridBagConstraints.gridy = 0;
		gridBagConstraints.insets = new Insets(14, 10, 5, 0);
		gridBagConstraints.gridx = 1;
		this.setLayout(new GridBagLayout());
		this.add(getEndpointsTextField(), gridBagConstraints);
		this.add(getEndpointsButton(), gridBagConstraints1);
		this.add(getWordsTextField(), gridBagConstraints11);
		this.add(getWordsButton(), gridBagConstraints2);
		this.add(getLogScrollPane(), gridBagConstraints3);
		this.add(endpointsLabel, gridBagConstraints4);
		this.add(wordsLabel, gridBagConstraints5);
		this.add(getOptPanel(), gridBagConstraints8);
		this.add(outputLabel, gridBagConstraints9);
		this.add(getOutputTextField(), gridBagConstraints10);
		this.add(getOutputRefButton(), gridBagConstraints111);
	}

	/**
	 * This method initializes endpointsTextField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getEndpointsTextField() {
		if (endpointsTextField == null) {
			endpointsTextField = new JTextField();
			endpointsTextField.setEditable(false);
		}
		return endpointsTextField;
	}

	/**
	 * This method initializes endpointsButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getEndpointsButton() {
		if (endpointsButton == null) {
			endpointsButton = new JButton("Ref");
			endpointsButton.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent arg0) {
					selectFile(getEndpointsTextField(), JFileChooser.OPEN_DIALOG);
					validateExecute();
				}
			});
		}
		return endpointsButton;
	}

	/**
	 * This method initializes wordsTextField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getWordsTextField() {
		if (wordsTextField == null) {
			wordsTextField = new JTextField();
			wordsTextField.setEditable(false);
		}
		return wordsTextField;
	}

	/**
	 * This method initializes wordsButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getWordsButton() {
		if (wordsButton == null) {
			wordsButton = new JButton("Ref");
			wordsButton.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent arg0) {
					selectFile(getWordsTextField(), JFileChooser.OPEN_DIALOG);
					validateExecute();
				}
			});
		}
		return wordsButton;
	}

	/**
	 * This method initializes logScrollPane	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getLogScrollPane() {
		if (logScrollPane == null) {
			logScrollPane = new JScrollPane();
			logScrollPane.setViewportView(getLogTextArea());
		}
		return logScrollPane;
	}

	/**
	 * This method initializes logTextArea	
	 * 	
	 * @return javax.swing.JTextArea	
	 */
	private JTextArea getLogTextArea() {
		if (logTextArea == null) {
			logTextArea = new JTextArea();
			logTextArea.getDocument().addDocumentListener(new DocumentListener() {
				
				@Override
				public void removeUpdate(DocumentEvent arg0) {
				}
				
				@Override
				public void changedUpdate(DocumentEvent arg0) {
				}
				@Override
				public void insertUpdate(DocumentEvent e) {
					final Document doc = logTextArea.getDocument();
					final Element root = doc.getDefaultRootElement();
					if(root.getElementCount() <= 100){ // TODO 100はベタ書き
						return;
					}
					EventQueue.invokeLater(new Runnable() {
						@Override
						public void run() {
							removeLines(doc, root);
						}
					});
					logTextArea.setCaretPosition(doc.getLength());
				}
				private void removeLines(Document doc, Element root) {
					Element fl = root.getElement(0);
					try{
						doc.remove(0, fl.getEndOffset());
					}catch(BadLocationException ble) {
						System.out.println(ble);
					}
				}
			});
		}
		return logTextArea;
	}
	
	void addLogText(String log){
		String[] logs = log.split("\n");
		for (String l : logs){
//			getLogTextArea().append((getLogTextArea().getDocument().getLength() > 0) ? "\n" + l : l);
			getLogTextArea().append(l);
		}
		if (log.endsWith("\n")){
			getLogTextArea().append("\n");
		}
		
		getLogTextArea().setCaretPosition(getLogTextArea().getDocument().getLength());
	}


	/**
	 * This method initializes executeButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getExecuteButton() {
		if (executeButton == null) {
			executeButton = new JButton("Execute");
			executeButton.setEnabled(false);
			executeButton.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent arg0) {
					if (getExecuteButton().getText().equals("Execute")){
						setProcessing(true);
						doCompare();
					} else {
						executeButton.setEnabled(false);
						doStop();
					}
				}
			});
		}
		return executeButton;
	}

	/**
	 * This method initializes optPanel	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getOptPanel() {
		if (optPanel == null) {
			optPanel = new JPanel();
			optPanel.setLayout(new BorderLayout());
			optPanel.add(getExecuteButton(), BorderLayout.EAST);
			optPanel.add(getMatchPanel(), BorderLayout.CENTER);
		}
		return optPanel;
	}

	/**
	 * This method initializes matchPanel	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getMatchPanel() {
		if (matchPanel == null) {
			FlowLayout flowLayout = new FlowLayout();
			flowLayout.setVgap(0);
			matchPanel = new JPanel();
			matchPanel.setLayout(flowLayout);
			matchPanel.add(getMatchFullRadioButton(), null);
			matchPanel.add(getMatchPartRadioButton(), null);
			matchPanel.add(getSeparator(), null);
			matchPanel.add(getFindSubjectRadioButton(), null);
			matchPanel.add(getFindObjectRadioButton(), null);
			matchPanel.add(getFindLabelObjectRadioButton(), null);
			ButtonGroup gp1 = new ButtonGroup();
			gp1.add(getMatchFullRadioButton());
			gp1.add(getMatchPartRadioButton());
			getMatchFullRadioButton().setSelected(true);
			ButtonGroup gp2 = new ButtonGroup();
			gp2.add(getFindSubjectRadioButton());
			gp2.add(getFindObjectRadioButton());
			gp2.add(getFindLabelObjectRadioButton());
			getFindSubjectRadioButton().setSelected(true);
		}
		return matchPanel;
	}
	
	private int getFindType(){
		if (getFindSubjectRadioButton().isSelected()){
			return SparqlAccessor.FIND_TARGET_SUBJECT;
		}
		if (getFindObjectRadioButton().isSelected()){
			return SparqlAccessor.FIND_TARGET_OBJECT;
		}
		if (getFindLabelObjectRadioButton().isSelected()){
			return SparqlAccessor.FIND_TARGET_LABEL_OBJECT;
		}
		return SparqlAccessor.FIND_TARGET_ALL;
	}
	
	private boolean isFullMatch(){
		return getMatchFullRadioButton().isSelected();
	}

	private void selectFile(JTextField tf, int dialogType){
		JFileChooser fileChooser = new JFileChooser("./");
		fileChooser.setDialogType(dialogType);
		// ファイル選択結果取得
		int result = fileChooser.showOpenDialog(this);
		File file = fileChooser.getSelectedFile();
		if (result == JFileChooser.CANCEL_OPTION || file == null) {
			// キャンセル押下、または、ファイル選択なしのため何もしない
			return;
		}
		
		try {
			tf.setText(file.getCanonicalPath());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void validateExecute(){
		boolean enable = true;
		if (getEndpointsTextField().getText().isEmpty() ||
				getWordsTextField().getText().isEmpty() || 
				getOutputTextField().getText().isEmpty()){
			enable = false;
		}
		getExecuteButton().setEnabled(enable);
	}

	Compare compare = null;
	
	private void doCompare(){
		compare = new Compare(new File(getWordsTextField().getText()), new File(getEndpointsTextField().getText()), new SparqlQueryListener() {
			
			@Override
			public void sparqlExecuted(String query) {
				addLogText(query);
			}
		});

		compare.outputResult(getFindType(), isFullMatch(), new File(getOutputTextField().getText()), new CompareResultListener() {

			@Override
			public void uncaughtException(Thread thread, Throwable e) {
				JOptionPane.showMessageDialog(parent, "Execute error:"+e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
				setProcessing(false);
			}

			@Override
			public void resultReceived(boolean result) {
				setProcessing(false);
			}
		});
	}
	
	private void doStop(){
		if (compare != null){
			compare.stop();
		}
	}
	

	private void setProcessing(boolean isProcess){
		this.getEndpointsButton().setEnabled(!isProcess);
		this.getWordsButton().setEnabled(!isProcess);
		this.getOutputRefButton().setEnabled(!isProcess);
		this.getMatchFullRadioButton().setEnabled(!isProcess);
		this.getMatchPartRadioButton().setEnabled(!isProcess);
		this.getFindSubjectRadioButton().setEnabled(!isProcess);
		this.getFindObjectRadioButton().setEnabled(!isProcess);
		this.getFindLabelObjectRadioButton().setEnabled(!isProcess);
		if (isProcess){
			this.getExecuteButton().setText("Stop");
		} else {
			this.getExecuteButton().setText("Execute");
		}
		this.getExecuteButton().setEnabled(true);
	}
	
	/**
	 * This method initializes matchFullRadioButton	
	 * 	
	 * @return javax.swing.JRadioButton	
	 */
	private JRadioButton getMatchFullRadioButton() {
		if (matchFullRadioButton == null) {
			matchFullRadioButton = new JRadioButton("Full Match");
		}
		return matchFullRadioButton;
	}

	/**
	 * This method initializes matchPartRadioButton	
	 * 	
	 * @return javax.swing.JRadioButton	
	 */
	private JRadioButton getMatchPartRadioButton() {
		if (matchPartRadioButton == null) {
			matchPartRadioButton = new JRadioButton("Part Match");
		}
		return matchPartRadioButton;
	}

	/**
	 * This method initializes findSubjectRadioButton	
	 * 	
	 * @return javax.swing.JRadioButton	
	 */
	private JRadioButton getFindSubjectRadioButton() {
		if (findSubjectRadioButton == null) {
			findSubjectRadioButton = new JRadioButton("Find Subject");
		}
		return findSubjectRadioButton;
	}

	/**
	 * This method initializes findObjectRadioButton	
	 * 	
	 * @return javax.swing.JRadioButton	
	 */
	private JRadioButton getFindObjectRadioButton() {
		if (findObjectRadioButton == null) {
			findObjectRadioButton = new JRadioButton("Find All Object");
		}
		return findObjectRadioButton;
	}

	/**
	 * This method initializes findLabelObjectRadioButton	
	 * 	
	 * @return javax.swing.JRadioButton	
	 */
	private JRadioButton getFindLabelObjectRadioButton() {
		if (findLabelObjectRadioButton == null) {
			findLabelObjectRadioButton = new JRadioButton("Find Label Object");
		}
		return findLabelObjectRadioButton;
	}

	/**
	 * This method initializes separator	
	 * 	
	 * @return JSeparator	
	 */
	private JSeparator getSeparator() {
		if (separator == null) {
			separator = new JSeparator(SwingConstants.VERTICAL);
			separator.setPreferredSize(new Dimension(5, 20));
		}
		return separator;
	}

	/**
	 * This method initializes outputTextField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getOutputTextField() {
		if (outputTextField == null) {
			outputTextField = new JTextField();
			outputTextField.setEditable(false);
		}
		return outputTextField;
	}

	/**
	 * This method initializes RefButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getOutputRefButton() {
		if (outputRefButton == null) {
			outputRefButton = new JButton("Ref");
			outputRefButton.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent e) {
					selectFile(getOutputTextField(), JFileChooser.SAVE_DIALOG);
					validateExecute();
				}
			});
		}
		return outputRefButton;
	}
	
}  //  @jve:decl-index=0:visual-constraint="10,10"
