/*
oStorybook: Open Source software for novelists and authors.
initial conception Martin Mustun, adaptation FaVdB

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 3 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 fileCopy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package storybook;

import assistant.Assistant;
import i18n.I18N;
import ideabox.IdeaxFrm;
import java.awt.Component;
import java.awt.HeadlessException;
import java.beans.PropertyChangeEvent;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.file.Files;
import java.nio.file.Path;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.hibernate.Session;
import resources.icons.IconUtil;
import storybook.Const.STORYBOOK;
import storybook.exim.importer.ImportDocument;
import storybook.model.H2File;
import storybook.model.book.Book;
import storybook.model.book.BookUtil;
import storybook.model.oldmodel.ModelMigration;
import storybook.shortcut.Shortcuts;
import storybook.tools.Dictaphone;
import storybook.tools.LOG;
import storybook.tools.ListUtil;
import storybook.tools.StringUtil;
import storybook.tools.file.EnvUtil;
import static storybook.tools.file.EnvUtil.getHomeDir;
import storybook.tools.file.IOUtil;
import storybook.tools.net.Updater;
import storybook.tools.spell.SpellUtil;
import storybook.tools.swing.LaF;
import storybook.tools.swing.SwingUtil;
import storybook.tools.swing.splash.WaitingSplash;
import storybook.tools.synonyms.Synonyms;
import storybook.ui.MainFrame;
import storybook.ui.dialog.ConfirmDlg;
import storybook.ui.dialog.ExceptionDlg;
import storybook.ui.dialog.FirstStartDlg;
import storybook.ui.dialog.MessageDlg;
import storybook.ui.dialog.NewProjectDlg;
import storybook.ui.dialog.preferences.PropertiesDlg;

public class App extends Component {

	private static final String TT = "App.";

	private static App instance;
	public static Pref preferences;
	public static AppFont fonts;
	private final List<MainFrame> mainFrames;
	private static String i18nFile = "";
	private Assistant assistant;
	private static boolean dev = false, test = false;
	private static File fileToOpen = null;
	private static boolean startIdeabox = false;

	/**
	 * getting if dev mode
	 *
	 * @return
	 */
	public static boolean isDev() {
		return dev;
	}

	/**
	 * set the mode to dev
	 */
	public static void setDev() {
		dev = true;
	}

	/**
	 * getting if test mode
	 *
	 * @return
	 */
	public static boolean isTest() {
		return test;
	}

	/**
	 * set the mode to test
	 */
	public static void setTest() {
		test = true;
	}

	/**
	 * get the instance of App
	 *
	 * @return
	 */
	public static App getInstance() {
		if (instance == null) {
			instance = new App();
		}
		return instance;
	}

	/**
	 * initialize preferences
	 */
	public static void initPref() {
		//LOG.trace(TT + "initPref()");
		preferences = new Pref();
		if (preferences.getBoolean(Pref.KEY.FIRST_START, true)) {
			preferences.initFont();
		}
	}

	/**
	 * the main process
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		LOG.log(TT + "main(...)");
		LOG.init();
		initPref();
		LaF.init();
		fonts = new AppFont();
		String vargs = String.join(" ", args);
		if (vargs.length() == 0) {
			vargs = "no option";
		}
		LOG.log(Const.getFullName() + " starting with: " + vargs);
		String tempDir = System.getProperty("java.io.tmpdir");
		String fn = tempDir + File.separator + "storybook.lck";
		List<String> mode = new ArrayList<>();
		if (args.length > 0) {
			for (int i = 0; i < args.length; i++) {
				switch (args[i].toLowerCase()) {
					/*case "--assistant":
						// start the AssistantApp
						SwingUtilities.invokeLater(() -> {
							AppAssistant.main(args);
						});
						return;*/
					case "--trace":
						mode.add("trace");
						LOG.setTrace();
						break;
					case "--hibernate":
						mode.add("hibernate");
						LOG.setTraceHibernate();
						break;
					case "--dev":
						mode.add("dev");
						App.dev = true;
						break;
					case "--msg":
						i++;
						String fileI18N = args[i];
						File f = new File(args[i]);
						if (!f.exists()) {
							fileI18N = args[i] + ".properties";
							f = new File(fileI18N);
							if (!f.exists()) {
								LOG.log("Message file not exists : " + fileI18N);
								fileI18N = "";
							}
						}
						if (!fileI18N.isEmpty()) {
							App.i18nFile = fileI18N;
							LOG.log("Message from : " + fileI18N);
							I18N.setFileMessages(i18nFile);
						}
						break;
					case "--ideabox":
						startIdeabox = true;
						break;
					default:
						//perhaps the file to open
						fileToOpen = new File(args[i]);
						if (!fileToOpen.exists()
						   || !fileToOpen.getAbsolutePath().endsWith(".osbk")) {
							fileToOpen = null;
						}
						break;
				}
			}
			if (!mode.isEmpty()) {
				LOG.trace("Set " + ListUtil.join(mode, ", ") + " mode");
			}
		}
		if (!lockInstance(fn)) {
			Object[] options = {I18N.getMsg("running.remove"), I18N.getMsg("cancel")};
			int n = JOptionPane.showOptionDialog(null,
			   I18N.getMsg("running.msg"),
			   I18N.getMsg("running.title"),
			   JOptionPane.YES_NO_CANCEL_OPTION,
			   JOptionPane.QUESTION_MESSAGE, null, options, options[1]);
			if (n == 1) {
				File file = new File(fn);
				if (file.exists() && file.canWrite() && !file.delete()) {
					JOptionPane.showMessageDialog(null, "Delete failed",
					   "File\n" + file.getAbsolutePath() + "\ncould not be deleted.",
					   JOptionPane.ERROR_MESSAGE);
				}
			}
			return;
		}
		SwingUtilities.invokeLater(() -> {
			App app = App.getInstance();
			app.init(fileToOpen);
			if (startIdeabox) {
				app.ideaboxStart();
			}
		});
	}

	/**
	 * the App class
	 */
	private App() {
		mainFrames = new ArrayList<>();
	}

	/**
	 * default initialization
	 */
	private void init() {
		//LOG.trace(TT + "init()");
		Shortcuts.init();
		assistant = new Assistant();
		String s = preferences.getString(Pref.KEY.ASSISTANT);
		if (!s.isEmpty()) {
			assistant = new Assistant(s);
		}
		fonts.defRestore();
		IconUtil.setDefSize();
		String spelling = preferences.getString(Pref.KEY.SPELLING, "none");
		String lang = "";
		if (!spelling.equals("none")) {
			SpellUtil.registerDictionaries();
			lang = spelling.substring(0, 2);
		}
		Synonyms.init(getHomeDir().getAbsolutePath()
		   + File.separator + ".storybook5/dicts/", lang);
	}

	/**
	 * initialization for a given file to open
	 *
	 * @param toOpen
	 */
	private void init(File toOpen) {
		//LOG.trace(TT + "init(toOpen=" + (toOpen != null ? toOpen.toString() : "null") + ")");
		init();
		dictaphoneInit();
		try {
			// first start dialog
			if (preferences.getBoolean(Pref.KEY.FIRST_START, true)) {
				LOG.trace("call First Start Dialog");
				FirstStartDlg dlg = new FirstStartDlg();
				dlg.setVisible(true);
				preferences.setBoolean(Pref.KEY.FIRST_START, false);
				fonts.defRestore();
				IconUtil.setDefSize();
			}

			boolean fileHasBeenOpened = false;
			H2File dbFile;
			if (toOpen != null) {
				LOG.trace("trying to open " + toOpen.getAbsolutePath());
				dbFile = new H2File(toOpen.getAbsolutePath());
				fileHasBeenOpened = openFile(dbFile, true);
			} else if (preferences.getBoolean(Pref.KEY.OPEN_LASTFILE, false)
			   && !preferences.getString(Pref.KEY.LASTOPEN_FILE, "").isEmpty()) {
				String str = preferences.getString(Pref.KEY.LASTOPEN_FILE, "");
				if (str.endsWith("h2.db")) {
					str = str.replace("h2.db", "osbk");
				}
				if (str.endsWith("mv.db")) {
					str = str.replace("mv.db", "osbk");
				}
				dbFile = new H2File(str);
				fileHasBeenOpened = openFile(dbFile, true);
			}
			if (!fileHasBeenOpened) {
				preferences.setString(Pref.KEY.LASTOPEN_FILE, "");
			}
			MainFrame mainFrame = new MainFrame();
			mainFrame.init();
			mainFrame.initBlankUi();
			addMainFrame(mainFrame);
			// check for updates
			Updater.checkForUpdate(false);
		} catch (Exception e) {
			ExceptionDlg.show(this.getClass().getSimpleName() + ".init()", e);
		}
	}

	/**
	 * initialize the I18N with preferences
	 */
	public void initI18N() {
		//LOG.trace(TT + "initI18N()");
		Locale locale = Locale.getDefault();
		try {
			String localeStr = preferences.getString(Pref.KEY.LANGUAGE, "en_US");
			String language[] = localeStr.split("_");
			locale = new Locale(language[0], language[1]);
			setLocale(locale);
		} catch (Exception ex) {
			LOG.err("unable to find Locale in oStorybook.ini");
		}
		try {
			String f = preferences.getString(Pref.KEY.MSGFILE, "");
			if (!f.isEmpty()) {
				I18N.setFileMessages(f);
			}
		} catch (Exception ex) {
		}
		I18N.initMessages(locale);
	}

	/**
	 * get the assistant
	 *
	 * @return
	 */
	public static Assistant getAssistant() {
		return getInstance().assistant;
	}

	/**
	 * set the assistant to the given file name
	 *
	 * @param nf
	 */
	public void setAssistant(String nf) {
		assistant = new Assistant(nf);
	}

	public static AppFont getFonts() {
		if (fonts == null) {
			fonts = new AppFont();
		}
		return fonts;
	}

	/**
	 * get all current opened MainFrame
	 *
	 * @return
	 */
	public List<MainFrame> getMainFrames() {
		return mainFrames;
	}

	/**
	 * add a MainFrame to the opened list
	 *
	 * @param mainFrame
	 */
	public void addMainFrame(MainFrame mainFrame) {
		//LOG.trace(TT+"addMainFrame(mainFrame="+mainFrame.getName()+")");
		if (!mainFrames.contains(mainFrame)) {
			mainFrames.add(mainFrame);
		}
	}

	/**
	 * remove a Mainframe from the opened list
	 *
	 * @param mainFrame
	 */
	public void removeMainFrame(MainFrame mainFrame) {
		//LOG.trace(TT+"removeMainFrame(mainFrame="+mainFrame.getName()+")");
		if (mainFrame.getH2File() != null) {
			mainFrame.getH2File().remove();
		}
		mainFrames.remove(mainFrame);
		reloadMenuBars();
	}

	/**
	 * close the blank Frame
	 *
	 */
	public void closeBlank() {
		//LOG.trace(TT+"closeBlank()");
		for (MainFrame mainFrame : mainFrames) {
			if (mainFrame.isBlank()) {
				mainFrames.remove(mainFrame);
				mainFrame.dispose();
			}
		}
	}

	/**
	 * get a project file name to create
	 *
	 * @param projtitle
	 * @return
	 */
	public static String getFileName(String projtitle) {
		//LOG.trace(TT+"getFileName()");
		String titlename = StringUtil.escapeTxt(projtitle);
		String x[] = titlename.split(" ");
		titlename = "";
		for (String z : x) {
			titlename += StringUtil.capitalize(z);
		}
		String filename = EnvUtil.getHomeDir().getPath()
		   + File.separator
		   + titlename
		   + STORYBOOK.FILE_EXT_OSBK.toString();
		File f = IOUtil.fileSelect(null, filename, "osbk", "osbk", "file.create");
		if (f == null) {
			return "";
		}
		return f.getAbsolutePath();
	}

	/**
	 * initialize a MainFrame for a new file
	 *
	 * @param dbFile
	 *
	 * @return
	 */
	private MainFrame initNewFile(H2File dbFile) {
		//LOG.trace(TT + "initNewFile(dbFile=" + dbFile.getH2Name() + ", title=" + thetitle + ")");
		MainFrame newMainFrame = new MainFrame();
		newMainFrame.init(dbFile);
		return newMainFrame;
	}

	/**
	 * initialize the properties for a new file project
	 *
	 * @param newMainFrame
	 * @param thetitle
	 * @param fromDoc
	 */
	private void initNewProperties(MainFrame newMainFrame, String thetitle, String... fromDoc) {
		PropertiesDlg properties = new PropertiesDlg(newMainFrame, true);
		properties.tfTitle.setText(thetitle);
		if (fromDoc != null && fromDoc.length > 0) {
			String ext = (fromDoc[0].endsWith(".docx") ? "docx" : "odt");
			properties.getXeditor().setExtension(ext);
		}
		properties.setVisible(true);
		newMainFrame.getBook().setProperties(properties);
		newMainFrame.getBook().setString(Book.PARAM.LAYOUT_BOOK,
		   preferences.getString(Pref.KEY.BOOK_LAYOUT));
	}

	/**
	 * initialize folders for a new project
	 *
	 * @param newMainFrame
	 * @param dbFile
	 * @param thetitle
	 */
	private void initNewFileEnd(MainFrame newMainFrame, H2File dbFile, String thetitle) {
		/*LOG.trace(TT + "initNewFileEnd(mainFrame"
			+ ", dbFile=" + dbFile.getH2Name()
			+ ", title=" + thetitle + ")");*/
		newMainFrame.initUi();
		newMainFrame.getBookController().fireAgain();
		addMainFrame(newMainFrame);
		closeBlank();
		recentfilesUpdate(dbFile, thetitle);
		newMainFrame.reloadBook();
		newMainFrame.fileSave(true);
		//create new Images directory
		IOUtil.dirCreate(dbFile.getPath(), "Images");
		//create new Documents directory
		IOUtil.dirCreate(dbFile.getPath(), "Documents");
		initNewProperties(newMainFrame, thetitle);
		setDefaultCursor();
	}

	/**
	 * create a new project file
	 */
	public void createNewFile() {
		//LOG.trace(TT + "createNewFile()");
		NewProjectDlg dlg = new NewProjectDlg(mainFrames.get(0));
		dlg.setVisible(true);
		if (dlg.isCanceled()) {
			return;
		}
		String thetitle = dlg.getTitle();
		int nature = dlg.getNature();
		int nbParts = dlg.getNbParts();
		int nbChapters = dlg.getNbChapters();
		Date objective = dlg.getObjective();
		String filename = getFileName(thetitle);
		if (filename.isEmpty()) {
			return;
		}
		if (filename.endsWith(STORYBOOK.FILE_EXT_H2DB.toString())) {
			filename = filename.replace(STORYBOOK.FILE_EXT_H2DB.toString(), "");
		}
		if (filename.endsWith(STORYBOOK.FILE_EXT_MVDB.toString())) {
			filename = filename.replace(STORYBOOK.FILE_EXT_MVDB.toString(), "");
		}
		if (filename.endsWith(STORYBOOK.FILE_EXT_OSBK.toString())) {
			filename = filename.replace(STORYBOOK.FILE_EXT_OSBK.toString(), "");
		}
		File fOsbk = new File(filename + STORYBOOK.FILE_EXT_OSBK.toString());
		File fH2db = new File(filename + STORYBOOK.FILE_EXT_H2DB.toString());
		File fMvdb = new File(filename + STORYBOOK.FILE_EXT_MVDB.toString());
		if (fOsbk.exists() || fH2db.exists() || fMvdb.exists()) {
			int ret = ConfirmDlg.show(null,
			   I18N.getMsg("file.save.overwrite.title"),
			   I18N.getMsg("file.exists", filename),
			   false);
			if (ret == ConfirmDlg.CANCEL) {
				return;
			}
			IOUtil.fileDelete(fOsbk);
			IOUtil.fileDelete(fH2db);
			IOUtil.fileDelete(fMvdb);
		}
		H2File dbFile = new H2File(filename + STORYBOOK.FILE_EXT_MVDB.toString());
		String dbName = dbFile.getH2Name();
		if (dbName == null) {
			return;
		}
		MainFrame newMainFrame = initNewFile(dbFile);
		newMainFrame.getBookModel().initEntities(nature, nbParts, nbChapters, objective);
		initNewFileEnd(newMainFrame, dbFile, thetitle);
		LOG.trace("file '" + dbName + "' was created");
	}

	/**
	 * import a document as an OSBK project
	 *
	 * @param hi : the import document
	 */
	public void importBook(ImportDocument hi) {
		//LOG.trace(TT + "importBook(hi)");
		MainFrame newMainFrame = initNewFile(hi.getDBFile());
		newMainFrame.getBookModel().initEntities(hi.getTitle());
		newMainFrame.getBook().setString(Book.PARAM.LAYOUT_SCENE_SEPARATOR,
		   preferences.getString(Pref.KEY.SCENE_SEPARATOR));
		Session session = newMainFrame.getBookModel().beginTransaction();
		hi.getAll(session);
		session.getTransaction().commit();
		initNewFileEnd(newMainFrame, hi.getDBFile(), hi.getTitle());
	}

	/**
	 * rename the OSBK file
	 *
	 * @param mainFrame
	 * @param outFile
	 */
	public void renameFile(final MainFrame mainFrame, File outFile) {
		//LOG.trace(TT+"renameFile(mainFrame,outFile="+outFile.getAbsolutePath()+")");
		try {
			File inFile = mainFrame.getH2File().getFile();
			mainFrame.close(false);
			Path inPath = inFile.toPath();
			Path outPath = outFile.toPath();
			Files.move(inPath, outPath, REPLACE_EXISTING);
			H2File dbFile = new H2File(outFile);
			openFile(dbFile, true);
		} catch (IOException e) {
			LOG.err(TT + "renameFile(" + mainFrame.getName() + "," + outFile.getName() + ")", e);
		}
	}

	/**
	 * open the project file
	 *
	 * @return
	 */
	public boolean openFile() {
		//LOG.trace(TT+"openFile()");
		final H2File h2File = BookUtil.openDialog();
		if (h2File == null) {
			return false;
		}
		return openFile(h2File, true);
	}

	/**
	 * open the H2DB file
	 *
	 * @param h2File
	 * @param oldPath
	 * @param newPath
	 * @return
	 */
	public boolean openFile(final H2File h2File, String oldPath, String newPath) {
		boolean rc = openFile(h2File, true);
		SwingUtilities.invokeLater(() -> App.changingDbFile(h2File, oldPath, newPath));
		return rc;
	}

	/**
	 * change the file
	 *
	 * @param h2File
	 * @param oldPath
	 * @param newPath
	 */
	public static void changingDbFile(H2File h2File, String oldPath, String newPath) {
		MainFrame mf = null;
		for (MainFrame m : App.getInstance().mainFrames) {
			LOG.trace("changingDbFile h2File=" + h2File.getH2Name() + ", m.dbFile=" + m.getH2File().getH2Name());
			if (m.getH2File().equals(h2File)) {
				mf = m;
			}
		}
		if (mf == null) {
			LOG.trace("changingDbFile mf=null");
			return;
		}
		mf.changePath(oldPath, newPath);
		mf.getBook().setTitle(I18N.getMsg("copyof") + " " + mf.getBook().getTitle());
	}

	/**
	 * open the H2DB file
	 *
	 * @param h2File
	 * @param mustExists
	 * @return
	 */
	public boolean openFile(H2File h2File, boolean mustExists) {
		//LOG.trace(TT+"openFile(" + h2File.getH2Name() + ")");
		if (!h2File.isOK() || !h2File.open()) {
			return (false);
		}
		if (h2File.isAlreadyOpened()) {
			return (true);
		}
		try {
			// model update from STORYBOOK 3.x to 4.0
			final ModelMigration oldPersMngr = ModelMigration.getInstance();
			if (oldPersMngr.open(h2File)) {
				try {
					if (!oldPersMngr.checkAndAlterModel(false)) {
						oldPersMngr.closeConnection();
						return false;
					}
				} catch (Exception e) {
					oldPersMngr.closeConnection();
					ExceptionDlg.show(this.getClass().getSimpleName()
					   + ".openFile(" + h2File.getH2Name() + ")", e);
					return false;
				}
				oldPersMngr.closeConnection();
				if (oldPersMngr.isAltered()) {
					MessageDlg.show(null,
					   I18N.getMsg("file.optimize_warning"),
					   I18N.getMsg("file.optimize"),
					   true);
				}
			}
			setWaitCursor();
			final WaitingSplash dlg = new WaitingSplash(null, I18N.getMsg("loading", h2File.getName()));
			SwingUtilities.invokeLater(() -> {
				try {
					setWaitCursor();
					//LOG.trace("loading file... " + h2File.getH2Name());
					dlg.setText("Initialize MainFrame...");
					MainFrame newMainFrame = new MainFrame(h2File);
					dlg.setText("Adding MainFrame...");
					addMainFrame(newMainFrame);
					dlg.setText("Close blank...");
					closeBlank();
					dlg.setText("Update preferences...");
					recentfilesUpdate(h2File, newMainFrame.getBook().getTitle());
					dlg.setText("Reload menu bars...");
					reloadMenuBars();
					setDefaultCursor();
				} catch (Exception e) {
					ExceptionDlg.show(this.getClass().getSimpleName()
					   + ".openFile(" + h2File.getH2Name() + ")", e);
				}
				dlg.dispose();
			});
		} catch (HeadlessException e) {
			LOG.err(TT + "openFile(...) error", e);
		}
		return true;
	}

	/**
	 * update the preferences for the given recent file
	 *
	 * @param h2file
	 * @param title
	 */
	public void recentfilesUpdate(H2File h2file, String title) {
		//LOG.trace(TT+"recentfilesUpdate(h2file='" + h2File.getH2Name() + "', title='"+title+")");
		if (h2file == null || title == null || title.isEmpty()) {
			return;
		}
		// save last openDocument directory and file
		File file = h2file.getFile();
		preferences.setString(Pref.KEY.LASTOPEN_DIR, file.getParent());
		preferences.setString(Pref.KEY.LASTOPEN_FILE, file.getPath());
		// save recent files
		preferences.recentFilesAdd(h2file.getOSBK(), title);
		preferences.save();
		reloadMenuBars();
	}

	/**
	 * clear the recent files list
	 *
	 */
	public void recentfilesClear() {
		//LOG.trace(TT+"recentFilesClear()");
		preferences.recentFilesClear();
		reloadMenuBars();
	}

	/**
	 * exit the application
	 *
	 */
	public void exit() {
		//LOG.trace(TT+"exit()");
		if (!mainFrames.isEmpty()) {
			for (MainFrame m : mainFrames) {
				if (m.askForSave() == 0) {
					return;
				}
			}
		}
		if (!mainFrames.isEmpty()
		   && preferences.getBoolean(Pref.KEY.CONFIRM_EXIT)
		   && ConfirmDlg.show(mainFrames.get(0),
			  I18N.getMsg("file.exit"),
			  I18N.getMsg("want.exit"),
			  false) == ConfirmDlg.CANCEL) {
			return;
		}
		preferences.save();
		System.exit(0);
	}

	/**
	 * refresh the data
	 */
	public void refresh() {
		//LOG.trace(TT+"refresh()");
		for (MainFrame mainFrame : mainFrames) {
			int width = mainFrame.getWidth();
			int height = mainFrame.getHeight();
			boolean maximized = mainFrame.isMaximized();
			mainFrame.getMainMenu().reloadToolbar();
			mainFrame.setSize(width, height);
			if (maximized) {
				mainFrame.setMaximized();
			}
			mainFrame.refresh();
		}
	}

	/**
	 * reload the menu bar
	 */
	public void reloadMenuBars() {
		//LOG.trace(TT + "reloadMenuBars()");
		for (MainFrame mainFrame : mainFrames) {
			mainFrame.getMainMenu().reloadRecentMenu();
			mainFrame.getMainMenu().reloadToolbar();
			mainFrame.getMainMenu().reloadWindowMenu();
		}
	}

	/**
	 * reload the status bar
	 */
	public void reloadStatusBars() {
		for (MainFrame mainFrame : mainFrames) {
			mainFrame.refreshStatusBar();
		}
	}

	/**
	 * set waiting cursor
	 */
	public void setWaitCursor() {
		for (MainFrame mainFrame : mainFrames) {
			SwingUtil.setWaitingCursor(mainFrame);
		}
	}

	/**
	 * set default cursor
	 */
	public void setDefaultCursor() {
		for (MainFrame mainFrame : mainFrames) {
			SwingUtil.setDefaultCursor(mainFrame);
		}
	}

	/**
	 * action for model properties change
	 *
	 * @param evt
	 */
	public void modelPropertyChange(PropertyChangeEvent evt) {
		// works, but currently not used
		// may be used for entity copying between files
	}

	/**
	 * lock the instance
	 *
	 * @param lockFile
	 * @return
	 */
	private static boolean lockInstance(final String lockFile) {
		try {
			final File file = new File(lockFile);
			final RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
			final FileLock fileLock = randomAccessFile.getChannel().tryLock();
			if (fileLock != null) {
				Runtime.getRuntime().addShutdownHook(new Thread() {
					@Override
					public void run() {
						try {
							fileLock.release();
							randomAccessFile.close();
							file.delete();
						} catch (IOException e) {
							LOG.err("Unable to remove lock file: " + lockFile, e);
						}
					}
				});
				return true;
			}
		} catch (IOException e) {
			LOG.err("Unable to create and/or lock file: " + lockFile, e);
		}
		return false;
	}

	/**
	 * get the I18N filename
	 *
	 * @return
	 */
	public String getI18nFile() {
		return i18nFile;
	}

	/**
	 * set the I18N filename
	 *
	 * @param file
	 */
	public void setI18nFile(String file) {
		i18nFile = file;
	}

	/**
	 * refresh view for all MainFrame
	 */
	public void refreshViews() {
		//LOG.trace(TT+"refreshViews()");
		for (MainFrame mainFrame : mainFrames) {
			mainFrame.refresh();
		}
	}

	/**
	 * refresh the status bar for all MainFrame
	 */
	public void refreshStatusBar() {
		for (MainFrame m : mainFrames) {
			m.refreshStatusBar();
		}
	}

	/**
	 * initialize the dictaphone
	 */
	private static void dictaphoneInit() {
		//LOG.trace(TT + "initDictaphone()");
		Dictaphone.init();
	}

	/**
	 * start the Ideabox frame
	 */
	private void ideaboxStart() {
		SwingUtilities.invokeLater(() -> {
			IdeaxFrm dlg = new IdeaxFrm();
			dlg.setVisible(true);
			dlg.toFront();
			dlg.requestFocus();
		});
	}

	/////// Copy/Paste functions
	private boolean pasteOK = false;

	public boolean pasteIsOK() {
		return pasteOK;
	}

	public void enablePaste(boolean b) {
		pasteOK = b;
		for (MainFrame m : mainFrames) {
			m.getMainMenu().enablePaste(b);
		}
	}

	public void enableCopyEntity() {
		for (MainFrame m : mainFrames) {
			m.getMainMenu().enableCopyEntity(mainFrames.size() > 1);
		}
	}

}
