package com.limegroup.gnutella.gui.options.panes;

import java.awt.Point;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import com.limegroup.gnutella.MediaType;
import com.limegroup.gnutella.gui.FileChooserHandler;
import com.limegroup.gnutella.gui.GUIMediator;
import com.limegroup.gnutella.gui.PaddedPanel;
import com.limegroup.gnutella.gui.SaveDirectoryHandler;
import com.limegroup.gnutella.gui.options.OptionsMediator;
import com.limegroup.gnutella.gui.search.NamedMediaType;
import com.limegroup.gnutella.gui.tables.AbstractTableMediator;
import com.limegroup.gnutella.gui.tables.BasicDataLineModel;
import com.limegroup.gnutella.gui.tables.DataLine;
import com.limegroup.gnutella.gui.tables.LimeJTable;
import com.limegroup.gnutella.gui.tables.TableSettings;

/**
 * Class which handles the construction of the download directories per
 * mediatype table and its associated actions.
 */
class MediaTypeDownloadDirMediator extends AbstractTableMediator {
	/**
	 * The PaneItem on which the media type is displayed.
	 */
	private final JTextField saveField;

	/**
	 * Handle to the browse action which can be retrieved through
	 * {@link #getBrowseDirectoryAction()}.
	 */
	private BrowseDirectoryAction browseAction;

	/**
	 * Handle to the reset action which can be retrieve through the
	 * {@link #getResetDirectoryAction()}.
	 */
	private ResetDirectoryAction resetAction;
	
	/**
	 * Workaround to allow reinstantiation without getting duplicate settings keys.
	 */
	private static TableSettings cachedSettings = null;
	
	/**
	 * @param item
	 * @param id
	 */
	public MediaTypeDownloadDirMediator(JTextField field) {
		super("MEDIA_TYPE_DOWNLOAD_DIR_TABLE");
		saveField = field;
		saveField.getDocument().addDocumentListener(new DocumentHandler());
	}

	/**
	 * Returns true if the settings in the table have changed and they need to
	 * be saved.
	 */
	public boolean isDirty() {
	    for( int i = 0; i < DATA_MODEL.getRowCount(); i++) {
	        if(((MediaTypeDownloadDirDataLine)DATA_MODEL.get(i)).isDirty())
	            return true;
	    }
	    return false;
	}

	/**
	 * Overriden to allow reinstantiation of this class.
	 */
	protected void buildSettings() {
		if (cachedSettings == null) {
			super.buildSettings();
			cachedSettings = SETTINGS;
		}
		SETTINGS = cachedSettings;
	}

	protected void setupConstants() {
		MAIN_PANEL = new PaddedPanel(0);
		DATA_MODEL = new MediaTypeDownloadDirModel(MediaTypeDownloadDirDataLine.class);
		TABLE = new LimeJTable(DATA_MODEL);
		TABLE.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		// construct actions here to avoid null pointer exceptions in
		// handleNoSelection()
		browseAction = new BrowseDirectoryAction();
		resetAction = new ResetDirectoryAction();
	}
	
	/**
	 * Save options for each mediatype in the table.
	 */
	public boolean applyOptions(Set newDirs) throws IOException {
		for (int i = 0; i < DATA_MODEL.getRowCount(); i++) {
			MediaTypeDownloadDirDataLine dl = (MediaTypeDownloadDirDataLine)DATA_MODEL.get(i);
			dl.saveDirectory(newDirs);
		}
		
		return false;
	}
	
	/** 
	 * Adds save dirs to the given option.
	 */
	void addSaveDirs(Set set) {
		for (int i = 0; i < DATA_MODEL.getRowCount(); i++) {
			String dir = ((MediaTypeDownloadDirDataLine)DATA_MODEL.get(i)).getDirectory();
			if(dir != null)
			    set.add(new File(dir));
		}
	}

	/**
	 * Fill the table with the mediatypes and the directory settings.
	 */
	public void initOptions() {
		DATA_MODEL.clear();
		for (Iterator i = NamedMediaType.getAllNamedMediaTypes().iterator(); i.hasNext();) {
			NamedMediaType nm = (NamedMediaType) i.next();
			if (!nm.getMediaType().getMimeType().equals(MediaType.SCHEMA_ANY_TYPE))
				DATA_MODEL.add(nm);
		}
		//  sort alphabetically, so sort twice
		DATA_MODEL.sort(0);
		DATA_MODEL.sort(0);
		updateModel();
	}

	public Action getBrowseDirectoryAction() {
		return browseAction;
	}

	public Action getResetDirectoryAction() {
		return resetAction;
	}

	/**
	 * Overriden to disable removal of rows.
	 */
	public void removeSelection() { }

	/*
	 * @see com.limegroup.gnutella.gui.tables.AbstractTableMediator#updateSplashScreen()
	 */
	protected void updateSplashScreen() { }

	/*
	 * @see com.limegroup.gnutella.gui.tables.AbstractTableMediator#createPopupMenu()
	 */
	protected JPopupMenu createPopupMenu() {
		JPopupMenu menu = new JPopupMenu();
		menu.add(new JMenuItem(browseAction));
		menu.add(new JMenuItem(resetAction));
		return menu;
	}

	/**
	 * Overridden to not popup a menu on the table header.
	 */
	public void handleHeaderPopupMenu(Point p) { }
	
	/*
	 * @see com.limegroup.gnutella.gui.tables.ComponentMediator#handleActionKey()
	 */
	public void handleActionKey() { }

	/**
	 * Enable the browse and reset action.
	 */
	public void handleSelection(int row) {
		browseAction.setEnabled(true);
		resetAction.setEnabled(true);
	}

	/**
	 * Disable the browse and reset action.
	 */
	public void handleNoSelection() {
		browseAction.setEnabled(false);
		resetAction.setEnabled(false);
	}

	/**
	 * The table model.
	 */
	class MediaTypeDownloadDirModel extends BasicDataLineModel {

		/**
		 * @param dataLineClass
		 */
		public MediaTypeDownloadDirModel(Class dataLineClass) {
			super(dataLineClass);
		}
		
		public DataLine createDataLine() {
		    return new MediaTypeDownloadDirDataLine();
		}

		public boolean isCellEditable(int row, int col) {
			return col == 1;
		}

		public void setValueAt(Object value, int row, int col) {
			setDirectory(get(row), new File((String) value));
		}
	}

	/**
	 * Setting of a download directory for a mediatype should be done with this
	 * method. It makes sure the directory is a valid download directory.
	 * 
	 * @param line
	 * @param dir
	 */
	private void setDirectory(DataLine line, File dir) {
		// Otherwise, make sure they selected a valid directory that
		// they can really write to.
		if (!SaveDirectoryHandler.isSaveDirectoryValid(dir)) {
			GUIMediator.showError("ERROR_INVALID_SAVE_DIRECTORY_SELECTION");
			return;
		}
		try {
			String newDir = dir.getCanonicalPath();
			MediaTypeDownloadDirDataLine dl = (MediaTypeDownloadDirDataLine) line;
			dl.setDirectory(newDir);
		} catch (IOException ioe) {
		}
	}

	/**
	 * Opens a directory chooser dialog and sets the selected directory for the
	 * selected mediatype row.
	 */
	private class BrowseDirectoryAction extends AbstractAction {
		public BrowseDirectoryAction() {
			putValue(Action.NAME, GUIMediator
					.getStringResource("GENERAL_BROWSE_BUTTON_LABEL"));
			putValue(Action.SHORT_DESCRIPTION, GUIMediator
					.getStringResource("OPTIONS_SAVE_MEDIATYPE_BROWSE_TIP"));
		}

		/*
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent e) {
			DataLine line = TABLE.getSelectedDataLine();

			if (line == null) {
				throw new IllegalArgumentException(
						"browse action was not correctly disabled");
			}

			File dir = FileChooserHandler
					.getInputDirectory(OptionsMediator.instance().getMainOptionsComponent());

			if (dir != null) {
				setDirectory(line, dir);
			}
		}
	}

	/**
	 * Resets the selected mediatype row so that its download directory is
	 * always the default download directory.
	 */
	private class ResetDirectoryAction extends AbstractAction {

		public ResetDirectoryAction() {
			putValue(Action.NAME, GUIMediator
					.getStringResource("OPTIONS_SAVE_RESET_BUTTON_LABEL"));
			putValue(Action.SHORT_DESCRIPTION, GUIMediator
					.getStringResource("OPTIONS_SAVE_RESET_BUTTON_TIP"));
		}

		public void actionPerformed(ActionEvent e) {
			DataLine line = TABLE.getSelectedDataLine();

			if (line == null) {
				throw new IllegalArgumentException(
						"reset action was not correctly disabled");
			}
			MediaTypeDownloadDirDataLine mediaLine = (MediaTypeDownloadDirDataLine) line;
			mediaLine.reset();
			MediaTypeDownloadDirMediator.this.DATA_MODEL.fireTableDataChanged();
		}
	}

	/**
	 * Listens for document changes of the {@link SaveDirPaneItem#_saveField}
	 * and updates the table model accordingly.
	 */
	private class DocumentHandler implements DocumentListener {

		public void insertUpdate(DocumentEvent e) {
			MediaTypeDownloadDirMediator.this.updateModel();
		}

		public void removeUpdate(DocumentEvent e) {
			MediaTypeDownloadDirMediator.this.updateModel();
		}

		public void changedUpdate(DocumentEvent e) {
		}
	}

	/**
	 * Notify all mediatype rows of a possible change of the default download
	 * directory.
	 */
	private void updateModel() {
		String text = saveField.getText();
		for (int i = 0; i < TABLE.getRowCount(); i++) {
			MediaTypeDownloadDirDataLine line = (MediaTypeDownloadDirDataLine) DATA_MODEL
					.get(i);
			line.setDefaultDir(text);
		}
		DATA_MODEL.fireTableDataChanged();
	}
}
