package d2rq;

import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.LinkedList;

import de.fuberlin.wiwiss.d2rq.D2RQException;
import de.fuberlin.wiwiss.d2rq.map.Database;
import de.fuberlin.wiwiss.d2rq.mapgen.MappingGenerator;

import javax.swing.*;  

/**
 * Graphical interface for {@link MappingGenerator}.
 * 
 * @author Simeon Polfliet (simeon.polfliet@ensimag.imag.fr)
 * From the command line version of Richard Cyganiak (richard@cyganiak.de)
 * @version $Id: generate_mapping.java,v 2 2010/07/29 13:28:18 fatorange Exp $
 */
public class generate_mapping extends JPanel implements ActionListener{
	private final static String[] includedDrivers = {
		"com.mysql.jdbc.Driver"
	};

	private final static String DEFAULT_BASE_URI = "http://localhost:2020/";
	private final static int DEFAULT_ONTOLOGY_NUMBER = 16;
	
	/** Graphic elements */
	private static LinkedList<JTextField> fPrefix = new LinkedList<JTextField>();
	private static LinkedList<JTextField> fUri = new LinkedList<JTextField>();
	private static LinkedList<JTextField> fFile = new LinkedList<JTextField>();	
	private static JCheckBox bWordNet;
	private LinkedList<JButton> bRemove = new LinkedList<JButton>();
	private LinkedList<JButton> bUp = new LinkedList<JButton>();
	private LinkedList<JButton> bDown = new LinkedList<JButton>();
	private JTextField fUsername, fPassword, fBaseURI, fOutput, fDriver, fJdbcURI, fDbSchema;
	private JButton bAddOnto, bGenerate, bOk;
	private JLabel lUsername = new JLabel("Username");
	private JLabel lPassword = new JLabel("Password");
	private JLabel lBaseURI = new JLabel("Base URI");
	private JLabel lOutput = new JLabel("Output file");
	private JLabel lDriver = new JLabel("JDBC Driver");
	private JLabel lDbSchema = new JLabel("Database schema");
	private JLabel lJdbcURI = new JLabel("JDBC Database URL");
	private JLabel lOnto = new JLabel("List of ontologies to use");
	private JLabel lPrefix = new JLabel("Ontology prefix");
	private JLabel lURI = new JLabel("URI of the ontology");
	private JLabel lFile = new JLabel("Path of the file with the ontology description");
	private JLabel lGeneration = new JLabel("Generation of the mapping. Please wait.");
	private JLabel lErrorConec = new JLabel("Error connecting to the database");
	private JFileChooser fc = new JFileChooser();

	private MappingGenerator gen;
	private File outputFile = null;

	private static JFrame frame;
	private static JFrame messageBox;

	/**
	 * Standard constructor
	 */
	public generate_mapping(){
		super(new GridBagLayout());
		init();
		redraw();
	}

	/** 
	 * 
	 * @return List of ontology prefixes
	 */
	public LinkedList<String> getPrefix(){
		LinkedList<String> res = new LinkedList<String>();

		Iterator<JTextField> it = fPrefix.iterator();
		while(it.hasNext()){
			res.add(it.next().getText());
		}
		return res;
	}
	/**
	 * 
	 * @return List of ontology URI
	 */
	public LinkedList<String> getUri(){
		LinkedList<String> res = new LinkedList<String>();

		Iterator<JTextField> it = fUri.iterator();
		while(it.hasNext()){
			res.add(it.next().getText());
		}
		return res;
	}

	/**
	 * 
	 * @return List of ontologies to use. String format: pathFile@Uri@Prefix
	 */
	public static LinkedList<String> getOnto(){
		LinkedList<String> res = new LinkedList<String>();

		Iterator<JTextField> it1 = fFile.descendingIterator();
		Iterator<JTextField> it2 = fUri.descendingIterator();
		Iterator<JTextField> it3 = fPrefix.descendingIterator();

		while (it1.hasNext()){
			res.add(it1.next().getText() + "@" + it2.next().getText() + "@" +it3.next().getText());
		}
		return res;
	}

	/**
	 * 
	 * @return whether we should use WordNet or not
	 */
	public static boolean useWordNet(){
		return !bWordNet.isSelected();
	}

	/**
	 * Initialization of the graphic elements
	 */
	private void init(){
		fUsername = new JTextField(20);
		fPassword = new JTextField(20);
		fDbSchema = new JTextField(20);
		fDriver = new JTextField(20);
		fOutput = new JTextField(20);
		fBaseURI = new JTextField(20);
		fJdbcURI = new JTextField(20);

		fUsername.setText("root");
		fPassword.setText("pass");
		fDriver.setText("com.mysql.jdbc.Driver");
		fOutput.setText("mapping.n3");
		fJdbcURI.setText("jdbc:mysql://127.0.0.1/database");
		fBaseURI.setText("http://localhost:2020/");

		bAddOnto = new JButton("Add an ontology");
		bGenerate = new JButton("Generate mapping");
		bAddOnto.setActionCommand("add");
		bAddOnto.addActionListener(this);
		bGenerate.setActionCommand("generate");
		bGenerate.addActionListener(this);
		bOk = new JButton("Ok");
		bOk.setActionCommand("ok");
		bOk.addActionListener(this);
		bWordNet = new JCheckBox("Do not use WordNet (faster but less good results)");

		for (int i = 0; i < DEFAULT_ONTOLOGY_NUMBER; i++){
			fPrefix.add(new JTextField(10));
			fUri.add(new JTextField(25));
			fFile.add(new JTextField(25));
			bRemove.add(new JButton("Remove"));
			bUp.add(new JButton("Up"));
			bDown.add(new JButton("Down"));
		}

		Iterator<JTextField> it1 = fPrefix.iterator();
		Iterator<JTextField> it2 = fUri.iterator();
		Iterator<JTextField> it3 = fFile.iterator();
		Iterator<JButton> it4 = bRemove.iterator();
		Iterator<JButton> it5 = bUp.iterator();
		Iterator<JButton> it6 = bDown.iterator();
		for (int i = 0; i < DEFAULT_ONTOLOGY_NUMBER; i++){
			it4.next().setActionCommand("remove" + i);
			it5.next().setActionCommand("up" + i);
			it6.next().setActionCommand("down" + i);
		}
		it4 = bRemove.iterator();
		it5 = bUp.iterator();
		it6 = bDown.iterator();
		for (int i = 0; i < DEFAULT_ONTOLOGY_NUMBER; i++){
			it4.next().addActionListener(this);
			it5.next().addActionListener(this);
			it6.next().addActionListener(this);
		}

		it1.next().setText("dcterms");
		it2.next().setText("http://purl.org/dc/terms/");
		it3.next().setText("dcterms.owl");
		it1.next().setText("foaf");
		it2.next().setText("http://xmlns.com/foaf/0.1/");
		it3.next().setText("foaf.owl");
		it1.next().setText("dbpedia");
		it2.next().setText("http://dbpedia.org/ontology/");
		it3.next().setText("dbpedia.owl");
		it1.next().setText("skos");
		it2.next().setText("http://www.w3.org/2004/02/skos/core#");
		it3.next().setText("skos.owl");
		it1.next().setText("akt");
		it2.next().setText("http://www.aktors.org/ontology/portal#");
		it3.next().setText("akt.owl");
		it1.next().setText("sioc");
		it2.next().setText("http://rdfs.org/sioc/ns#");
		it3.next().setText("sioc.owl");
		it1.next().setText("cc");
		it2.next().setText("http://creativecommons.org/ns#");
		it3.next().setText("creativecommons.owl");
		it1.next().setText("geonames");
		it2.next().setText("http://www.geonames.org/ontology#");
		it3.next().setText("geonames.owl");
		it1.next().setText("time");
		it2.next().setText("http://www.w3.org/2006/time#");
		it3.next().setText("time.owl");
		it1.next().setText("bio");
		it2.next().setText("http://purl.org/vocab/bio/0.1/");
		it3.next().setText("biography.owl");
		it1.next().setText("rel");
		it2.next().setText("http://purl.org/vocab/relationship/");
		it3.next().setText("relationship.owl");
		it1.next().setText("cal");
		it2.next().setText("http://www.w3.org/2002/12/cal/ical#");
		it3.next().setText("ical.owl");
		it1.next().setText("food");
		it2.next().setText("http://www.w3.org/TR/2003/PR-owl-guide-20031209/food#");
		it3.next().setText("food.owl");
		it1.next().setText("vin");
		it2.next().setText("http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#");
		it3.next().setText("wine.owl");
		it1.next().setText("beer");
		it2.next().setText("http://www.purl.org/net/ontology/beer#");
		it3.next().setText("beer.owl");
		it1.next().setText("mo");
		it2.next().setText("http://purl.org/ontology/mo/");
		it3.next().setText("musicontology.owl");
	}
	
	/**
	 * Add an graphic component 
	 * @param comp the component to add
	 * @param c the GridBagConstraint
	 * @param x the horizontal position 
	 * @param y the vertical position
	 */
	private void addComp(Component comp, GridBagConstraints c, int x, int y){
		c.gridx = x;
		c.gridy = y;
		add(comp, c);
	}

	/**
	 * Display the graphic elements
	 */
	private void redraw(){
		this.removeAll();
		Iterator<JTextField> it1 = fPrefix.iterator();
		Iterator<JTextField> it2 = fUri.iterator();
		Iterator<JTextField> it3 = fFile.iterator();
		Iterator<JButton> it4 = bRemove.iterator();
		Iterator<JButton> it5 = bUp.iterator();
		Iterator<JButton> it6 = bDown.iterator();
		GridBagConstraints c = new GridBagConstraints();
		int i = 0;
		addComp(lUsername, c,0,i);
		addComp(fUsername, c,1,i++);
		addComp(lPassword, c,0,i);
		addComp(fPassword, c,1,i++);
		addComp(lDbSchema, c,0,i);
		addComp(fDbSchema, c,1,i++);
		addComp(lDriver, c,0,i);
		addComp(fDriver, c,1,i++);
		addComp(lOutput, c,0,i);
		addComp(fOutput, c,1,i++);
		addComp(lBaseURI, c,0,i);
		addComp(fBaseURI, c,1,i++);
		addComp(lJdbcURI, c,0,i);
		addComp(fJdbcURI, c,1,i++);
		addComp(lOnto, c,0,i++);
		addComp(lPrefix, c,0,i);
		addComp(lURI, c,1,i);
		addComp(lFile, c,2,i++);

		while (it1.hasNext()){
			addComp(it1.next(), c,0,i);
			addComp(it2.next(), c,1,i);
			addComp(it3.next(), c,2,i);	
			addComp(it4.next(), c,3,i);	
			addComp(it5.next(), c,4,i);	
			addComp(it6.next(), c,5,i++);	
		}
		it5 = bUp.iterator();
		it6 = bDown.descendingIterator();
		if (it5.hasNext()){
			it5.next().setEnabled(false);	
			it6.next().setEnabled(false);
			while(it5.hasNext()){
				it5.next().setEnabled(true);
				it6.next().setEnabled(true);
			}
		}
		
		addComp(bWordNet, c,1,i++);
		addComp(bAddOnto, c,1,i);
		addComp(bGenerate, c,2,i);

		frame.pack();
		frame.validate();
		frame.repaint();	
	}

	/**
	 * Switch the ontologies positions of row and row - 1
	 * Prerequisite : row > 0
	 * @param row the line number of the ontology
	 */
	private void switchOnto(int row){
		Iterator<JTextField> it1 = fPrefix.iterator();
		Iterator<JTextField> it2 = fUri.iterator();
		Iterator<JTextField> it3 = fFile.iterator();
		LinkedList<JTextField> tmp1 = new LinkedList<JTextField>();
		LinkedList<JTextField> tmp2 = new LinkedList<JTextField>();
		LinkedList<JTextField> tmp3 = new LinkedList<JTextField>();	
		JTextField t1, t2, t3;

		for (int j = 0; j < row - 1; j++){
			it1.next();
			it2.next();
			it3.next();
		}
		t1 = it1.next();
		t2 = it2.next();
		t3 = it3.next();
		it1.remove();
		it2.remove();
		it3.remove();
		tmp1.add(it1.next());
		tmp2.add(it2.next());
		tmp3.add(it3.next());
		tmp1.add(t1);
		tmp2.add(t2);
		tmp3.add(t3);
		it1.remove();
		it2.remove();
		it3.remove();		
		while (it1.hasNext()){
			tmp1.add(it1.next());
			tmp2.add(it2.next());
			tmp3.add(it3.next());
			it1.remove();
			it2.remove();
			it3.remove();
		}
		fPrefix.addAll(tmp1);
		fUri.addAll(tmp2);
		fFile.addAll(tmp3);
	}

	/**
	 * remove an ontology
	 * @param row the line number of the ontology
	 */
	private void removeOnto(int row){
		Iterator<JTextField> it1 = fPrefix.iterator();
		Iterator<JTextField> it2 = fUri.iterator();
		Iterator<JTextField> it3 = fFile.iterator();
		Iterator<JButton> it4 = bRemove.iterator();
		Iterator<JButton> it5 = bUp.iterator();
		Iterator<JButton> it6 = bDown.iterator();
		for (int j = 0; j <= row; j++){
			it1.next();
			it2.next();
			it3.next();
			it4.next();
			it5.next();
			it6.next();
		}
		it1.remove();
		it2.remove();
		it3.remove();
		it4.remove();
		it5.remove();
		it6.remove();
		while (it4.hasNext()){
			it4.next().setActionCommand("remove" + row);
			it5.next().setActionCommand("up" + row);
			it6.next().setActionCommand("down" + row);
			row++;
		}
	}

	/**
	 * Add an ontology
	 */
	private void addOnto(){
		int returnVal = fc.showOpenDialog(this);
		String fileName="";

		if (returnVal == JFileChooser.APPROVE_OPTION) {
			fileName = fc.getSelectedFile().getPath();
		}
		fPrefix.add(new JTextField(10));
		fUri.add(new JTextField(25));
		JTextField f = new JTextField(25);
		f.setText(fileName);
		fFile.add(f);

		JButton b = new JButton("Remove");
		b.setActionCommand("remove" + bRemove.size());
		b.addActionListener(this);
		bRemove.add(b);
		b = new JButton("Up");
		b.setActionCommand("up" + bUp.size());
		b.addActionListener(this);
		bUp.add(b);
		b = new JButton("Down");
		b.setActionCommand("down" + bDown.size());
		b.addActionListener(this);
		bDown.add(b);
	}

	/**
	 * Display a message box
	 * @param Title title of the box
	 * @param message message to put in the box
	 */
	private void displayMessage(String Title, JLabel message){
		messageBox = new JFrame(Title);
		messageBox.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		JPanel pane = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		c.insets = new Insets(10,10,10,10);
		c.gridx=0;
		c.gridy=0;
		pane.add(message,c);
		c.gridy = 1;
		pane.add(bOk,c);

		messageBox.add(pane);

		messageBox.pack();
		messageBox.setLocation(frame.getLocation().x + 400, frame.getLocation().y + 200);
		messageBox.setVisible(true);
	}

	/**
	 * Check if WordNet directory and the ontology files exist. 
	 * Display and error message when one file is not found
	 * @return true if all files exist, false otherwise
	 */
	private boolean checkFile(){
		Iterator<JTextField> it = fFile.iterator();
		while (it.hasNext()){
			String filePath = it.next().getText();
			File file = new File(filePath);
			if (!file.exists()){
				JLabel lErrorFile = new JLabel("Error, " + filePath + " not found");
				displayMessage("Error", lErrorFile);
				return false;
			}
		}
		if (!bWordNet.isSelected()){
			File file = new File("WordNet");
			if (!file.exists()){
				JLabel lErrorWN = new JLabel("Error, WordNet directory not found");
				displayMessage("Error", lErrorWN);
				return false;
			}
		}
		return true;
	}

	/**
	 * Start the mapping generation
	 */
	private void generate(){
		if(!checkFile()){
			return;
		}

		displayMessage("Generating...", lGeneration);

		for (int i = 0; i < includedDrivers.length; i++) {
			Database.registerJDBCDriverIfPresent(includedDrivers[i]);
		}

		String jdbc = fJdbcURI.getText();
		gen = new MappingGenerator(jdbc);
		if (!fUsername.getText().equals("")) {
			gen.setDatabaseUser(fUsername.getText());
		}
		if (!fPassword.getText().equals("")) {
			gen.setDatabasePassword(fPassword.getText());
		}
		if (!fDbSchema.getText().equals("")) {
			gen.setDatabaseSchema(fDbSchema.getText());
		}
		if (!fDriver.getText().equals("")) {
			gen.setJDBCDriverClass(fDriver.getText());
		}

		if (!fOutput.getText().equals("")) {
			outputFile = new File(fOutput.getText());
			gen.setMapNamespaceURI(outputFile.toURI().toString() + "#");
		} else {
			gen.setMapNamespaceURI("file:///stdout#");
		}
		gen.setInstanceNamespaceURI("");

		String baseURI = !this.fBaseURI.getText().equals("") ? this.fBaseURI.getText()
				: DEFAULT_BASE_URI;

		gen.setVocabNamespaceURI(baseURI + "vocab/resource/");

		try {
			gen.precalculation();
		} catch (D2RQException e){
			messageBox.dispose();
			displayMessage("Error", lErrorConec);			
			e.printStackTrace();
			return;		
		}
		messageBox.dispose();
		new check_mapping(frame, this, gen);
	}

	/**
	 * Finish the mapping generation
	 */
	public void endGeneration(){
		try {
			PrintStream out = (outputFile == null)
			? System.out
					: new PrintStream(new FileOutputStream(outputFile));
			gen.writeMapping(out, System.err);
		} catch (IOException ex) {
			System.err.println(ex.getMessage());
			System.exit(1);
		}

		System.exit(0);
	}

	/**
	 * Action handler
	 */
	public void actionPerformed(ActionEvent e) {

		if (e.getActionCommand().contains("up")){
			switchOnto(Integer.valueOf(e.getActionCommand().replaceAll("up", "")));
		} else if (e.getActionCommand().contains("down")){
			switchOnto(Integer.valueOf(e.getActionCommand().replaceAll("down", "")) + 1);
		} else if (e.getActionCommand().contains("remove")){
			removeOnto(Integer.valueOf(e.getActionCommand().replaceAll("remove", "")));			
		} else if (e.getActionCommand().contains("add")){
			addOnto();
		} else if (e.getActionCommand().contains("generate")){
			generate();
			return;
		} else if (e.getActionCommand().equals("ok")){
			messageBox.dispose();
		}
		redraw();
	}

	/**
	 * Create the GUI
	 */
	private static void createAndShowGUI() {
		frame = new JFrame("AuReLi Mapping generator");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		JScrollPane pane = new JScrollPane(new generate_mapping());
		frame.add(pane);

		frame.pack();
		frame.setVisible(true);
	}

	public static void main(String[] args) {

		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});

	}
}
