/**
 * @(#)GT2DatasourceFactoryChooser.java	29.06.2004
 *
 * Copyright 2004 Edgar Soldin
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package de.soldin.gt2jump.readwrite;

import java.awt.Frame;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JTextPane;

import org.geotools.data.DataSource;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataSourceFactorySpi;
import org.geotools.factory.FactoryFinder;
import org.swixml.SwingEngine;

/**
 * Implements the {@link org.swixml.SwingEngine} and is the ui 
 * component for choosing a GT2 datasource modul.
 * <p>
 * The matching file or db parameter panel is shown depending
 * on the choice.
 * </p>
 */
public class GT2DatasourceFactoryChooser 
	extends SwingEngine implements ActionListener, ItemListener
	{
	private static String templates = "de/soldin/gt2jump/readwrite/";
	//private SwingEngine swix = new SwingEngine( this );
	public JPanel p_main,p_attributes,p_file,p_db,p_error;
	public JTextPane errormsg;
	public JTextField user,passwd,host,port,table;
	public JFileChooser file;
	public JComboBox ds_factorychooser;
	JDialog dialog;
		
	public static int DS_FILE = 0;
	public static int DS_DB = 1;
	public static int DS_ERROR = 2;
	private String ds_name;	
	private HashMap ds_factories = new HashMap();
	private DataSourceFactorySpi ds_f_selected;
	private Map ds_f_params;
	private boolean cancelled = false;

	public JTextField database;

  public GT2DatasourceFactoryChooser() throws Exception{
		this(new Frame(), "", getAvailableDS());
  }
  
	public GT2DatasourceFactoryChooser(Frame owner) throws Exception{
		this(owner, "", getAvailableDS());
	}

	public GT2DatasourceFactoryChooser(Frame owner,String title) throws Exception{
		this(owner, title, getAvailableDS());
	}
	
	public GT2DatasourceFactoryChooser(Frame owner, String title, HashMap in_ds_factories) throws Exception{
		this.getTaglib().registerTag("filechooser",JFileChooser.class);
		this.ds_factories = in_ds_factories;
		this.dialog = new JDialog(owner,title,true);
		this.dialog.getContentPane().setLayout(new GridBagLayout());
		
		try {
			p_file = (JPanel)this.render( templates+"filepanel.xml" );
			p_db = (JPanel)this.render( templates+"dbpanel.xml" );
			p_error = (JPanel)this.render( templates+"errorpanel.xml" );
			p_main = (JPanel)this.render( templates+"dialog.xml" );
			ds_factorychooser.addItemListener(this);
			setFactories(in_ds_factories);

			this.setActionListener(p_main,this);

			dialog.getContentPane().add(p_main);
			dialog.pack();
		}catch (Exception e) {
			throw e;
		}
	}

 
  public void setLocation(int x, int y) { this.dialog.setLocation(x,y); }
	
	public void setTitle(String title) { this.dialog.setTitle(title); }
	
	public void setFactories(HashMap in_ds_factories) { 
		this.ds_factories = in_ds_factories;
		//Object selected = ds_factorychooser.getSelectedItem();
		
		ds_factorychooser.removeAllItems();
		Object[] keys = ds_factories.keySet().toArray();
		for (int i = 0; i < keys.length; i++) {
			ds_factorychooser.addItem(keys[i]);
		}
		// select first item
		if(ds_factorychooser.getItemCount()>0){
			ds_factorychooser.setSelectedItem(ds_factorychooser.getItemAt(0));
		}
	}

	public DataSource choose() throws Exception{
		// init 
		cancelled = false;
		ds_name = "untitled";
		
		file.rescanCurrentDirectory();
		file.setSelectedFile(null);
		file.setSelectedFiles(new File[]{});
		file.repaint();
		
		// start chooser
		dialog.setVisible( true );
		
		// we were cancelled, return immediately
		if(cancelled){ return null;	}
		
		if (ds_f_selected!=null){
			Map ds_f_params = getDSParamMap();

			if (ds_f_params.containsKey("dbtype")){
				String[] words = (String[])ds_f_params.get("dbtype");
				for (int i = 0; i < words.length; i++) {
					String word = words[i];
					Map foo = new HashMap(ds_f_params);
					foo.put("dbtype",word);
					//try{
						DataSource ds = ds_f_selected.createDataSource(foo);
						if (ds==null)
							continue;
						else
							return ds;

				}
			}else{
				return ds_f_selected.createDataSource(ds_f_params);
			}
			
		}
		return null;
	}
	
	public boolean isCancelled(){ return this.cancelled; };
	
	public String getDSName(){ return (ds_name!=null) ? ds_name : "untitled";}

  public static void main(String[] args) throws Exception {
    GT2DatasourceFactoryChooser fch = new GT2DatasourceFactoryChooser();
    fch.setLocation(100,200);
    fch.setTitle("Ich bin ein Bibabutzemann");
    try{
    	System.out.println(fch.choose());
    }catch(Exception e){
    	e.printStackTrace();
    }
    System.exit(0);
  }
  
	/**
	 * Invoked when an action occurs.
	 */
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		
		if ("AC_OK".equals( command )) {
			file.approveSelection();
			dialog.setVisible(false);
		}else if ("AC_CANCEL".equals( command )) {
			cancelled = true;
			dialog.setVisible(false);
		}
	}

	public void itemStateChanged(ItemEvent e) {
		if(e.getStateChange()==ItemEvent.SELECTED){
			p_attributes.removeAll();
			String key = (String)e.getItem();

			try{
				ds_f_selected = (DataSourceFactorySpi)this.ds_factories.get(key);
				if (ds_f_selected==null){
					System.err.println("empty item selected");
					return;
				}
				
				if (detectType(ds_f_selected)==DS_DB){
					p_attributes.add(p_db);			
				}else{
					p_attributes.add(p_file);			
				}
			}catch (ClassCastException cce){
				// must be some error, or what else??
				String errmsg;
				try{
					Exception exc = (Exception)this.ds_factories.get(key);
					errmsg = throwableToString(exc); 
				}catch(ClassCastException cce2){
					try{
						Error err = (Error)this.ds_factories.get(key);
						errmsg = throwableToString(err);
					}catch(ClassCastException cce3){
						errmsg = "Unknown Error occured:\n"+this.ds_factories.get(key).toString();
					}
				}
			
				errormsg.setText(errmsg);
				p_attributes.add(p_error);
			}
				
			p_attributes.validate();
			dialog.pack();
		}
		//System.out.println( e.getItem() );
	}
	
	public Map getDSParamMap(){
		HashMap params = new HashMap();
		// not factory selected, no params necessary
		if(ds_f_selected==null) return params;

		if (detectType(ds_f_selected)==DS_DB){
			Pattern word_pattern = Pattern.compile("[\\s]+");
			String[] words = word_pattern.split(ds_f_selected.getDescription());
			for (int i = 0; i < words.length; i++) {
				words[i] = words[i].toLowerCase();
				//System.out.print(words[i]+",");
			}
			//words[0]="test";
			//words[2]="postgis";
			params.put("dbtype",words);
			
			params.put("host"    ,host.getText().toString());
			params.put("port"    ,port.getText().toString());
			params.put("user"    ,user.getText().toString());
			params.put("passwd"  ,passwd.getText().toString());
			params.put("database",database.getText().toString());
			params.put("table"   ,table.getText().toString());
			//System.out.print("  DB");
			ds_name = (String)params.get("table");
		}else{
			try {
				if (file.getSelectedFiles().length > 0){
					params.put("url",file.getSelectedFiles()[0].toURL().toString());
					ds_name = new File(new URL(((String)params.get("url"))).getFile()).getName();
				}
			}catch (MalformedURLException e) {
				// should never get here
				e.printStackTrace();
			}
			//System.out.print(" File");
		}
		
		return params;		
	}
	
	public static HashMap getAvailableDS() throws DataSourceException{
		// find available ds and add them to list
		HashMap ds_factories = new HashMap();
		for (Iterator it = FactoryFinder.factories(DataSourceFactorySpi.class, GT2ReaderWriterExtension.createLoader()); it.hasNext();) {
		//for (Iterator it = DataSourceFinder.getAvailableDataSources(); it.hasNext();) {
			try {
				DataSourceFactorySpi ds = (DataSourceFactorySpi) it.next();
				ds_factories.put(ds.getDescription(),ds);
				//System.out.println(ds.getDescription());
			}catch (Exception e) {
				ds_factories.put("Exception: "+(e.getMessage()!=null?e.getMessage().subSequence(0,50):""),e);
			}catch (Error e){
				ds_factories.put("Error: "+(e.getMessage()!=null?e.getMessage().subSequence(0,50):""),e);
			}
		}
		return (HashMap)ds_factories.clone();
	}
	
	public static int detectType(DataSourceFactorySpi ds){
		String desc = ds.getDescription();

		Pattern db_pattern = Pattern.compile(".*Database.*",Pattern.CASE_INSENSITIVE+Pattern.UNICODE_CASE);										
		Matcher m = db_pattern.matcher(desc);
		if (m.matches()){
			return DS_DB;
		}
		
		// must be a file datasource		
		return DS_FILE;
		
	}
	
	public static String throwableToString(Throwable t){
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		PrintStream ps = new PrintStream(os);
		StringBuffer buffy = new StringBuffer();
		
		do{
			buffy.append("\nMESSAGE:\t"+t.toString()+"\n");
			buffy.append("\nSTACK:\n");
			StackTraceElement[] stackies = t.getStackTrace();
			for (int i = 0; i < stackies.length; i++) {
				StackTraceElement stacky = stackies[i];
				buffy.append(stacky+"\n");
			}
		}while((t = t.getCause())!=null);
		
		return buffy.toString();
	}

	private JTextField getDatabase() {
		return database;
	}

	private void setDatabase(JTextField database) {
		this.database = database;
	}

}