public class ConfigIO {

	// private constructor to prevent from initializing this class
	private ConfigIO() {
		// empty constructor
	}

	// =========================== public methods
	// ===============================

	/**
	 * Writes an xml representation of this configuration to a file. If the file
	 * already exists, the user will be asked if he wants to overwrite.
	 */
	public static void write(File file, Configuration config, Proxy proxy) throws IOException {

		// check, if file already exists
		if (file.exists()) {
			// ask user to confirm overwrite
			int option = JOptionPane.showConfirmDialog(EFrame.getInstance(), file.getName()
					+ Dictionary.CONFIRM_OVERWRITE_TEXT.getText(), Dictionary.CONFIRM_OVERWRITE_TITLE.getText(),
					JOptionPane.OK_CANCEL_OPTION);
			if (option == JOptionPane.CANCEL_OPTION) {
				// if writing canceled, return
				return;
			}
		}

		// announce that we are writing the configuration
		Progressor.getInstance().setText(Dictionary.CONFIGURATION_WRITING.getText());
		int max = config.getGarten().getAllKinder().length;
		Progressor.getInstance().setProgress(0, max);

		// collect all tags in a list
		List<String> list = new LinkedList<>();
		list.add(XMLUtil.ENIAC_HEADER);
		list.add(XMLUtil.wrapOpenTag(EData.Tag.ENIAC.name().toLowerCase()));
		proxy.appendTags(list, 1);
		config.appendTags(list, 1);
		list.add(XMLUtil.wrapCloseTag(EData.Tag.ENIAC.name().toLowerCase()));

		// convert list to stringbuffer
		StringBuilder stringBuilder = new StringBuilder();
		for (String s : list) {
			stringBuilder.append(s);
			stringBuilder.append('\n');
		}

		// write stringbuffer to file.
		Writer writer = IOUtil.openWriter(file);
		writer.write(stringBuilder.toString());
		writer.flush();
		writer.close();
	}

	/**
	 * Loads a configuration from the local file system
	 * 
	 * @param path
	 *            a <code>string</code> containing the configuration's path
	 */
	public static void loadConfiguration(String path) {
		InputStream in = IOUtil.openInputStream(path);
		loadConfiguration(in);
	}

	/**
	 * Loads a basic configuration from the classLoader as described by a
	 * <code>configurationProxy</code>
	 * 
	 * @param proxy
	 *            the <code>configurationProxy</code> identifying the
	 *            configuration to load.
	 */
	public static void loadConfiguration(Proxy proxy) {
		String path = proxy.getPath();
		InputStream in = Manager.getInstance().getResourceAsStream(path);
		loadConfiguration(in);
	}

	public static List<Proxy> loadProxies() {
		String path = getPathWithoutIndex();
		int max = StringConverter.toInt(EProperties.getInstance().getProperty("MAX_NUMBER_OF_CONFIGS"));
		String text = Dictionary.CONFIGURATION_SCANNING.getText();
		return IOUtil.loadProxies(path, max, text);
	}

	public static void loadDefaultConfiguration() {
		if (EProperties.getInstance().getProperty("INDEX_OF_DEFAULT_CONFIG").startsWith("-")) {

			// if no default configuration is given, let user choose one
			(new OpenConfiguration()).run();
		}
		else {

			// otherwise load configuration as given by default index
			int index = StringConverter.toInt(EProperties.getInstance().getProperty("INDEX_OF_DEFAULT_CONFIG"));
			String path = getPathWithoutIndex();
			String fullPath = IOUtil.addIndex(path, index);
			Proxy proxy = IOUtil.loadProxy(fullPath);
			loadConfiguration(proxy);
		}
	}

	// ========================== private methods
	// ===============================

	private static String getPathWithoutIndex() {
		String folder = EProperties.getInstance().getProperty("CONFIG_FOLDER");
		String file = EProperties.getInstance().getProperty("CONFIG_FILE_WITHOUT_INDEX");
		return folder + "/" + file; //$NON-NLS-1$
	}

	// this is called, when loading the configuration failed
	private static void loadingFailed(String reason) {
		Log.log(LogWords.COULD_NOT_LOAD_CONFIGURATION, JOptionPane.ERROR_MESSAGE, reason, true);
	}

	private static void loadingSucceded(Configuration newConfig) {

		// dispose old configuration object, if there is any
		// EFrame.getInstance().disposeConfigPanel();
		Object oldConfig = Status.CONFIGURATION.getValue();
		Status.CONFIGURATION.setValue(null);
		if (oldConfig != null) {
			((Configuration) oldConfig).dispose();
		}

		// init new configuration object
		newConfig.init();

		// there were invalid ids. Display message to user.
		if (newConfig.getIDManager().hasInvalids()) {
			newConfig.getIDManager().integrateInvalids();
			Log.log(LogWords.INVALID_IDS_CONTAINED, JOptionPane.INFORMATION_MESSAGE, true);
		}

		// set new configuration Object as current Configuration
		Status.CONFIGURATION.setValue(newConfig);
	}

	// loads a configuration from the given inputStream.
	private static void loadConfiguration(InputStream in) {

		// check for null
		if (in == null) {
			loadingFailed("Inputstream is null"); //$NON-NLS-1$
			return;
		}

		// create handler
		ConfigHandler handler = new ConfigHandler();

		// parse data tree from xml
		try {
			IOUtil.parse(in, handler);
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		// check, if parsing was successful
		Configuration config = handler.getConfiguration();
		if (config != null) {
			loadingSucceded(config);
		}
		else {
			loadingFailed("new configuration is null"); //$NON-NLS-1$
		}
	}
} public class Configuration extends ParentData {

	private IDManager _idManager = new IDManager();

	// ============================ lifecycle
	// ===================================

	public Configuration() {
		// empty
	}

	public void dispose() {
		super.dispose();
		_idManager.dispose();
		_idManager = null;
	}

	// ============================== methods
	// ===================================

	public IDManager getIDManager() {
		return _idManager;
	}

	public Configuration getConfiguration() {
		return this;
	}

	public CyclingLights getCyclingLights() {
		return (CyclingLights) getGarten().getKind(EType.CYCLING_LIGHTS, 0);
	}
} public class TypeHandler extends DefaultHandler {

	// =============================== fields
	// ===================================

	private EType _type;

	private List<String> _listOfCodes = new LinkedList<>();

	// character data as parsed by characters()
	private String _cdata = null;

	// flag indicating whether we are reading whitespace
	private boolean _readWhitespace = false;

	// ============================= constructor
	// ================================

	public TypeHandler() {
		// empty
	}

	// ========================== defaultHandler methods
	// ========================

	public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
// System.out.println(qName);
		try {
			if (qName.equalsIgnoreCase(EType.Tag.TYPE.toString())) {
				// create new EType by name
				String name = XMLUtil.parseString(attrs, EType.Tag.NAME);
				_type = Enum.valueOf(EType.class, name.toUpperCase());

			}
			else if (qName.equalsIgnoreCase(EType.Tag.CODES.toString())) {
				// init list of codes
				_listOfCodes.clear();
				// set codeName
				String codeName = XMLUtil.parseString(attrs, EType.Tag.NAME).toUpperCase();
				_type.setCodeName(Enum.valueOf(EType.Tag.class, codeName));

			}
			else if (qName.equalsIgnoreCase(EType.Tag.MODEL.toString())
					|| qName.equalsIgnoreCase(EType.Tag.VIEW.toString())
					|| qName.equalsIgnoreCase(EType.Tag.CODE.toString())) {
				// start reading whitespace
				_readWhitespace = true;
			}
		} catch (Exception e) {
			// important: catch any exception and print its tree.
			// otherwise you won't get the error's source.
			e.printStackTrace();
			throw new SAXException(e);
		}
	}

	public void endElement(String uri, String localName, String qName) throws SAXException {

		try {
			if (qName.equalsIgnoreCase(EType.Tag.TYPE.toString())) {
				// finished parsing this type. Set it to Prototypes.
				// ProtoTypes.setType(_type);

			}
			else if (qName.equalsIgnoreCase(EType.Tag.CODES.toString())) {
				// convert list of codes to an array and set it to type
				String[] codes = new String[_listOfCodes.size()];
				_listOfCodes.toArray(codes);
				_type.setCodes(codes);

			}
			else if (qName.equalsIgnoreCase(EType.Tag.MODEL.toString())) {
				// set edata class and stop reading whitespace
				_type.setEDataClass(_cdata);
				_cdata = null;
				_readWhitespace = false;

			}
			else if (qName.equalsIgnoreCase(EType.Tag.VIEW.toString())) {
				// set epanel class and stop reading whitespace
				_type.setEPanelClass(_cdata);
				_cdata = null;
				_readWhitespace = false;

			}
			else if (qName.equalsIgnoreCase(EType.Tag.CODE.toString())) {
				// add code to list of codes and stop reading whitespace
				_listOfCodes.add(_cdata);
				_cdata = null;
				_readWhitespace = false;
			}
		} catch (Exception e) {
			// in case of exception, print stack trace and rethrow as sax
			e.printStackTrace();
			throw new SAXException(e);
		}
	}

	public void warning(SAXParseException e) throws SAXException {
		Log.log(e.toString());
	}

	public void error(SAXParseException e) throws SAXException {
		Log.log(e.toString());
	}

	public void characters(char[] ch, int start, int length) {
		if (_readWhitespace) {
			if (_cdata == null) {
				_cdata = new String(ch, start, length);
			}
			else {
				_cdata += new String(ch, start, length);
			}
		}
	}

	public static void loadTypes() {
		String file = EProperties.getInstance().getProperty("PROTOTYPES_FILE");
		InputStream in = Manager.class.getClassLoader().getResourceAsStream(file);
		TypeHandler handler = new TypeHandler();
		try {
			IOUtil.parse(in, handler);
		} catch (Exception e) {
			System.out.println("Error in initializing types"); //$NON-NLS-1$
		}
	}

} public class Progressor extends JDialog implements Runnable, StatusListener {

	// jpanel as contentpane
	private JPanel _panel = new JPanel(new BorderLayout());

	// a separate thread is startet in order to show the dialog whith blocking
	// all other gui but without blocking the initialization process.
	private Thread _thread = null;

	// cancel button
	private JButton _button = new JButton();

	// label to display the current task, its progress we are showing
	private JLabel _label = new JLabel(Dictionary.INITIALIZING.getText());

	// progressbar to show progress of our
	private JProgressBar _progressBar = new JProgressBar();

	// ============================ singleton stuff
	// =============================

	// singleton self reference
	private static Progressor instance;

	private Progressor() {

		// create progressor as modal dialog with eframe as owner
		super(EFrame.getInstance(), Dictionary.PLEASE_WAIT.getText(), true);

		// add as status listener to be notified when language or runlevel
// changes
		Status.LANGUAGE.addListener(this);
		Status.LIFECYCLE.addListener(this);

		// init components
		// _progressBar.setIndeterminate(true);
		setAction(null);
		_panel.add(_label, BorderLayout.NORTH);
		_panel.add(_progressBar, BorderLayout.CENTER);
		_panel.add(_button, BorderLayout.SOUTH);
		setContentPane(_panel);
		setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

		// bring it to the screen.
		pack();
		setLocationRelativeTo(EFrame.getInstance());

		// start thread that controlles visibility
		_thread = new Thread(this);
		_thread.start();
	}

	public static Progressor getInstance() {
		if (instance == null) {
			instance = new Progressor();
		}
		return instance;
	}

	// =============================== methods
	// ==================================

	public void setAction(Action a) {

		// if no action wanted, disable button.
		if (a == null) {
			a = new AbstractAction(Dictionary.CANCEL.getText()) {
				public void actionPerformed(ActionEvent e) {
					// empty
				}

				public boolean isEnabled() {
					return false;
				}
			};
		}

		// create and add action
		_panel.getActionMap().put(a.getValue(Action.NAME), a);

		// map action to button
		_button.setAction(a);
		_button.setEnabled(true);

		// fill actionMap
		_panel.getActionMap().put(Dictionary.CANCEL, a);

		// fill inputMap
		_panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), Dictionary.CANCEL);

		// adjust inputMaps of buttons
		_button.getActionMap().setParent(_panel.getActionMap());
		_button.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
				Dictionary.CANCEL);
	}

	public void setText(String text) {
		_label.setText(text);
	}

	public void clear() {
		setText(Dictionary.INITIALIZING.getText());
		_progressBar.setIndeterminate(true);
	}

	public void incrementValue() {
		_progressBar.setValue(_progressBar.getValue() + 1);
	}

	public void setProgress(int value, int max) {
		_progressBar.setIndeterminate(false);
		_progressBar.setValue(value);
		_progressBar.setMaximum(max);
	}

	public void run() {

		// run this thread until applet is shutting down.
		while (Status.LIFECYCLE.getValue() != Manager.LifeCycle.STOPPED
				|| Status.LIFECYCLE.getValue() != Manager.LifeCycle.DESTROYED) {

			// show progressor, if applet is busy
			if (Status.LIFECYCLE.getValue() != Manager.LifeCycle.RUNNING) {
				setVisible(true);
			}

			// wait until the runlevel changes
			synchronized (this) {
				if (Status.LIFECYCLE.getValue() == Manager.LifeCycle.BLOCKED) {
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
		// applet is shutting down. Hide and dispose progressor.
		setVisible(false);
		_thread = null;
		instance = null;
		dispose();
	}

	public synchronized void statusChanged(Status status, Object newValue) {
		assert status == Status.LANGUAGE || status == Status.LIFECYCLE;

		switch (status) {
			case LANGUAGE :
				_button.setText(Dictionary.CANCEL.getText());
				setTitle(Dictionary.PLEASE_WAIT.getText());
				break;

			case LIFECYCLE :
				// If applet is idling, hide progressor.
				// otherwise notify thread to show it.
				if (newValue == Manager.LifeCycle.RUNNING) {
					setVisible(false);
				}
				else if (newValue == Manager.LifeCycle.STOPPED) {
					setVisible(false);
					notifyAll();
				}
				else {
					notifyAll();
				}
				break;

			default :
				break;
		}
	}
} public class DictionaryIO {

	public static final String USER_LANGUAGE_SYSTEM_PROPERTY = "user.language"; //$NON-NLS-1$

	private DictionaryIO() {
		// empty
	}

	public static List<Proxy> loadProxies() {
		String path = getLanguagePathWithoutIndex();
		int max = StringConverter.toInt(EProperties.getInstance().getProperty("MAX_LANGUAGE_INDEX"));
		String text = Dictionary.DICTIONARY_SCANNING.getText();
		return IOUtil.loadProxies(path, max, text);
	}

	public static void loadLanguage(Proxy proxy) {

		// announce that we are loading a language
		Progressor.getInstance().setText(Dictionary.DICTIONARY_LOADING.getText());

		String path = proxy.getPath();
		InputStream in = Manager.getInstance().getResourceAsStream(path);
		DictionaryHandler handler = new DictionaryHandler();
		try {
			IOUtil.parse(in, handler);

			// set new language
			Status.LANGUAGE.setValue(proxy.get(Proxy.Tag.KEY));
		} catch (IOException e) {
			Log.log("Loading of language failed. Cannot change to new language", //$NON-NLS-1$
					JOptionPane.ERROR_MESSAGE, true);
			e.printStackTrace();
		}
	}

	public static void loadDefaultLanguage() {
		if (Manager.getInstance().hasIOAccess()) {
			// if we are privileged, scan for proxies and call a method,
			// that looks for the users language in the system properties.
			List<Proxy> proxies = loadProxies();
			// get user's default language.
			String locale = System.getProperty(USER_LANGUAGE_SYSTEM_PROPERTY);

			// recurse on proxies and find one that fits to the default language
			for (Proxy proxy : proxies) {
				if (proxy.get(Proxy.Tag.KEY).equals(locale)) {
					// load language and return.
					loadLanguage(proxy);
					return;
				}
			}
			// we have no language bundle for the user's default language.
			// so load default language as encoded at parameters.
		}
		// we are not privileged and so have no access to system properties.
		// so load default language as encoded by parameters.
		loadDefaultLanguageFromParameter();
	}

	private static void loadDefaultLanguageFromParameter() {
		String path = getLanguagePathWithoutIndex();
		int index = StringConverter.toInt(EProperties.getInstance().getProperty("INDEX_OF_DEFAULT_LANGUAGE"));
		String languagePath = IOUtil.addIndex(path, index);
		Proxy proxy = IOUtil.loadProxy(languagePath);
		if (proxy == null) {
			// TODO: if proxy is null, log this and return
			Log.log("proxy is null");
			// System.out.println("proxy is null");
			return;
		}
		loadLanguage(proxy);
	}

	private static String getLanguagePathWithoutIndex() {
		String folder = EProperties.getInstance().getProperty("LANGUAGE_FOLDER");
		String file = EProperties.getInstance().getProperty("LANGUAGE_FILE_WITHOUT_INDEX");
		return folder + "/" + file; //$NON-NLS-1$
	}
} public class LogWindow extends JDialog {

	// reference to logpanel
	private LogPanel _logPanel = null;

	// ============================= singleton stuff
	// ============================

	// singleton self reference
	private static LogWindow instance;

	// private constructor
	private LogWindow() {
		super(EFrame.getInstance(), false);
	}

	public static LogWindow getInstance() {
		if (instance == null) {
			instance = new LogWindow();
			instance.init();
		}
		return instance;
	}

	private void init() {

		// layout
		setTitle(Dictionary.LOG_WINDOW_TITLE.getText());
		_logPanel = Log.getInstance().getLogPanel();
		setContentPane(_logPanel);

		// add status Listener for visibility
		Status.SHOW_LOG.addListener(new StatusListener() {

			@Override
			public void statusChanged(Status status, Object newValue) {
				// show_log changed. Show or hide log window
				setVisible((boolean) newValue);
			}
		});

		// add status listener for language
		Status.LANGUAGE.addListener(new StatusListener() {

			@Override
			public void statusChanged(Status status, Object newValue) {
				// language changed. update window title
				setTitle(Dictionary.LOG_WINDOW_TITLE.getText());
			}
		});

		// add windowListener
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				Status.SHOW_LOG.setValue(false);
			}
		});

		// to the screen
		setBounds(StringConverter.toRectangle(EProperties.getInstance().getProperty("LOG_WINDOW_BOUNDS")));
		setVisible((boolean) Status.SHOW_LOG.getValue());
	}
} public abstract class DialogPanel extends JPanel {

	protected Window _window = null;

	public DialogPanel(LayoutManager lm) {
		super(lm);
	}

	public abstract void performCancelAction();

	public void setWindow(Window window) {
		_window = window;
	}

} public final class SkinIO {

	private SkinIO() {
		// empty
	}

	public static List<Proxy> loadProxies() {
		String path = getSkinPathWithoutIndex();
		int max = StringConverter.toInt(EProperties.getInstance().getProperty("MAX_NUMBER_OF_SKINS"));
		String text = Dictionary.SKIN_SCANNING.getText();
		return IOUtil.loadProxies(path, max, text);
	}

	public static void loadSkin(Proxy proxy) {

		String path = proxy.getPath();
		InputStream in = Manager.getInstance().getResourceAsStream(path);
		Skin skin = new Skin(proxy);
		SkinHandler handler = new SkinHandler(skin);
		try {
			IOUtil.parse(in, handler);

			// check, if all images could be loaded.
			// if not, announce this to the user
			if (handler.hasMissingImages()) {
				Log.log(LogWords.MISSING_IMAGES, JOptionPane.INFORMATION_MESSAGE, true);
			}

			// TODO: this should be done at another place
			// iterate on types
			for (EType type : EType.values()) {
				// set descriptors to etypes
				handler.setDescriptorsToType(type);
			}
			// set new skin
			Status.SKIN.setValue(skin);
		} catch (IOException e) {
			Log.log(LogWords.LOADING_OF_SKIN_FAILED, JOptionPane.ERROR_MESSAGE, true);
			e.printStackTrace();
		}
	}

	public static void loadDefaultSkin() {
		String path = getSkinPathWithoutIndex();
		int index = StringConverter.toInt(EProperties.getInstance().getProperty("INDEX_OF_DEFAULT_SKIN"));
		String skinPath = IOUtil.addIndex(path, index);
		Proxy proxy = IOUtil.loadProxy(skinPath);
		if (proxy == null) {
			Log.log("skin loading: proxy is null");
			// System.out.println("skin loading: proxy is null");
			return;
		}
		loadSkin(proxy);
	}

	private static String getSkinPathWithoutIndex() {
		String folder = EProperties.getInstance().getProperty("SKIN_FOLDER");
		String file = EProperties.getInstance().getProperty("SKIN_FILE_WITHOUT_INDEX");
		return folder + "/" + file; //$NON-NLS-1$
	}
} public enum Status {

	/**
	 * the current lifecycle state
	 */
	LIFECYCLE,

	/**
	 * the active configuration or null
	 */
	CONFIGURATION,

	/**
	 * flag indicating, whether the overview window is shown or not
	 */
	SHOW_OVERVIEW,

	/**
	 * the default height of the configuration panel
	 */
	BASIC_CONFIGURATION_HEIGHT,

	/**
	 * the height of the configuration panel
	 */
	ZOOMED_HEIGHT,

	/**
	 * flag indicating, whether the overview window is shown or not
	 */
	SHOW_LOG,

	/**
	 * the currently loaded skin (currently only buttercream) or null
	 */
	SKIN,

	/**
	 * the currently loaded language
	 */
	LANGUAGE,

	/**
	 * flag indicating, whether the pulses shall be highlighted or not
	 */
	HIGHLIGHT_PULSE,

	/**
	 * the current simulation time
	 */
	SIMULATION_TIME;

	/*
	 * ============================= fields ==================================
	 */

	// the value for this status key
	private Object _value;

	private LinkedList<StatusListener> _listeners = new LinkedList<>();

	/*
	 * ========================= singleton stuff =======================
	 */

	public static void initValues() {
		CONFIGURATION._value = null;
		SHOW_OVERVIEW._value = StringConverter.toBoolean(EProperties.getInstance().getProperty(SHOW_OVERVIEW));
		ZOOMED_HEIGHT._value = StringConverter.toInt(EProperties.getInstance().getProperty(BASIC_CONFIGURATION_HEIGHT));
		SHOW_LOG._value = StringConverter.toBoolean(EProperties.getInstance().getProperty(SHOW_LOG));
		SKIN._value = null;
		LANGUAGE._value = null;
		HIGHLIGHT_PULSE._value = StringConverter.toBoolean(EProperties.getInstance().getProperty(HIGHLIGHT_PULSE));
		SIMULATION_TIME._value = -1L;
	}

	// =========================== getter and setter
	// ============================

	public void setValue(Object newValue) {
		Object oldValue = _value;
		if (oldValue == null) {
			if (newValue != null) {
				_value = newValue;
				informListeners();
			}
		}
		else if (!oldValue.equals(newValue)) {
			_value = newValue;
			informListeners();
		}
	}

	public boolean toggle() {
		return (boolean) (_value = !((boolean) _value));
	}

	public Object getValue() {
		return _value;
	}

	/*
	 * ========================= listener stuff =======================
	 */

	public void addListener(StatusListener listener) {
		_listeners.add(listener);
	}

	public void removeListener(StatusListener listener) {
		_listeners.remove(listener);
	}

	private void informListeners() {
		for (StatusListener listener : _listeners) {
			listener.statusChanged(this, _value);
		}
	}
} public class EFrame extends JFrame implements StatusListener {

	/*
	 * ========================== private fields ===============================
	 */

	// scrollPane as south component of the contentPane (North component is
	// the actionBar).
	private JScrollPane _scrollPane;

	// configuration panel for display the current configuration of the eniac.
	// This component is child to the scrollPane.
	// If no current configuration, this is null.
	private ConfigPanel _configPanel = null;

	private MediaTracker _mediaTracker;

	/*
	 * ============================ singleton stuff ===========================
	 */

	private static EFrame instance;

	private EFrame() {

		// set bounds.
		Dimension mySize = StringConverter.toDimension(EProperties.getInstance().getProperty("EFRAME_SIZE"));
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		mySize.width = Math.min(mySize.width, screenSize.width);
		mySize.height = Math.min(mySize.height, screenSize.height);
		setSize(mySize);
		setLocation((screenSize.width - mySize.width) / 2, (screenSize.height - mySize.height) / 3);

		// create media tracker
		_mediaTracker = new MediaTracker(this);

		// add as singleton to starter
		Status.LIFECYCLE.addListener(this);
	}

	public static EFrame getInstance() {
		if (instance == null) {
			instance = new EFrame();
		}
		return instance;
	}

	/**
	 * Initializes this dvFrame. Listener registration is done, the frame is
	 * layouted and finally brought to the screen.
	 */
	public void toScreen() {

		// set window title
		setTitle(Dictionary.MAIN_FRAME_TITLE.getText());

		// window listener
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				Manager.getInstance().stop();
				Manager.getInstance().destroy();
				dispose();
			}
		});

		// init scrollPane
		_scrollPane = new JScrollPane();
		Color c = StringConverter.toColor(EProperties.getInstance().getProperty("BACKGROUND_COLOR"));
		_scrollPane.getViewport().setBackground(c);

		// add components
		// create and init menu handler
		Hashtable<String, EAction> actionDefaults = new Hashtable<>();
		MenuHandler handler = new MenuHandler(actionDefaults);
		handler.init();

		setJMenuBar(handler.getMenuBar());
		getContentPane().setLayout(new BorderLayout());
		getContentPane().add(handler.getToolBar(), BorderLayout.NORTH);
		getContentPane().add(_scrollPane, BorderLayout.CENTER);

		// update configurationPanel
		// TODO: find new way of initializing configuration
		updateConfigPanel();

		// add this as propertyChangeListener
		Status.CONFIGURATION.addListener(this);
		Status.SKIN.addListener(this);
		Status.LANGUAGE.addListener(this);

		// init LogWindow
		// LogWindow.getInstance();
		setVisible(true);
	}

	/*
	 * =============================== methods =================================
	 */

	// update config panel. This method is called, when the current
	// configuration changed.
	private void updateConfigPanel() {

		// dispose the old panel if any
		if (_configPanel != null) {
			_scrollPane.setViewportView(null);
			_configPanel.dispose();
		}

		// get current configuration and the viewDimension
		Configuration config = (Configuration) Status.CONFIGURATION.getValue();

		// determine which configPanel to set
		if (config == null) {

			// configuration is null. Set null.
			_configPanel = null;
		}
		else {
			// create new configurationPanel
			_configPanel = (ConfigPanel) config.makePanel();
			// add configPanel to scrollPane and init
			_configPanel.init();
			_scrollPane.setViewportView(_configPanel);
		}

		// tell OVWindow to adjust its panel to the new configPanel
		OVWindow.getInstance().configPanelChanged();
	}

	public int showFileChooser(JFileChooser chooser, String approveButtonText) {
		return chooser.showDialog(this, approveButtonText);
	}

	public ConfigPanel getConfigPanel() {
		return _configPanel;
	}

	/*
	 * ===================== PropertyChangeListener methods ==================
	 */

	/**
	 * This method is called when a status property is changed
	 * 
	 * @param evt
	 *            The propertyEvent indicating that the architecture changed
	 */
	public void statusChanged(Status status, Object newValue) {

		switch (status) {

			case CONFIGURATION :
				// configuration changed. Update the configPanel.
				updateConfigPanel();
				break;
			case SKIN :
				// skin changed. repaint
				repaint();
				break;

			case LANGUAGE :
				// language changed. repaint and adjust title.
				repaint();

				// set default locale to JComponent.
				// So optionPane buttons get the right language.
				JComponent.setDefaultLocale(new Locale((String) newValue));
				setTitle(Dictionary.MAIN_FRAME_TITLE.getText());
				break;

			case LIFECYCLE :
				if (newValue == Manager.LifeCycle.STOPPED) {
					setVisible(false);
				}
				else if (newValue == Manager.LifeCycle.DESTROYED) {
					dispose();
					instance = null;
				}
				break;

			default :
				break;
		}
	}

	/*
	 * ============================ changelistener ===========================
	 */

	public void addChangeListener(ChangeListener listener) {
		_scrollPane.getViewport().addChangeListener(listener);
	}

	public void removeChangeListener(ChangeListener listener) {
		_scrollPane.getViewport().removeChangeListener(listener);
	}

	/**
	 * @param path
	 * @return
	 */
	public Image getResourceAsImage(String name) {

		// get url. If path cannot be resolved, return null.
		URL url = Manager.class.getClassLoader().getResource(name);
		if (url == null) {
			return null;
		}

		Image img = Toolkit.getDefaultToolkit().createImage(url);
		_mediaTracker.addImage(img, 1);
		try {
			_mediaTracker.waitForAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		return img;
	}

}
import java.applet.Applet;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.InputStream;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import eniac.data.io.ConfigIO;
import eniac.data.model.parent.Configuration;
import eniac.data.type.TypeHandler;
import eniac.io.Progressor;
import eniac.lang.DictionaryIO;
import eniac.log.LogWindow;
import eniac.menu.action.gui.DialogPanel;
import eniac.skin.SkinIO;
import eniac.util.Status;
import eniac.window.EFrame;
/*******************************************************************************
 * Copyright (c) 2003-2005, 2013 Till Zoppke.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Till Zoppke - initial API and implementation
 ******************************************************************************/
package eniac;




/**
 * The main class of this project and the one extending the JApplet. So on
 * start-up the (empty) constructor is called by the browser, and then the
 * methods init() and start(). If you start the program as an application, the
 * main(String[]) method does this instead. <br>
 * <br>
 * The applet's property of participating in the java.awt.component-hierarchy is
 * neglected. The applet doesn't display any content itself, but it opens an
 * {@link eniac.window.EFrame} for this purpose. So the applet's size should be
 * like 1x1 or 0x0 in order to hide its defaulty greyness. <br>
 * <br>
 * This class principally implements the singleton pattern (just the constructor
 * is public because as an applet it has to), There is a couple of other
 * singleton classes in this project. These classes implement the
 * {@link eniac.LifecycleListener} interface. Their instances can be added as
 * listeners to the Main-instance in order to be informed about the applet's
 * lifecycle state. <br>
 * <br>
 * The applet's life cycles through 7 runlevels.
 * 
 * @author zoppke
 */
public class Manager {

	/*
	 * ========================= applet lifecycle states =======================
	 */

	public enum LifeCycle {

		/**
		 * Default lifecycle state on startup
		 */
		DEFAULT,

		/**
		 * Livecycle state indicating a successful initialization
		 */
		INITIALIZED,

		/**
		 * Lifecycle state indicating that the application is running and the
		 * gui expects input.
		 */
		RUNNING,

		/**
		 * Lifecycle state indicating that the application is running but the
		 * gui is blocked.
		 */
		BLOCKED,

		/**
		 * Lifecycle state indicating that the application is stopped.
		 */
		STOPPED,

		/**
		 * Lifecycle state indicating that the application is destroyed.
		 */
		DESTROYED,
	}

	/*
	 * =============================== fields ==================================
	 */

	// flag indicating, whether we have privileged local file access
	private boolean _ioAccess;

	// reference to the applet. Stays null, if started as application.
	private Applet _applet = null;

	/*
	 * ========================== singleton stuff ==============================
	 */

	private Manager() {
		// empty constructor
	}

	// singleton self reference
	private static Manager instance = null;

	public static Manager getInstance() {
		if (instance == null) {
			instance = new Manager();
		}
		return instance;
	}

	/*
	 * =================== lifecycle state transitions =========================
	 */

	/**
	 * creates and initializes all instances
	 */
	public void init() {
		Status.initValues();
		DictionaryIO.loadDefaultLanguage();
		SkinIO.loadDefaultSkin();
		Status.LIFECYCLE.setValue(LifeCycle.INITIALIZED);
	}

	public void start() {

		// loading the configuration may open an input dialog for the user.
		// as the java-plugin may expect this method to return quickly,
		// we start a new thread.
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {

				// load types
				TypeHandler.loadTypes();

				// load default configuration
				ConfigIO.loadDefaultConfiguration();

				// check, if we haven't been interrupted
				if (Status.LIFECYCLE.getValue() == LifeCycle.INITIALIZED) {

					// open eframe and adjust runlevel
					EFrame.getInstance().toScreen();
					LogWindow.getInstance();
					Status.LIFECYCLE.setValue(LifeCycle.RUNNING);
				}
			}
		});
	}

	public void stop() {

		// TODO: check, if we need to block first

		// dispose configuration
		Configuration config = (Configuration) Status.CONFIGURATION.getValue();
		if (config != null) {
			config.dispose();
		}
		Status.CONFIGURATION.setValue(null);

		// announce that applet is shutting down
		Status.LIFECYCLE.setValue(LifeCycle.STOPPED);
	}

	public void destroy() {

		// check that we haven't been destroyed before
		if (Status.LIFECYCLE.getValue() != LifeCycle.STOPPED) {
			return;
		}

		// run finalization.
		// Though it probably has no effect, the purpose provides good fortune.
		System.runFinalization();

		// announce that applet is destroyed
		Status.LIFECYCLE.setValue(LifeCycle.DESTROYED);
	}

	/*
	 * ============================== methods ==================================
	 */

	public void setApplet(Applet applet) {
		_applet = applet;
	}

	public void makeDialog(DialogPanel content, String title) {