package org.greenstone.gatherer.gui;

import java.awt.*;
import java.net.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.Configuration;

/** Modified. 
 * Class used by WarningDialog and Gatherer which represents a textual input control 
 * (TextField or Combobox) into which a URL can be entered. Provides some static methods
 * shared by these controls and by a general JTextField control which can all be used 
 * in a WarningDialog. Class URLField also contains an inner Interface class and two 
 * static inner classes that implement this interface: Text and DropDown. 
 * Finally, another static inner class URLCollectionPair associates each Gliserver URL
 * (or Library URL) that can be displayed in the DropDown with the last opened collection
 * file for the Greenstone server at that URL. 
*/
public class URLField
{
    static public String getText(JComponent control)
    {
	if(control instanceof JTextField) { // not specifically a URL Field, therefore, we're not meant to do checking
	    return ((JTextField)control).getText();
	} 
	else if(control instanceof URLField.DropDown) {
	    return (String)((JComboBox)control).getSelectedItem();
	}

	// unknown control, shouldn't happen
	return "";
    }

    static public String setText(JComponent control, String text)
    {
	if(control instanceof JTextField) { // not specifically a URL Field, therefore, we're not meant to do checking
	    ((JTextField)control).setText(text);
	} 
	else if(control instanceof URLField.DropDown) {
	    ((JComboBox)control).setSelectedItem(text);	    
	}

	// unknown control, shouldn't happen
	return "";
    }

    static public boolean validateURL(JComponent control)
    {
	if(control instanceof URLField.Text) {
	    return ((URLField.Text)control).validateURL();
	} 
	else if (control instanceof JTextField) {
	    return true; // not specifically a URL Field, therefore, we're not meant to do checking
	}
	else { // control is a DropDown
	    return ((URLField.DropDown)control).validateURL();
	}
    }

    static public boolean validateURL(String url_string)
    {
	if (!url_string.equals("")) {
	    // Check the URL string is valid by trying to create a URL object from it
	    try {
		new URL(url_string);
	    }
	    catch (MalformedURLException exception) {
		// URL string is invalid
		return false;
	    }
	}

	// URL string is valid
	return true;
    }

    static public void store(JComponent control, String affected_property) {
	if(control instanceof JTextField) { 
	    String value = ((JTextField)control).getText();
	    Configuration.setString(affected_property, true, value);
	    Configuration.save(); // save it in case of a crash
	} 
	else { // DropDown
	    ((DropDown)control).saveProperties(); // saves it already
	}
    }

    
    /** INTERFACE CLASS IMPLEMENTED BY Static inner classes Text and DropDown */
    public static interface URLFieldControl {
	public boolean validateURL();
    }


    /** STATIC INNER CLASS URLTextField */
    public static class Text 
	extends JTextField implements URLFieldControl 
    {
	public Text(Color foreground, Color background)
	{
	    super();
	     this.setComponentOrientation(Dictionary.getOrientation());
	    setBackground(background);
	    setForeground(foreground);
	}

	public boolean validateURL() {
	    String url_string = getText();
	    return URLField.validateURL(url_string);
	}
    }

   
    /** STATIC INNER CLASS DropDown, an editable URLComboBox */
    public static class DropDown 
	extends JComboBox implements URLFieldControl
    {
	private String checkString = null; // optional string to check the URLs items against to see whether the URLs contain this
	private String affectedProperty = null; // the Configuration property associated with this DropDown
	private String coaffectedProperty = null; // the Configuration property whose suffix should change along with the affectedProperty
	
	private static final int MAX_URLS = 5;

	public DropDown(Color foreground, Color background, String[] defaultURLs, 
			String affectedProperty, String coaffectedProperty)
	{
	    super(URLCollectionPair.createDefaultsArray(defaultURLs));
	    this.setEditable(true);
	    this.getEditor().selectAll();
	    this.setComponentOrientation(Dictionary.getOrientation());

	    setBackground(background);
	    setForeground(foreground);
	    
	    this.affectedProperty = affectedProperty;
	    this.coaffectedProperty = coaffectedProperty;
	    // read any URLS specified in the config file
	    setComboBoxValues();
	    
	}

	public DropDown(Color foreground, Color background, String[] defaultURLs, 
			String affectedProperty, String coaffectedProperty, String checkString)
	{
	    this(foreground, background, defaultURLs, affectedProperty, coaffectedProperty);
	    this.checkString = checkString;
	}	

	public boolean validateURL() {
	    //String url_string = (String)this.getEditor().getItem();
	    String url_string = this.getSelectedItem().toString(); // returns url of the combobox item
		
	    if(checkString != null && !url_string.endsWith(checkString)) {
		return false;
	    } else {
		return URLField.validateURL(url_string);
	    }
	}

	public void saveProperties() {
	    // 1. Add the first url just edited (if any) 

	    // The user might have created a new STRING, or selected an existing URLCollectionPair
	    // We don't know the type!
	    Object o = this.getSelectedItem(); 
	    URLCollectionPair editedItem = (o instanceof URLCollectionPair) 
		? (URLCollectionPair)o : new URLCollectionPair(o.toString());
	    	    
	    if(editedItem != null) {
		insertItemAt(editedItem, 0);
	    } else {
		editedItem = (URLCollectionPair)getItemAt(0); // else reuse the first default:
	    }
	    Configuration.setString(affectedProperty, true, editedItem.getURL());
	    
	    // concurrently set the last opened collection for this url
	    String lastOpenedCollection = editedItem.getCollection();
	    if(lastOpenedCollection.equals("")) {
		Configuration.setString(coaffectedProperty, true, null);
	    } else {
		Configuration.setString(coaffectedProperty, true, lastOpenedCollection);
	    }


	    // 2. Add the remaining urls into the combobox
	    int url_count = 1; // remain under MAX_URLS AND under the number of actual URLs in the dropdown
	    for(int i = 0; url_count < MAX_URLS && i < getItemCount(); i++, url_count++) {
		
		URLCollectionPair item = (URLCollectionPair)getItemAt(i);
		String url = item.getURL();
		
		if(item == null || url.equals(editedItem.getURL()) || url.equals("")) { 
		    // skip any duplicates of the just-edited URL and skip empty URLs
		    url_count--;
		} 
		else { 
		    Configuration.setString(affectedProperty+url_count, true, url);
		    
		    // concurrently save the name of the corresponding open collection
		    if(item.getCollection().equals("")) {
			Configuration.setString(coaffectedProperty+url_count, true, null);
		    } else {
			Configuration.setString(coaffectedProperty+url_count, true, item.getCollection());
		    }

		} // else retain old value for this affectedProperty (general.gliserver_url)
	    }
	    Configuration.save();
	}

	
	// Get values from Configuration file, or (if none) use defaultURLs.
	// Put them into the combobox
	private void setComboBoxValues() {

	    String url = Configuration.getString(affectedProperty, true);
	    String collection = Configuration.getString(coaffectedProperty, true);
	    boolean finished = url.equals("");
	    if(!finished) {
		this.removeAllItems(); // remove defaults, since config file now contains init values
		this.addItem(new URLCollectionPair(url, collection));
	    } // else urls and combobox already contains the defaults
	   	    

	    for(int i = 1; !finished; i++) {
		url = Configuration.getString(affectedProperty+i, true);
		collection = Configuration.getString(coaffectedProperty+i, true);
		if(url.equals("")) {
		    finished = true;
		}
		else { // url is not empty
		    this.addItem(new URLCollectionPair(url, collection));
		}
	    }
	    
	    // setting the size of the dropdown control
	    Dimension newSize=new Dimension(getPreferredSize().width+20, getPreferredSize().height);
	    setPreferredSize(newSize);
	}
    }


    /** STATIC INNER CLASS URLCollectionPair. 
     * Since the configuration for a (remote) Greenstone2 and Greenstone3 is now
     * stored in a single file for both, we can have GLI connecting to different servers 
     * while for each server, a different collection may have been left open last time.
     * This class URLCollectionPair will associate a GLIServer_URL or Library_URL with
     * the last opened collection file for it. It is used by URLField.DropDown combobox. */
    public static class URLCollectionPair {
	String url;        // gliserverURL 
	String collection; // last opened collection for the corresponding gliserver

	
	public static URLCollectionPair[] createDefaultsArray(String[] defaultURLs) {
	    URLCollectionPair[] pairs = new URLCollectionPair[defaultURLs.length];
	    for(int i = 0; i < pairs.length; i++) {
		pairs[i] = new URLCollectionPair(defaultURLs[i]); // no associated open collection
	    }
	    return pairs;
	}

	public URLCollectionPair(String url) {
	    this.set(url, "");
	}

	public URLCollectionPair(String url, String collection) {
	    this.set(url, collection);
	}

	public void set(String url, String collection) {
	    this.url = url;
	    this.collection = collection;
	}

	public void setURL(String url) {
	    this.url = url;
	}

	public void setCollection(String collection) {
	    this.collection = collection;
	}

	public String getCollection() {
	    return collection;
	}

	public String getURL() {
	    return url;
	}

	/** For display in comboboxes */
	public String toString() {
	    return url;
	}

	/** @returns the number that is suffixed to the gliserver_url */
	public String getSuffixNumber() {
	    String numericSuffix = "";
	    boolean stop = false;
	    for(int index = url.length()-1; index >= 0 && !stop; index--) { // work from the end
		char character = url.charAt(index);
		if(Character.isDigit(character)) {
		    numericSuffix = Character.toString(character) + numericSuffix;
		} else {
		    stop = true;
		}
	    }

	    return numericSuffix;
	}
    }

}