/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.logisim.proj;

import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map;
import java.util.regex.Pattern;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import com.cburch.logisim.circuit.Circuit;
import com.cburch.logisim.file.LoadFailedException;
import com.cburch.logisim.file.Loader;
import com.cburch.logisim.file.LogisimFile;
import com.cburch.logisim.gui.main.Frame;
import com.cburch.logisim.gui.start.SplashScreen;
import com.cburch.logisim.prefs.AppPreferences;
import com.cburch.logisim.tools.Tool;
import com.cburch.logisim.util.JFileChoosers;
import com.cburch.logisim.util.StringUtil;

public class ProjectActions {
	private ProjectActions() { }
	
	private static class CreateFrame implements Runnable {
		private Loader loader;
		private Project proj;
		private boolean isStartupScreen;
		
		public CreateFrame(Loader loader, Project proj, boolean isStartup) {
			this.loader = loader;
			this.proj = proj;
			this.isStartupScreen = isStartup;
		}
		
		public void run() {
			Frame frame = createFrame(null, proj);
			frame.setVisible(true);
			frame.toFront();
			frame.getCanvas().requestFocus();
			loader.setParent(frame);
			if (isStartupScreen) proj.setStartupScreen(true);
		}
	}

	public static Project doNew(SplashScreen monitor) {
		return doNew(monitor, false);
	}

	public static Project doNew(SplashScreen monitor, boolean isStartupScreen) {
		if (monitor != null) monitor.setProgress(SplashScreen.FILE_CREATE);
		Loader loader = new Loader(monitor);
		InputStream templReader = AppPreferences.getTemplate().createStream();
		LogisimFile file = null;
		try {
			file = loader.openLogisimFile(templReader);
		} catch (IOException ex) {
			displayException(monitor, ex);
		} catch (LoadFailedException ex) {
			displayException(monitor, ex);
		} finally {
			try { templReader.close(); } catch (IOException e) { }
		}
		if (file == null) file = createEmptyFile(loader);
		return completeProject(monitor, loader, file, isStartupScreen);
	}
	
	private static void displayException(Component parent, Exception ex) {
		String msg = StringUtil.format(Strings.get("templateOpenError"),
				ex.toString());
		String ttl = Strings.get("templateOpenErrorTitle");
		JOptionPane.showMessageDialog(parent, msg, ttl, JOptionPane.ERROR_MESSAGE);
	}
	
	private static LogisimFile createEmptyFile(Loader loader) {
		InputStream templReader = AppPreferences.getEmptyTemplate().createStream();
		LogisimFile file;
		try {
			file = loader.openLogisimFile(templReader);
		} catch (Throwable t) {
			file = LogisimFile.createNew(loader);
			file.addCircuit(new Circuit("main"));
		} finally {
			try { templReader.close(); } catch (IOException e) { }
		}
		return file;
	}
	
	private static Project completeProject(SplashScreen monitor, Loader loader,
			LogisimFile file, boolean isStartup) {
		if (monitor != null) monitor.setProgress(SplashScreen.PROJECT_CREATE);
		Project ret = new Project(file);
		
		if (monitor != null) monitor.setProgress(SplashScreen.FRAME_CREATE);
		SwingUtilities.invokeLater(new CreateFrame(loader, ret, isStartup));
		return ret;
	}

	public static LogisimFile createNewFile(Project baseProject) {
		Loader loader = new Loader(baseProject == null ? null : baseProject.getFrame());
		InputStream templReader = AppPreferences.getTemplate().createStream();
		LogisimFile file;
		try {
			file = loader.openLogisimFile(templReader);
		} catch (IOException ex) {
			displayException(baseProject.getFrame(), ex);
			file = createEmptyFile(loader);
		} catch (LoadFailedException ex) {
			if (!ex.isShown()) {
				displayException(baseProject.getFrame(), ex);
			}
			file = createEmptyFile(loader);
		} finally {
			try { templReader.close(); } catch (IOException e) { }
		}
		return file;
	}
	
	private static Frame createFrame(Project sourceProject, Project newProject) {
		if (sourceProject != null) {
			Frame frame = sourceProject.getFrame();
			if (frame != null) {
				frame.savePreferences();
			}
		}
		Frame newFrame = new Frame(newProject);
		newProject.setFrame(newFrame);
		return newFrame;
	}
	
	public static Project doNew(Project baseProject) {
		LogisimFile file = createNewFile(baseProject);
		Project newProj = new Project(file);
		Frame frame = createFrame(baseProject, newProj);
		frame.setVisible(true);
		frame.getCanvas().requestFocus();
		newProj.getLogisimFile().getLoader().setParent(frame);
		return newProj;
	}
	
	public static Project doOpen(SplashScreen monitor, File source,
			Map<File,File> substitutions) throws LoadFailedException {
		if (monitor != null) monitor.setProgress(SplashScreen.FILE_LOAD);
		Loader loader = new Loader(monitor);
		LogisimFile file = loader.openLogisimFile(source, substitutions);
		AppPreferences.updateRecentFile(source);
		
		return completeProject(monitor, loader, file, false);
	}

	public static void doOpen(Component parent, Project baseProject) {
		JFileChooser chooser;
		if (baseProject != null) {
			Loader oldLoader = baseProject.getLogisimFile().getLoader();
			chooser = oldLoader.createChooser();
			if (oldLoader.getMainFile() != null) {
				chooser.setSelectedFile(oldLoader.getMainFile());
			}
		} else {
			chooser = JFileChoosers.create();
		}
		chooser.setFileFilter(Loader.LOGISIM_FILTER);
					
		int returnVal = chooser.showOpenDialog(parent);
		if (returnVal != JFileChooser.APPROVE_OPTION) return;
		File selected = chooser.getSelectedFile();
		if (selected != null) {
			doOpen(parent, baseProject, selected);
		}
	}

	public static Project doOpen(Component parent,
			Project baseProject, File f) {
		Project proj = Projects.findProjectFor(f);
		Loader loader = null;
		if (proj != null) {
			proj.getFrame().toFront();
			loader = proj.getLogisimFile().getLoader();
			if (proj.isFileDirty()) {
				String message = StringUtil.format(Strings.get("openAlreadyMessage"),
						proj.getLogisimFile().getName());
				String[] options = {
						Strings.get("openAlreadyLoseChangesOption"),
						Strings.get("openAlreadyNewWindowOption"),
						Strings.get("openAlreadyCancelOption"),
					};
				int result = JOptionPane.showOptionDialog(proj.getFrame(),
						message, Strings.get("openAlreadyTitle"), 0,
						JOptionPane.QUESTION_MESSAGE, null,
						options, options[2]);
				if (result == 0) {
					; // keep proj as is, so that load happens into the window
				} else if (result == 1) {
					proj = null; // we'll create a new project
				} else {
					return proj;
				}
			}
		}

		if (proj == null && baseProject != null && baseProject.isStartupScreen()) {
			proj = baseProject;
			proj.setStartupScreen(false);
			loader = baseProject.getLogisimFile().getLoader();
		} else {
			loader = new Loader(baseProject == null ? parent : baseProject.getFrame());
		}

		try {
			LogisimFile lib = loader.openLogisimFile(f);
			AppPreferences.updateRecentFile(f);
			if (lib == null) return null;
			if (proj == null) {
				proj = new Project(lib);
			} else {
				proj.setLogisimFile(lib);
			}
		} catch (LoadFailedException ex) {
			if (!ex.isShown()) {
				JOptionPane.showMessageDialog(parent,
					StringUtil.format(Strings.get("fileOpenError"),
						ex.toString()),
					Strings.get("fileOpenErrorTitle"),
					JOptionPane.ERROR_MESSAGE);
			}
			return null;
		}
		
		Frame frame = proj.getFrame();
		if (frame == null) {
			frame = createFrame(baseProject, proj);
		}
		frame.setVisible(true);
		frame.toFront();
		frame.getCanvas().requestFocus();
		proj.getLogisimFile().getLoader().setParent(frame);
		return proj;
	}
	

	// returns true if save is completed
	public static boolean doSaveAs(Project proj) {
		Loader loader = proj.getLogisimFile().getLoader();
		JFileChooser chooser = loader.createChooser();
		chooser.setFileFilter(Loader.LOGISIM_FILTER);
		if (loader.getMainFile() != null) {
			chooser.setSelectedFile(loader.getMainFile());
		}
		int returnVal = chooser.showSaveDialog(proj.getFrame());
		if (returnVal != JFileChooser.APPROVE_OPTION) return false;

		File f = chooser.getSelectedFile();
		String circExt = Loader.LOGISIM_EXTENSION;
		if (!f.getName().endsWith(circExt)) {
			String old = f.getName();
			int ext0 = old.lastIndexOf('.');
			if (ext0 < 0 || !Pattern.matches("\\.\\p{L}{2,}[0-9]?", old.substring(ext0))) {
				f = new File(f.getParentFile(), old + circExt);
			} else {
				String ext = old.substring(ext0);
				String ttl = Strings.get("replaceExtensionTitle");
				String msg = Strings.get("replaceExtensionMessage", ext);
				Object[] options = {
						Strings.get("replaceExtensionReplaceOpt", ext),
						Strings.get("replaceExtensionAddOpt", circExt),
						Strings.get("replaceExtensionKeepOpt")
					};
				JOptionPane dlog = new JOptionPane(msg);
				dlog.setMessageType(JOptionPane.QUESTION_MESSAGE);
				dlog.setOptions(options);
				dlog.createDialog(proj.getFrame(), ttl).setVisible(true);
				
				Object result = dlog.getValue();
				if (result == options[0]) {
					String name = old.substring(0, ext0) + circExt;
					f = new File(f.getParentFile(), name);
				} else if (result == options[1]) {
					f = new File(f.getParentFile(), old + circExt);
				}
			}
		}
		
		if (f.exists()) {
			int confirm = JOptionPane.showConfirmDialog(proj.getFrame(),
				Strings.get("confirmOverwriteMessage"),
				Strings.get("confirmOverwriteTitle"),
				JOptionPane.YES_NO_OPTION);
			if (confirm != JOptionPane.YES_OPTION) return false;
		}
		return doSave(proj, f);
	}

	public static boolean doSave(Project proj) {
		Loader loader = proj.getLogisimFile().getLoader();
		File f = loader.getMainFile();
		if (f == null) return doSaveAs(proj);
		else return doSave(proj, f);
	}
	
	private static boolean doSave(Project proj, File f) {
		Loader loader = proj.getLogisimFile().getLoader();
		Tool oldTool = proj.getTool();
		proj.setTool(null);
		boolean ret = loader.save(proj.getLogisimFile(), f);
		if (ret) {
			AppPreferences.updateRecentFile(f);
			proj.setFileAsClean();
		}
		proj.setTool(oldTool);
		return ret;
	}

	public static void doQuit() {
		Frame top = Projects.getTopFrame();
		top.savePreferences();
		
		for (Project proj : new ArrayList<Project>(Projects.getOpenProjects())) {
			if (!proj.confirmClose(Strings.get("confirmQuitTitle"))) return;
		}
		System.exit(0);
	}
}
