package javajs.async;


import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.function.Function;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileSystemView;

/**
 * A simple Asynchronous file chooser for JavaScript and Java.
 * 
 * Requires an OK runnable; JavaScript can return notification of cancel for
 * file reading only, not saving.
 * 
 * @author Bob Hanson
 */

public class AsyncFileChooser extends JFileChooser implements PropertyChangeListener {

	private int optionSelected;
	private Runnable ok, cancel; // sorry, no CANCEL in JavaScript for file open
	@SuppressWarnings("unused")
	private boolean isAsyncSave = true;
	private static boolean notified;

	public AsyncFileChooser() {
		super();
	}

	public AsyncFileChooser(File file) {
		super(file);
	}

	public AsyncFileChooser(File file, FileSystemView view) {
		super(file, view);
	}

	@Deprecated
	@Override
	public int showDialog(Component frame, String btnText) {
		// This one can come from JFileChooser - default is OPEN
		return super.showDialog(frame, btnText);
	}

	private int err() {
		try {
			throw new java.lang.IllegalAccessException("Warning! AsyncFileChooser interface bypassed!");
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return JFileChooser.ERROR_OPTION;
	}

	@Deprecated
	@Override
	public int showOpenDialog(Component frame) {
		return err();
	}

	@Override
	public int showSaveDialog(Component frame) {
		isAsyncSave  = false;
		return super.showSaveDialog(frame);
	}

	/**
	 * 
	 * @param frame
	 * @param btnLabel "open" or "save"
	 * @param ok
	 * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
	 */
	public void showDialog(Component frame, String btnLabel, Runnable ok, Runnable cancel) {
		this.ok = ok;
		if (getDialogType() != JFileChooser.SAVE_DIALOG && cancel != null)
			notifyCancel();
		process(super.showDialog(frame, btnLabel));
	}

	/**
	 * 
	 * @param frame
	 * @param ok
	 * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
	 */
	public void showOpenDialog(Component frame, Runnable ok, Runnable cancel) {
		this.ok = ok;
		if (cancel != null)
			notifyCancel();
		process(super.showOpenDialog(frame));
	}

	/**
	 * 
	 * This just completes the set. It is not necessary for JavaScript, because JavaScript
	 * will just throw up a simple modal OK/Cancel message anyway.
	 * 
	 * @param frame
	 * @param ok
	 * @param cancel must be null
	 */
	public void showSaveDialog(Component frame, Runnable ok, Runnable cancel) {
		this.ok = ok;
		this.cancel = cancel;
		process(super.showSaveDialog(frame));
	}

	
	/**
	 * Locate a file for input or output. Note that JavaScript will not return on cancel for OPEN_DIALOG.
	 * 
	 * @param title       The title for the dialog
	 * @param mode        OPEN_DIALOG or SAVE_DIALOG
	 * @param processFile function to use when complete
	 */
	  public static void getFileAsync(Component parent, String title, int mode, Function<File, Void> processFile) {
		  // BH no references to this method. So changing its signature for asynchonous use
		  // And it didn't do as advertised - ran System.exit(0) if canceled
	    // create and display a file dialog
		AsyncFileChooser fc = new AsyncFileChooser();
		fc.setDialogTitle(title);
		Runnable after = new Runnable() {

			@Override
			public void run() {
				processFile.apply(fc.getSelectedFile());
			}
	
		};
		if (mode == JFileChooser.OPEN_DIALOG) {
			fc.showOpenDialog(parent, after, after);  
		} else {
			fc.showSaveDialog(parent, after, after);  
		}
				
	  }
	    
		/**
		 * Run yes.run() if a file doesn't exist or if the user allows it, else run no.run()
		 * @param parent
		 * @param filename
		 * @param title
		 * @param yes (approved)
		 * @param no (optional)
		 */
		public static void checkReplaceFileAsync(Component parent, File outfile, String title, Runnable yes, Runnable no) {
			if (outfile.exists()) {
				AsyncDialog.showYesNoAsync(parent,
						outfile + " exists. Replace it?", null, new ActionListener() {
		
							@Override
							public void actionPerformed(ActionEvent e) {
								switch (e.getID()) {
								case JOptionPane.YES_OPTION:
									yes.run();
									break;
								default:
									if (no != null)
										no.run();
									break;
								}
							}
		
						});
		
			} else {
				yes.run();
			}
		
		}

	private void notifyCancel() {
		if (!notified) {
			System.err.println("developer note: JavaScript cannot fire a FileChooser CANCEL action");
		}
		notified = true;
	}

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		switch (evt.getPropertyName()) {
		case "SelectedFile":
		case "SelectedFiles":
			process(optionSelected = (evt.getNewValue() == null ? CANCEL_OPTION : APPROVE_OPTION));
			break;
		}
	}

	private void process(int ret) {
		if (ret != -(-ret))
			return; // initial JavaScript return is NaN
		optionSelected = ret;
		File f = getSelectedFile();
		if (f == null) {
			if (cancel != null)
				cancel.run();
		} else {
			if (ok != null)
				ok.run();
		}
	}

	public int getSelectedOption() {
		return optionSelected;
	}

	public static byte[] getFileBytes(File f) {
		return /** @j2sNative f.秘bytes || */null;
	}

}