package org.spview.gui;

/*
 * Class to play with SPVIEW
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.print.PageFormat;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;
import java.util.prefs.Preferences;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;
import java.awt.Toolkit;

import org.spview.filehandler.ExpFile;
import org.spview.filehandler.DataFile;
import org.spview.filehandler.PredFile;
import org.spview.filehandler.SPVProject;
import org.spview.residuals.ObsCalc;

/////////////////////////////////////////////////////////////////////

/**
 * This is the master window.
 */
public class JobPlay extends JFrame implements ActionListener, Runnable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -8418036777163263772L;
	private SPVProject project = null;
	private final String workd; // working directory
	private File lastProjectPath; // last Project path
	private String threadType; // dataread, expread, predread or print
	
	RecentItems openRecent;

	// menus
	private JMenuItem jmiexit; // Exit of FILE menu
	//
	private JMenuItem jmiloadspect; // Load spectrum of DATA menu
	private JMenuItem jmiloadstick; // Load stick of DATA menu
	private JMenuItem jmiloadpeakas; // Load peak/experiment as stick of DATA menu
	private JMenuItem jmiloadtdsas; // Load pred-as-stick (TDS) of DATA menu
	private JMenuItem jmiloadhitras; // Load pred-as-stick (HITRAN) of DATA menu
	private JMenu jmiopenrecents;
	//
	private JMenu jmdataman; // DataManagement
	private JMenuItem jmiresetall; // Restore all in basic state of DATAMANAGEMENT menu
	private final JMenu[] jmndataf; // data file names
	private final JMenuItem[] jmishow; // Show of DATAMANAGEMENT menu
	private final JMenuItem[] jmihide; // Hide of DATAMANAGEMENT menu
	private final JMenuItem[] jmireload; // Reload of DATAMANAGEMENT menu
	private final JMenuItem[] jmixshift; // X shift of DATAMANAGEMENT menu
	private final JMenuItem[] jmixunshift; // X unshift of DATAMANAGEMENT menu
	private final JMenuItem[] jmiyshift; // Y shift of DATAMANAGEMENT menu
	private final JMenuItem[] jmiyunshift; // Y unshift of DATAMANAGEMENT menu
	private final JMenuItem[] jmiyreverse; // Y reverse of DATAMANAGEMENT menu
	//
	private JMenu jmcreatepeakf; // Create peak file of ASSIGNMENT menu
	private JMenuItem jmicpfnone; // no default spectrum file name
	private final JMenuItem[] jmicpf; // data file names
	private JMenuItem jmiloadexpf; // Load experiment file of ASSIGNMENT menu
	private JMenu jmloadpredf; // Load prediction file of ASSIGNMENT menu
	private JMenuItem jmilpTDS; // Load prediction file (TDS) of ASSIGNMENT menu
	private JMenuItem jmilpHITRAN; // Load prediction file (HITRAN) of ASSIGNMENT menu
	private JMenu jmexp2data; // Associate experiment to data of ASSIGNMENT menu
	private JMenuItem jmiexp2null; // Associate experiment to null of ASSIGNMENT menu
	private final JMenuItem[] jmiexp2; // data file names
	private JMenu jmpred2data; // Associate prediction to data of ASSIGNMENT menu
	private JMenuItem jmipred2null; // Associate prediction to null of ASSIGNMENT menu
	private final JMenuItem[] jmipred2; // data file names
	private JMenuItem jmisavexp; // Update experiment file of ASSIGNMENT menu
	//
	private JMenuItem jmiprint; // Print of PLOT menu
	//
	private JMenuItem jmiabout; // About SPVIEW of HELP menu

	private final JPanel pccc;

	private final JButton jbyplus; // Y +
	private final JButton jbyegal; // Y =
	private final JButton jbymoins; // Y -
	private final JButton jbxpipeinf; // X |<
	private final JButton jbx2inf; // X <<
	private final JButton jbx1inf; // X <
	private final JButton jbxegal; // X =
	private final JButton jbx1sup; // X >
	private final JButton jbx2sup; // X >>
	private final JButton jbxpipesup; // X >|

	private final JTextArea jta; // show selected exp/pred

	// labels to show mouse position
	private final JLabel jlx; // X mouse
	private final JLabel jly; // Y mouse
	private final JLabel jlypredas; // Y mouse in pred-as-stick scale
	private JLabel cjl; // value

	// drawing window
	private PanAff panaff;
	private final Color[] coul = { Color.BLUE, Color.RED, Color.GRAY, Color.GREEN, Color.MAGENTA, // colors choice
			Color.ORANGE, Color.BLACK, Color.PINK, Color.CYAN, Color.YELLOW };
	private final float coulength; // size of color array

	private File lastUsedPath;

	// data files
	private DataFile[] dataf; // data file
	private String[] ndataf; // data file name
	private int nbdf; // nb of data files
	private final int mxnbdf = 20; // max nb of data files
	private int relfi; // reloadable file index
	// experiment file
	private ExpFile expf; // experiment file
	private String nexpf; // name of experiment file
	// prediction file
	private PredFile predf; // prediction file
	private String npredf; // name of prediction file

	private final String lnsep; // line separator
	private JMenuItem jmiopenproject;
	private JMenuItem jmisaveasproject;
	private JMenuItem jmisaveproject;
	private String spvFileName = null; // path file name

	private final ArrayList<String> pathListToSave = new ArrayList<>(); // List of paths of the project to save
	private final ArrayList<String> typeOfFileToSave = new ArrayList<>(); // Contains all attribute-types of DataFiles in order to save
	private final ArrayList<String> AttributeOfFileToSave = new ArrayList<>(); // Contains attribute "dataread" to
																		// differentiate them from predread or expread

	// Prediction files
	private String typeOfPredFile; // Contains the type of the PrefFile loaded
	private String attributeOfPredFile; // Contains "predread", if one has been loaded
	private String pathOfPredfileToSave; // Contains the path of the prediction file loaded
	private int PredfileToSave = 0; // Indicates if there is a prediction file to save or not

	// Experimental files
	private String typeOfExpFile; // Contains the type of the ExpFile loaded
	private String attributeOfExpFile; // Contains "expread", if one has been loaded
	private String pathOfExpfileToSave; // Contains the path of the experimental file loaded
	private int ExpfileToSave = 0; // Indicates if there is an experimental file to save or not

	// Y reverse
	private final ArrayList<String> yReverseElementToSave = new ArrayList<>(); // Contains information about "y-reverse" state
	// hide
	private final ArrayList<String> hideElementToSave = new ArrayList<>();
	private JMenuItem jmixobscalc;
	private JMenuItem jmiyobscalc;
	private JMenuItem jmilpreload;
	private final JLabel expFileLabel;
	private JMenuItem jmicloseproject;
	private JSeparator datamanSeparator;

	/////////////////////////////////////////////////////////////////////

	/**
	 * Construct a new JobPlay.
	 */
	public JobPlay() {

		super("Managing  SPVIEW"); // main window
		setIconImage(Toolkit.getDefaultToolkit().getImage(JobPlay.class.getResource("/pixmaps/spview.png")));
		setName("JobPlay"); // for help files
		setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

		addWindowListener(new WindowAdapter() { // clean end
			public void windowClosing(WindowEvent e) {
				closeApplication();
			}
		});
		new JavaAwtDesktop();

		workd = System.getProperty("user.dir"); // working directory
		lnsep = System.getProperty("line.separator");
		// Monospaced 14 font
		Font mono14 = new Font("Monospaced", Font.PLAIN, 14);
		// menu bar
		buildMenu(); // build menu bar

		getContentPane().setLayout(new BorderLayout());

		// center panel for drawing
		// panels
		JPanel pcentre = new JPanel(new BorderLayout());
		JPanel pco = new JPanel(new BorderLayout());
		JPanel pcc = new JPanel(new BorderLayout());
		JPanel pccs = new JPanel(new GridLayout(2, 0, 5, 5));
		pccc = new JPanel(new BorderLayout());
		pcc.add(pccc, "Center");
		pcc.add(pccs, "South");
		pcentre.add(pco, "West");
		pcentre.add(pcc, "Center");
		// Y buttons
		// boxes and buttons
		// box for Y buttons
		Box boxy = Box.createVerticalBox();
		jbyplus = new JButton("");
		jbyplus.setContentAreaFilled(false);
		jbyplus.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbyplus.setBorder(null);
		jbyplus.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/up.png"))));
		jbyplus.setFont(new Font("Monospaced", Font.BOLD, 12));
		jbyplus.setToolTipText("Shifts drawing one interval down along Y axis");
		jbyplus.addActionListener(this);
		jbyegal = new JButton("");
		jbyegal.setContentAreaFilled(false);
		jbyegal.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbyegal.setBorder(null);
		jbyegal.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/equal.png"))));
		jbyegal.setFont(new Font("Monospaced", Font.BOLD, 12));
		jbyegal.setToolTipText("Centers drawing along Y axis");
		jbyegal.addActionListener(this);
		jbymoins = new JButton("");
		jbymoins.setContentAreaFilled(false);
		jbymoins.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbymoins.setBorder(null);
		jbymoins.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/bottom.png"))));
		jbymoins.setFont(new Font("Monospaced", Font.BOLD, 12));
		jbymoins.setToolTipText("Shifts drawing one interval up along Y axis");
		jbymoins.addActionListener(this);
		boxy.add(Box.createVerticalGlue());
		boxy.add(jbyplus);
		boxy.add(Box.createVerticalStrut(50));
		boxy.add(jbyegal);
		boxy.add(Box.createVerticalStrut(50));
		boxy.add(jbymoins);
		boxy.add(Box.createVerticalGlue());
		pco.add(boxy);
		// mouse position
		jlx = new JLabel();
		jlx.setFont(new Font("Monospaced", Font.PLAIN, 14));
		jly = new JLabel();
		jly.setFont(new Font("Monospaced", Font.PLAIN, 14));
		jlypredas = new JLabel();
		jlypredas.setFont(new Font("Monospaced", Font.PLAIN, 14));
		JPanel pcs = new JPanel(new GridLayout(0, 7, 5, 5));
		pcs.setBackground(Color.WHITE);
		cjl = new JLabel("x =", null, JLabel.RIGHT);
		cjl.setFont(new Font("Monospaced", Font.PLAIN, 14));
		pcs.add(cjl);
		pcs.add(jlx);
		cjl = new JLabel("y =", null, JLabel.RIGHT);
		cjl.setFont(new Font("Monospaced", Font.PLAIN, 14));
		pcs.add(cjl);
		pcs.add(jly);
		cjl = new JLabel("", null, JLabel.RIGHT);
		cjl.setFont(new Font("Monospaced", Font.PLAIN, 14));
		pcs.add(cjl);
		pcs.add(jlypredas);
		pcs.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		pccs.add(pcs);
		// X buttons
		// box for X buttons
		Box boxx = Box.createHorizontalBox();
		jbxpipeinf = new JButton("");
		jbxpipeinf.setContentAreaFilled(false);
		jbxpipeinf.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbxpipeinf.setBorder(null);
		jbxpipeinf.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/beg.png"))));
		jbxpipeinf.setToolTipText("Shifts drawing at the beginning of the X axis");
		jbxpipeinf.addActionListener(this);
		jbx2inf = new JButton("");
		jbx2inf.setContentAreaFilled(false);
		jbx2inf.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbx2inf.setBorder(null);
		jbx2inf.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/backward.png"))));
		jbx2inf.setToolTipText("Shifts drawing one screen right along X axis");
		jbx2inf.addActionListener(this);
		jbx1inf = new JButton("");
		jbx1inf.setContentAreaFilled(false);
		jbx1inf.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbx1inf.setBorder(null);
		jbx1inf.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/playback.png"))));
		jbx1inf.setToolTipText("Shifts drawing one interval right along X axis");
		jbx1inf.addActionListener(this);
		jbxegal = new JButton("");
		jbxegal.setContentAreaFilled(false);
		jbxegal.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbxegal.setBorder(null);
		jbxegal.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/pause.png"))));
		jbxegal.setToolTipText("Centers drawing along X axis");
		jbxegal.addActionListener(this);
		jbx1sup = new JButton("");
		jbx1sup.setContentAreaFilled(false);
		jbx1sup.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbx1sup.setBorder(null);
		jbx1sup.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/play.png"))));
		jbx1sup.setToolTipText("Shifts drawing one interval left along X axis");
		jbx1sup.addActionListener(this);
		jbx2sup = new JButton("");
		jbx2sup.setContentAreaFilled(false);
		jbx2sup.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbx2sup.setBorder(null);
		jbx2sup.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/forward.png"))));
		jbx2sup.setToolTipText("Shifts drawing one screen left along X axis");
		jbx2sup.addActionListener(this);
		jbxpipesup = new JButton("");
		jbxpipesup.setContentAreaFilled(false);
		jbxpipesup.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		jbxpipesup.setBorder(null);
		jbxpipesup.setIcon(new ImageIcon(Objects.requireNonNull(JobPlay.class.getResource("/pixmaps/end.png"))));
		jbxpipesup.setToolTipText("Shifts drawing at the end of the X axis");
		jbxpipesup.addActionListener(this);
		boxx.add(Box.createHorizontalGlue());
		boxx.add(jbxpipeinf);
		boxx.add(Box.createHorizontalStrut(25));
		boxx.add(jbx2inf);
		boxx.add(Box.createHorizontalStrut(25));
		boxx.add(jbx1inf);
		boxx.add(Box.createHorizontalStrut(25));
		boxx.add(jbxegal);
		boxx.add(Box.createHorizontalStrut(25));
		boxx.add(jbx1sup);
		boxx.add(Box.createHorizontalStrut(25));
		boxx.add(jbx2sup);
		boxx.add(Box.createHorizontalStrut(25));
		boxx.add(jbxpipesup);
		boxx.add(Box.createHorizontalStrut(25));
		boxx.add(Box.createHorizontalGlue());
		pccs.add(boxx);
		// TextArea for selected exp/pred characteristics
		jta = new JTextArea();
		panaff = new PanAff(this, mxnbdf, coul, jta, jlx, jly, jlypredas);
		
		expFileLabel = new JLabel("", null, SwingConstants.TRAILING);
		expFileLabel.setFont(new Font("Monospaced", Font.PLAIN, 14));
		pcs.add(expFileLabel);
		panaff.setBackground(Color.WHITE);
		pccc.add(panaff, "Center");
		pccc.setBorder(BorderFactory.createLineBorder(Color.BLACK));

		// south panel contains TextArea
		JPanel psud = new JPanel(new GridLayout(1, 0, 5, 5));
		jta.setBackground(Color.BLACK);
		jta.setForeground(Color.WHITE);
		jta.setEditable(false); // not editable
		jta.setFont(mono14);
		jta.setLineWrap(false); // no line wrap
		jta.setRows(8); // nb of lines
		// with lifts
		JScrollPane jsp = new JScrollPane(jta); // with lifts
		psud.add(jsp);

		// insert panels
		getContentPane().add(pcentre, "Center");
		getContentPane().add(psud, "South");
		pack();

		// initialisation
		dataf = new DataFile[mxnbdf]; // data file
		ndataf = new String[mxnbdf]; // name of data file
		nbdf = 0; // nb of data files
		jmndataf = new JMenu[mxnbdf]; // data file menus
		jmishow = new JMenuItem[mxnbdf]; // show
		jmihide = new JMenuItem[mxnbdf]; // hide
		jmireload = new JMenuItem[mxnbdf]; // reload
		jmixshift = new JMenuItem[mxnbdf]; // X shift
		jmixunshift = new JMenuItem[mxnbdf]; // X unshift
		jmiyshift = new JMenuItem[mxnbdf]; // Y shift
		jmiyunshift = new JMenuItem[mxnbdf]; // Y unshift
		jmiyreverse = new JMenuItem[mxnbdf]; // Y reverse
		jmicpf = new JMenuItem[mxnbdf]; // data file names
		jmiexp2 = new JMenuItem[mxnbdf]; // exp associated to data
		jmipred2 = new JMenuItem[mxnbdf]; // pred associated to data
		coulength = coul.length; // size of color array
	}

/////////////////////////////////////////////////////////////////////
	
	private void updateOpenRecentMenu(RecentItems recentItems) {
		jmiopenrecents.removeAll();
		
		for (int i = 0; i < recentItems.size(); i++) {
			String item = recentItems.get(i);
			JMenuItem recentItem = new JMenuItem(item);
			recentItem.addActionListener(e -> loadProjectFileName(item));
			jmiopenrecents.add(recentItem);
		}
	}

	// build the menu bar
	private void buildMenu() {

		// menu bar
		JMenuBar jmb = new JMenuBar();
		// FILE
		JMenu jmfile = new JMenu("File");
		// DATA
		JMenu jmload = new JMenu("Data");
		jmiloadspect = new JMenuItem("Load spectrum file");
		jmiloadspect.addActionListener(this);
		jmiloadstick = new JMenuItem("Load stick file");
		jmiloadstick.addActionListener(this);
		jmiloadpeakas = new JMenuItem("Load (as stick) peak/experiment file");
		jmiloadpeakas.addActionListener(this);
		// Load pred-as-stick of DATA menu
		JMenu jmloadpredas = new JMenu("Load (as stick) prediction file");
		jmiloadtdsas = new JMenuItem("TDS format");
		jmiloadtdsas.setEnabled(false);
		jmiloadtdsas.addActionListener(this);
		jmloadpredas.add(jmiloadtdsas);
		jmiloadhitras = new JMenuItem("HITRAN format");
		jmiloadhitras.setEnabled(false);
		jmiloadhitras.addActionListener(this);
		jmloadpredas.add(jmiloadhitras);
		jmload.add(jmiloadspect);
		jmload.addSeparator();
		jmload.add(jmiloadstick);
		jmload.add(jmiloadpeakas);
		jmload.addSeparator();
		jmload.add(jmloadpredas);
		// DATAMANAGEMENT
		jmdataman = new JMenu("DataManagement");
		jmiresetall = new JMenuItem("Restore all in basic state");
		jmiresetall.addActionListener(this);
		jmdataman.add(jmiresetall);
		
		// ASSIGNMENT
		JMenu jmassign = new JMenu("Assignment");
		jmcreatepeakf = new JMenu("Create peak file from spectrum file");
		jmicpfnone = new JMenuItem("Unknown default spectrum file name");
		jmicpfnone.addActionListener(this);
		jmcreatepeakf.add(jmicpfnone);
		jmiloadexpf = new JMenuItem("Load peak/experiment file");
		jmiloadexpf.setEnabled(false);
		jmiloadexpf.addActionListener(this);
		jmloadpredf = new JMenu("Load prediction file");
		jmloadpredf.setEnabled(false);
		jmilpTDS = new JMenuItem("TDS format");
		jmilpTDS.addActionListener(this);
		jmloadpredf.add(jmilpTDS);
		jmilpHITRAN = new JMenuItem("HITRAN format");
		jmilpHITRAN.addActionListener(this);
		jmloadpredf.add(jmilpHITRAN);
		jmexp2data = new JMenu("Associate experiment to data");
		jmexp2data.setEnabled(false);
		jmiexp2null = new JMenuItem("null");
		jmiexp2null.addActionListener(this);
		jmexp2data.add(jmiexp2null);
		jmpred2data = new JMenu("Associate prediction to data");
		jmpred2data.setEnabled(false);
		jmipred2null = new JMenuItem("null");
		jmipred2null.addActionListener(this);
		jmpred2data.add(jmipred2null);
		jmisavexp = new JMenuItem("Save assignment file as...");
		jmisavexp.setEnabled(false);
		jmisavexp.addActionListener(this);
		jmassign.add(jmcreatepeakf);
		jmassign.addSeparator();
		jmassign.add(jmiloadexpf);
		jmassign.add(jmloadpredf);
		
		jmilpreload = new JMenuItem("Reload");
		jmilpreload.addActionListener(this);
		
		jmloadpredf.addSeparator();
		jmloadpredf.add(jmilpreload);
		jmassign.addSeparator();
		jmassign.add(jmexp2data);
		jmassign.add(jmpred2data);
		jmassign.addSeparator();
		jmassign.add(jmisavexp);
		// PLOT
		JMenu jmplot = new JMenu("Plot");
		jmiprint = new JMenuItem("Print");
		jmiprint.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));
		jmiprint.addActionListener(this);
		
		jmixobscalc = new JMenuItem("X Obs.-Calc.");
		jmplot.add(jmixobscalc);
		jmixobscalc.addActionListener(this);

		
		jmiyobscalc = new JMenuItem("Y Obs.-Calc.");
		jmplot.add(jmiyobscalc);
		jmiyobscalc.addActionListener(this);

		jmplot.addSeparator();
		jmplot.add(jmiprint);
		// HELP
		JMenu jmhelp = new JMenu("Help");
		jmiabout = new JMenuItem("About SPVIEW");
		jmiabout.addActionListener(this);
		jmhelp.add(jmiabout);
		if (System.getProperty("os.name").contains("OS X")){
			jmiabout.setVisible(false);
		}
		jmb.add(jmfile);

		// FILE
		jmiopenproject = new JMenuItem("Open Project...");
		jmiopenproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));
		jmiopenproject.addActionListener(this);
		jmfile.add(jmiopenproject);
		
		jmiopenrecents = new JMenu("Open Recent");
		Preferences prefs = Preferences.userRoot().node(JobPlay.class.getSimpleName());
		int maxrecent = 10;
		openRecent = new RecentItems(maxrecent, prefs);
		updateOpenRecentMenu(openRecent);

		jmfile.add(jmiopenrecents);

		jmfile.addSeparator();
		jmicloseproject = new JMenuItem("Close Project");
		jmicloseproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));
		jmicloseproject.addActionListener(this);
		jmfile.add(jmicloseproject);

		jmfile.addSeparator();
		jmisaveasproject = new JMenuItem("Save Project As...");
		jmisaveasproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | java.awt.event.InputEvent.SHIFT_DOWN_MASK));
		jmisaveasproject.addActionListener(this);
		jmfile.add(jmisaveasproject);
		jmisaveproject = new JMenuItem("Save");
		jmisaveproject.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));
		jmisaveproject.addActionListener(this);
		jmfile.add(jmisaveproject);

		jmfile.addSeparator();
		jmiexit = new JMenuItem("Exit");
		jmiexit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()));
		jmiexit.addActionListener(this);
		jmfile.add(jmiexit);
		if (System.getProperty("os.name").contains("OS X")){
			jmiexit.setVisible(false);
		}
		
		jmb.add(jmload);
		jmb.add(jmdataman);
		jmb.add(jmassign);
		jmb.add(jmplot);
		jmb.add(jmhelp);
		setJMenuBar(jmb); // set menu bar
	}
	
	private boolean checkForSave() {
		boolean fileExist = true;
		if (nbdf == 0) return true;
		if (project == null) {
			project = new SPVProject(this);
			spvFileName = workd;
			project.setUnsave(true);
			fileExist = false;
		}
		if (project.isUnsave()) {
			int n = JOptionPane.showConfirmDialog(null,
					"The current project is in a unsaved state, Do you want to save it?", "Project file is unsaved",
					JOptionPane.YES_NO_CANCEL_OPTION);
			if (n == JOptionPane.YES_OPTION) {
				project.saveSPVFile(spvFileName, fileExist);
			} else return n != JOptionPane.CANCEL_OPTION;
		}
		return true;
	}

/////////////////////////////////////////////////////////////////////

	/**
	 * Process events.
	 */
	public void actionPerformed(ActionEvent evt) {

		// FILE
		// -> Exit
		if (evt.getSource() == jmiexit) {
			closeApplication();
			return;
		}

		// NO action while printing
		if (panaff.getPrinting()) {
			JOptionPane.showMessageDialog(null, "No action while printing");
			return;
		}

		// BUTTONS
		// Y +
		if (evt.getSource() == jbyplus) {
			panaff.jbyplus();
			setUnsave(true);
			return;
		}

		// Y =
		if (evt.getSource() == jbyegal) {
			panaff.jbyegal();
			setUnsave(true);
			return;
		}

		// Y -
		if (evt.getSource() == jbymoins) {
			panaff.jbymoins();
			setUnsave(true);
			return;
		}

		// X |<
		if (evt.getSource() == jbxpipeinf) {
			panaff.jbxpipeinf();
			setUnsave(true);
			return;
		}

		// X <<
		if (evt.getSource() == jbx2inf) {
			panaff.jbx2inf();
			setUnsave(true);
			return;
		}

		// X <
		if (evt.getSource() == jbx1inf) {
			panaff.jbx1inf();
			setUnsave(true);
			return;
		}

		// X =
		if (evt.getSource() == jbxegal) {
			panaff.jbxegal();
			setUnsave(true);
			return;
		}

		// X >
		if (evt.getSource() == jbx1sup) {
			panaff.jbx1sup();
			setUnsave(true);
			return;
		}

		// X >>
		if (evt.getSource() == jbx2sup) {
			panaff.jbx2sup();
			setUnsave(true);
			return;
		}

		// X >|
		if (evt.getSource() == jbxpipesup) {
			panaff.jbxpipesup();
			setUnsave(true);
			return;
		}

		// all following actions reset pred selection in PanAff
		panaff.endSelPred();

		// PROJECT SPVIEW 2.0
		// -> Load project files

		if (evt.getSource() == jmiopenproject) {
			loadProjectFile();
			return;
		}
		
		// -> Close project

		if (evt.getSource() == jmicloseproject) {
			if (checkForSave()) {
				spvFileName = null;
				closeAll();
			}
			return;

		}

		// -> Save project files

		if (evt.getSource() == jmisaveasproject) {
			if (spvFileName == null) {
				project = new SPVProject(this);
				spvFileName = workd;
			}
			project.saveSPVFile(spvFileName, false);

			return;

		}
		
		if (evt.getSource() == jmisaveproject) {
			if (spvFileName == null) {
				project = new SPVProject(this);
				spvFileName = workd;
				// if save is cancelled or failed, no new project is created
				if (!project.saveSPVFile(spvFileName, false)) {
					spvFileName = null;
				}
			} else {
				project.saveSPVFile(spvFileName, true);
			}

			return;

		}

		// DATA
		// -> Load spectrum file
		if (evt.getSource() == jmiloadspect) {
			loadDataFile("spectrum");
			return;
		}
		// -> Load stick file
		if (evt.getSource() == jmiloadstick) {
			loadDataFile("stick");
			return;
		}
		// -> Load peak/experiment file as stick
		if (evt.getSource() == jmiloadpeakas) {
			loadDataFile("peakas");
			return;
		}
		// -> Load prediction file (TDS) as stick
		if (evt.getSource() == jmiloadtdsas) {
			loadDataFile("tdsas");
			return;
		}
		// -> Load prediction file (HITRAN) as stick
		if (evt.getSource() == jmiloadhitras) {
			loadDataFile("hitras");
			return;
		}

		// DATAMANAGEMENT
		// -> Reset All
		if (evt.getSource() == jmiresetall) {
			if (nbdf > 0) {
				panaff.resetAll(); // call panaff
				for (int i = 0; i < nbdf; i++) {
					resetDatafJMI(i); // reset jmi of this dataf (like show)
				}
			}
			setUnsave(true);
			return;
		}
		// -> data files
		if (nbdf > 0) {
			for (int i = 0; i < nbdf; i++) {
				// -> Show
				if (evt.getSource() == jmishow[i]) {
					panaff.setShow(i, true); // call panaff
					resetDatafJMI(i); // reset jmi of this dataf
					setHideState(i, false);
					setUnsave(true);
					return;
				}
				// -> Hide
				if (evt.getSource() == jmihide[i]) {
					jmishow[i].setEnabled(true);
					// disallow jmi of this dataf
					jmihide[i].setEnabled(false);
					if (dataf[i].getType().equals("spectrum")) {
						jmireload[i].setEnabled(false);
					}
					jmixshift[i].setEnabled(false);
					jmixunshift[i].setEnabled(false);
					jmiyshift[i].setEnabled(false);
					jmiyunshift[i].setEnabled(false);
					jmiyreverse[i].setEnabled(false);
					panaff.setShow(i, false); // call panaff
					setHideState(i, true);
					setUnsave(true);
					return;
				}
				// -> Reload
				if (evt.getSource() == jmireload[i]) {
					reloadDataFile(i);
					return;
				}
				// -> X shift
				if (evt.getSource() == jmixshift[i]) {
					panaff.xShift(i); // call panaff
					jmixunshift[i].setEnabled(true);
					setUnsave(true);
					return;
				}
				// -> X unshift
				if (evt.getSource() == jmixunshift[i]) {
					panaff.xUnshift(i); // call panaff
					jmixunshift[i].setEnabled(false);
					setUnsave(true);
					return;
				}
				// -> Y shift
				if (evt.getSource() == jmiyshift[i]) {
					panaff.yShift(i); // call panaff
					jmiyunshift[i].setEnabled(true);
					setUnsave(true);
					return;
				}
				// -> Y unshift
				if (evt.getSource() == jmiyunshift[i]) {
					panaff.yUnshift(i); // call panaff
					jmiyunshift[i].setEnabled(false);
					setUnsave(true);
					return;
				}
				// -> Y reverse
				if (evt.getSource() == jmiyreverse[i]) {
					panaff.yReverse(i); // call panaff
					setyReverseState(i);
					setUnsave(true);
				}
			}
		}

		// ASSIGNMENT
		// -> Create peak file from spectrum
		if (evt.getSource() == jmicpfnone) {
			// Unknown default spectrum file name
			JFCreatePeakFile jfcpf = new JFCreatePeakFile(null); // create the window
			jfcpf.setVisible(true); // show it
			return;
		}
		for (int i = 0; i < nbdf; i++) { // search in spectrum type data files
			if (evt.getSource() == jmicpf[i]) { // found
				JFCreatePeakFile jfcpf = new JFCreatePeakFile(dataf[i]); // create the window
				jfcpf.setVisible(true); // show it
				return;
			}
		}
		// -> Load peak/experiment file
		if (evt.getSource() == jmiloadexpf) {
			loadExpFile();
			typeOfExpFile = "peakExp";
			attributeOfExpFile = "expread";
			return;
		}
		// -> Load prediction file (TDS)
		if (evt.getSource() == jmilpTDS) {
			loadPredFile("TDS");
			typeOfPredFile = "TDS";
			return;
		}
		// -> Load prediction file (HITRAN)
		if (evt.getSource() == jmilpHITRAN) {
			loadPredFile("HITRAN");
			typeOfPredFile = "HITRAN";
			return;
		}
		// -> Reload prediction file
		if (evt.getSource() == jmilpreload) {
			reloadPredFile();
			return;
		}
		// -> Associate experiment to data
		if (evt.getSource() == jmiexp2null) {
			panaff.expAss2(-1); // unassociate
			setUnsave(true);
			return;
		}
		String str;
		for (int i = 0; i < nbdf; i++) { // for each datafile
			if (evt.getSource() == jmiexp2[i]) { // found
				str = panaff.expAss2(i); // associate
				if (str.length() != 0) {
					// show error message
					JOptionPane.showMessageDialog(null, str);
				}
				setUnsave(true);
				return;
			}
		}
		// -> Associate prediction to data
		if (evt.getSource() == jmipred2null) {
			panaff.predAss2(-1); // unassociate
			setUnsave(true);
			return;
		}
		for (int i = 0; i < nbdf; i++) { // for each data file
			if (evt.getSource() == jmipred2[i]) { // found
				str = panaff.predAss2(i); // associate
				if (str.length() != 0) {
					// show error message
					JOptionPane.showMessageDialog(null, str);
				}
				setUnsave(true);
				return;
			}
		}

		// -> Save experiment file
		if (evt.getSource() == jmisavexp) {
			panaff.saveExp(); // call panaff
			return;
		}

		// PLOT
		// -> Print
		if (evt.getSource() == jmiprint) {
			// thread
			panaff.prepThread("Printing File ..."); // wait message
			threadType = "print"; // define action of run
			Thread threadThis = new Thread(this); // define the thread (run of this instance)
			threadThis.start(); // start it
			return;
		}

		if (evt.getSource() == jmixobscalc) {
			try {
				ObsCalc obsCalc = new ObsCalc(expf, predf);
				obsCalc.show(this, "X");
			} catch (Exception e) {
				JOptionPane.showMessageDialog(null, "You must first load prediction and peak/experiment files.");
			}
		}
		
		if (evt.getSource() == jmiyobscalc) {
			try {
				ObsCalc obsCalc = new ObsCalc(expf, predf);
				obsCalc.show(this, "Y");
			} catch (Exception e) {
				JOptionPane.showMessageDialog(null, "You must first load prediction and peak/experiment files.");
			}
		}

		// HELP
		// -> About
		if (evt.getSource() == jmiabout) {
			About.show();
		}
	}

/////////////////////////////////////////////////////////////////////

	// reset JMenuItem of a data file
	private void resetDatafJMI(int ci) {

		jmishow[ci].setEnabled(false);
		jmihide[ci].setEnabled(true);
		if (dataf[ci].getType().equals("spectrum")) {
			jmireload[ci].setEnabled(true);
		}
		jmixshift[ci].setEnabled(true);
		jmixunshift[ci].setEnabled(panaff.getXShift(ci) != 0.);
		jmiyshift[ci].setEnabled(true);
		// NO yshift
		jmiyunshift[ci].setEnabled(panaff.getYShift(ci) != 0.);
		jmiyreverse[ci].setEnabled(true);
	}
	
	private void closeAll() {
		if (nbdf == 0)
			return;
		
		dataf = new DataFile[mxnbdf];
		ndataf = new String[mxnbdf];
		
		pccc.remove(panaff);
		panaff = new PanAff(this, mxnbdf, coul, jta, jlx, jly, jlypredas);
		panaff.setBackground(Color.WHITE);
		pccc.add(panaff, "Center");
		pccc.setBorder(BorderFactory.createLineBorder(Color.BLACK));
		pccc.repaint();

		project = null;
		pathListToSave.clear();
		typeOfFileToSave.clear();
		AttributeOfFileToSave.clear();
		yReverseElementToSave.clear();
		hideElementToSave.clear();
		PredfileToSave = 0;
		ExpfileToSave = 0;
		expFileLabel.setText("");

		predf = null;
		expf = null;
		
		jmdataman.remove(datamanSeparator);
		for (int i = 0; i < nbdf; i++) {
			jmdataman.remove(jmndataf[i]);
			jmcreatepeakf.remove(jmicpf[i]);
			jmexp2data.remove(jmiexp2[i]);
			jmpred2data.remove(jmipred2[i]);
		}
		
		jmiloadexpf.setEnabled(false);
		jmloadpredf.setEnabled(false);
		jmexp2data.setEnabled(false);
		jmpred2data.setEnabled(false);
		jmisavexp.setEnabled(false);
		
		nbdf = 0;
	}

	
////////////////////////////////////////////////////////////
	/**
	 * Load files by file name
	 *
	 * @param cntfile file type
	 * @since SPVIEW2
	 */
	public void loadFileByFilename(String filename, String cntfile, int i) {
		// -> Show
		// instantiate a data file
		String attribute = project.getAttributeOfFileList().get(i);

		switch (attribute) {
			case "dataread":
				ndataf[i] = filename;
				dataf[i] = new DataFile(ndataf[i]); // full data file name

				dataf[i].setType(cntfile); // type


				// thread
				panaff.prepThread("Loading File ..."); // wait message

				nbdf = i;
				threadType = "dataread"; // define action of run()

				this.run();
				break;
			case "predread":
				PredfileToSave = 1;
				pathOfPredfileToSave = npredf;

				// instantiate the prediction file
				npredf = filename;
				predf = new PredFile(npredf, cntfile); // full pred file name, type

				// Color associated
				panaff.setPfass2(project.getIndexColorPred());

				// thread
				panaff.prepThread("Loading File ..."); // wait message

				threadType = "predread"; // define action of run()

				this.run();
				break;
			case "expread":
				ExpfileToSave = 1;

				// instantiate the experimental file
				nexpf = filename;
				expf = new ExpFile(nexpf); // full exp file name

				pathOfExpfileToSave = nexpf;
				// Color associated
				panaff.setEfass2(project.getIndexColorExp());

				// thread
				panaff.prepThread("Loading File ..."); // wait message

				threadType = "expread"; // define action of run()

				this.run();
				break;
		}
	}

////////////////////////////////////////////////////////////

	/**
	 * Get the yReverse-state of each file loaded in order to save this state
	 * 
	 * @since SPVIEW2
	 */

	private void setyReverseState(int j) {
		if (j < yReverseElementToSave.size()) {
			String reverse = yReverseElementToSave.get(j);
			yReverseElementToSave.set(j, reverse.equals("true") ? "false" : "true");
		} else {
			yReverseElementToSave.add("true");
		}
	}
	
	/**
	 * Get the hide-state of each file loaded in order to save this state
	 * 
	 * @since SPVIEW2
	 */

	private void setHideState(int j, boolean hide) {
		if (j < hideElementToSave.size()) {
			String tag = hide ? "true" : "false";
			hideElementToSave.set(j, tag);
		} else {
			hideElementToSave.add("false");
		}
	}
	
	private void displayName(String filename) {
		File file = new File(filename);
		expFileLabel.setText(/*"Peak/experiment file: " + */file.getName());
	}
	
////////////////////////////////////////////////////////////
	
	public void addInOpenRecentList(String projectPath) {
		openRecent.push(projectPath);
		updateOpenRecentMenu(openRecent);
	}
	
	public void removeInOpenRecentList(String projectPath) {
		openRecent.remove(projectPath);
		updateOpenRecentMenu(openRecent);
	}
	
	public void loadProjectFileName(String filename) {
		spvFileName = filename;
		panaff.prepThread("Loading Project File ..."); // wait message
		threadType = "project"; // define action of run()
		Thread threadThis = new Thread(this); // define thread (run of this instance)
		threadThis.start(); // start it
	}

	/**
	 * Open a dialog box to save the files as a project
	 * 
	 * @since SPVIEW2
	 */

	private void loadProjectFile() {
		JFileChooser jfcfile = new JFileChooser();
		if (lastProjectPath != null) {
			jfcfile.setCurrentDirectory(lastProjectPath);
		}

		jfcfile.setSize(400, 300);
		jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only
		jfcfile.setDialogTitle("Open Project");

		FileNameExtensionFilter filter = new FileNameExtensionFilter("Project file (*.spv)", "spv"); // Only files .spv
																										// can be chosen
		jfcfile.addChoosableFileFilter(filter); // add the filter created above
		jfcfile.setAcceptAllFileFilterUsed(false); // All types of file are NOT accepted

		Container parent = getParent();
		int choice = jfcfile.showOpenDialog(parent);
		if (choice == JFileChooser.APPROVE_OPTION) {
			String projectPath = jfcfile.getSelectedFile().getAbsolutePath();
			loadProjectFileName(projectPath);
			lastProjectPath = jfcfile.getSelectedFile().getParentFile();
		}
	}

////////////////////////////////////////////////////////////

	/**
	 * Load a data file.
	 *
	 * @param cntfile file type
	 */
	
	private void loadDataFile(String cntfile) {

		// max nb of file reached ?
		if (nbdf == mxnbdf) {
			JOptionPane.showMessageDialog(null, "Max number of data files is reached (" + mxnbdf + ")");
			return;
		}
		// choose the file
		JFileChooser jfcfile = new JFileChooser();
		if (lastUsedPath != null) {
			jfcfile.setCurrentDirectory(lastUsedPath);
		}
		jfcfile.setSize(400, 300);
		jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only
		jfcfile.setDialogTitle("Define the file to be visualized");
		Container parent = getParent();
		int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select
		if (choice == JFileChooser.APPROVE_OPTION) {
			// instantiate a data file
			lastUsedPath = jfcfile.getSelectedFile().getParentFile();
			ndataf[nbdf] = jfcfile.getSelectedFile().getAbsolutePath();
			dataf[nbdf] = new DataFile(ndataf[nbdf]); // full data file name
			dataf[nbdf].setType(cntfile); // type

			// thread
			panaff.prepThread("Loading File ..."); // wait message
			threadType = "dataread"; // define action of run()
			Thread threadThis = new Thread(this); // define thread (run of this instance)
			threadThis.start(); // start it
		}
	}

////////////////////////////////////////////////////////////

	/**
	 * Reload a data file.
	 *
	 * @param ci file index
	 */
	
	private void reloadDataFile(int ci) {

		// thread
		relfi = ci; // reloadable file index
		panaff.prepThread("Reloading File ..."); // wait message
		threadType = "datareload"; // define action of run()
		Thread threadThis = new Thread(this); // define thread (run of this instance)
		threadThis.start(); // start it
	}

////////////////////////////////////////////////////////////

	/**
	 * Reload a data file.
	 *
	 * */

	public void reloadPredFile() {

		// thread
		panaff.prepThread("Reloading File ..."); // wait message
		threadType = "predreload"; // define action of run()
		Thread threadThis = new Thread(this); // define thread (run of this instance)
		threadThis.start(); // start it
	}

////////////////////////////////////////////////////////////

	/**
	 * Load an experiment file.
	 */
	private void loadExpFile() {

		// a file is already loaded
		if (expf != null) {
			int n = JOptionPane.showConfirmDialog(null,
					"An experiment file is already loaded" + lnsep
							+ "Would you like to overload a new experiment file ?" + lnsep,
					"New experiment file", JOptionPane.YES_NO_OPTION);
			if (n != JOptionPane.YES_OPTION) {
				// nothing to do
				return;
			}
		}
		// choose the file
		JFileChooser jfcfile = new JFileChooser();
		if (lastUsedPath != null) {
			jfcfile.setCurrentDirectory(lastUsedPath);
		}
		jfcfile.setSize(400, 300);
		jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only
		jfcfile.setDialogTitle("Define the experiment file to be loaded");
		Container parent = getParent();
		int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select
		if (choice == JFileChooser.APPROVE_OPTION) {
			// instantiate an exp file
			lastUsedPath = jfcfile.getSelectedFile().getParentFile();
			nexpf = jfcfile.getSelectedFile().getAbsolutePath();
			expf = new ExpFile(nexpf); // full exp file name

			// thread
			panaff.prepThread("Loading File ..."); // wait message
			threadType = "expread"; // define action of run()
			Thread threadThis = new Thread(this); // define thread (run of this instance)
			threadThis.start(); // start it
		}
	}

	/**
	 * Load a prediction file.
	 *
	 * @param ctype file type
	 */
	private void loadPredFile(String ctype) {

		// a file is already loaded
		if (predf != null) {
			int n = JOptionPane.showConfirmDialog(
					null, "A prediction file is already loaded" + lnsep
							+ "Would you like to overload a new prediction file ?" + lnsep,
					"New prediction file", JOptionPane.YES_NO_OPTION);
			if (n != JOptionPane.YES_OPTION) {
				// nothing to do
				return;
			}
		}
		// choose the file
		JFileChooser jfcfile = new JFileChooser();
		if (lastUsedPath != null) {
			jfcfile.setCurrentDirectory(lastUsedPath);
		}
		jfcfile.setSize(400, 300);
		jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // files only
		jfcfile.setDialogTitle("Define the prediction file to be loaded");
		Container parent = getParent();
		int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select
		if (choice == JFileChooser.APPROVE_OPTION) {
			// instantiate a pred file
			npredf = jfcfile.getSelectedFile().getAbsolutePath();
			lastUsedPath = jfcfile.getSelectedFile().getParentFile();
			predf = new PredFile(npredf, ctype); // full pred file name, type
			
			// thread
			panaff.prepThread("Loading File ..."); // wait message
			threadType = "predread"; // define action of run()
			Thread threadThis = new Thread(this); // define thread (run of this instance)
			threadThis.start(); // start it
		}
	}

////////////////////////////////////////////////////////////

	/**
	 * Thread launched by start. <br>
	 * Processes read of data files <br>
	 * or read of experiment file <br>
	 * or read of prediction file <br>
	 * or reload of data files <br>
	 * or print of graphs.
	 */
	public void run() {

		// what to do
		switch (threadType) {
			case "dataread":
				// read data file
				if (dataf[nbdf].read()) { // read data file
					if (nbdf == 0) {
						jmiloadtdsas.setEnabled(true);
						jmiloadhitras.setEnabled(true);
					}
					typeOfFileToSave.add(dataf[nbdf].getType());
					// show data
					if (dataf[nbdf].getType().equals("tdsas") || dataf[nbdf].getType().equals("hitras")
							|| dataf[nbdf].getType().equals("pickettas")) {

						dataf[nbdf].setType("predas"); // generic type for pred-as-stick files
						jmndataf[nbdf] = new JMenu(ndataf[nbdf] + " (pred-as-stick)");
						if (cjl.getText().trim().length() == 0) {
							// not already set : 1st pred-as-stick file
							cjl.setText("pred-as-stick y ="); // last x/y/pasy label for mouse position
						}
						pathListToSave.add(ndataf[nbdf]); // add the path of the file loaded
						AttributeOfFileToSave.add("dataread");
						yReverseElementToSave.add("false"); // add the y-state : "false" by default
						hideElementToSave.add("false");
					} else if (dataf[nbdf].getType().equals("peakas")) {
						jmndataf[nbdf] = new JMenu(ndataf[nbdf] + " (peak-as-stick)");
						pathListToSave.add(ndataf[nbdf]); // add the path of the file loaded
						AttributeOfFileToSave.add("dataread");
						yReverseElementToSave.add("false"); // add the y-state : "false" by default
						hideElementToSave.add("false");
					} else { // spectrum/stick
						jmndataf[nbdf] = new JMenu(ndataf[nbdf] + " (" + dataf[nbdf].getType() + ")");
					}

					jmndataf[nbdf].setBackground(Color.WHITE);
					jmndataf[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]);
					jmishow[nbdf] = new JMenuItem("Show");
					jmishow[nbdf].setEnabled(false);
					jmishow[nbdf].addActionListener(this);
					jmndataf[nbdf].add(jmishow[nbdf]);
					jmihide[nbdf] = new JMenuItem("Hide");
					jmihide[nbdf].addActionListener(this);
					jmndataf[nbdf].add(jmihide[nbdf]);
					if (dataf[nbdf].getType().equals("spectrum")) {
						jmireload[nbdf] = new JMenuItem("Reload");
						jmireload[nbdf].addActionListener(this);
						jmndataf[nbdf].add(jmireload[nbdf]);
					}
					jmndataf[nbdf].addSeparator();
					jmixshift[nbdf] = new JMenuItem("X shift");
					jmixshift[nbdf].addActionListener(this);
					jmndataf[nbdf].add(jmixshift[nbdf]);
					jmixunshift[nbdf] = new JMenuItem("X unshift");
					jmixunshift[nbdf].setEnabled(false);
					jmixunshift[nbdf].addActionListener(this);
					jmndataf[nbdf].add(jmixunshift[nbdf]);
					jmndataf[nbdf].addSeparator();
					jmiyshift[nbdf] = new JMenuItem("Y shift");
					jmiyshift[nbdf].addActionListener(this);
					jmndataf[nbdf].add(jmiyshift[nbdf]);
					jmiyunshift[nbdf] = new JMenuItem("Y unshift");
					jmiyunshift[nbdf].setEnabled(false);
					jmiyunshift[nbdf].addActionListener(this);
					jmndataf[nbdf].add(jmiyunshift[nbdf]);
					jmndataf[nbdf].addSeparator();
					jmiyreverse[nbdf] = new JMenuItem("Y reverse");
					jmiyreverse[nbdf].addActionListener(this);
					jmndataf[nbdf].add(jmiyreverse[nbdf]);
					if (nbdf == 0) {
						datamanSeparator = new JSeparator();
						jmdataman.add(datamanSeparator);
					}
					jmdataman.add(jmndataf[nbdf]);
					//
					if (dataf[nbdf].getType().equals("spectrum") || dataf[nbdf].getType().equals("stick")) {
						// only spectrum type data file for jmcreatpeakf
						jmicpf[nbdf] = new JMenuItem(ndataf[nbdf]);
						jmicpf[nbdf].setBackground(Color.WHITE);
						jmicpf[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]);
						jmicpf[nbdf].addActionListener(this);
						jmcreatepeakf.add(jmicpf[nbdf]);

						pathListToSave.add(ndataf[nbdf]); // add the path of the file loaded
						AttributeOfFileToSave.add("dataread");
						yReverseElementToSave.add("false"); // add the y-state : "false" by default
						hideElementToSave.add("false");
					} else {
						jmicpf[nbdf] = new JMenuItem(); // instantiated but not activated
					}
					//
					jmiexp2[nbdf] = new JMenuItem(ndataf[nbdf]);
					jmiexp2[nbdf].setBackground(Color.WHITE);
					jmiexp2[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]);
					jmiexp2[nbdf].addActionListener(this);
					jmexp2data.add(jmiexp2[nbdf]);
					if (expf != null) {
						jmexp2data.setEnabled(true);
					}
					jmipred2[nbdf] = new JMenuItem(ndataf[nbdf]);
					jmipred2[nbdf].setBackground(Color.WHITE);
					jmipred2[nbdf].setForeground(coul[nbdf - ((int) coulength) * ((int) (nbdf / coulength))]);
					jmipred2[nbdf].addActionListener(this);
					jmpred2data.add(jmipred2[nbdf]);
					if (predf != null) {
						jmpred2data.setEnabled(true);
					}
					panaff.addData(dataf[nbdf]); // call panaff
					jmiloadexpf.setEnabled(true);
					jmloadpredf.setEnabled(true);
					nbdf++;
					setUnsave(true);
				} else {
					// read error
					dataf[nbdf] = null;
					ndataf[nbdf] = null;
				}
				break;
			case "expread":
				// read exp file
				if (expf.read()) {
					// show exp
					panaff.setExp(expf); // call panaff
					if (nbdf != 0) {
						jmexp2data.setEnabled(true);
					}
					jmisavexp.setEnabled(true);
					typeOfExpFile = "peakExp";
					pathOfExpfileToSave = nexpf;
					attributeOfExpFile = "expread";
					ExpfileToSave = 1;
					displayName(nexpf);
					setUnsave(true);
				} else {
					// read error
					expf = null;
				}
				break;
			case "predread":
				// read pred file
				if (predf.read()) {
					// show pred
					panaff.setPred(predf); // call panaff
					if (nbdf != 0) {
						jmpred2data.setEnabled(true);
						pathOfPredfileToSave = npredf;
						attributeOfPredFile = "predread";
						PredfileToSave = 1;
					}
					setUnsave(true);
				} else {
					// read error
					predf = null;
				}
				break;
			case "datareload":
				// reload data file
				if (dataf[relfi].read()) { // reload data file
					panaff.reloadData(dataf[relfi], relfi);
				}
				break;
			case "predreload":
				if (predf.read()) {
					/* update prediction file */
					panaff.setPred(predf);
					/* update shifts */
					panaff.reloadPredf(predf);
				}
				break;
			case "project":
				try {
					if ((project == null && nbdf > 0) || (project != null && project.isUnsave())) {
						int n = JOptionPane.showConfirmDialog(null,
								"The current project is in a unsaved state, Do you want to save it?",
								"Project file is unsaved", JOptionPane.YES_NO_CANCEL_OPTION);
						if (n == JOptionPane.YES_OPTION) {
							project.saveSPVFile(workd, false);
						} else if (n == JOptionPane.CANCEL_OPTION) {
							panaff.endThread();
							return;
						}
					}
					closeAll();
					project = new SPVProject(this, spvFileName);
					project.open();
					project.setUnsave(false);
				} catch (ParserConfigurationException | SAXException | IOException e) {
					JOptionPane.showMessageDialog(null, "Cannot Parse File " + spvFileName, "Error", JOptionPane.ERROR_MESSAGE);
					e.printStackTrace();
				}
				break;
			default:
				// print
				PrinterJob pjob = PrinterJob.getPrinterJob(); // define a printing job

				PageFormat pf = pjob.defaultPage(); // define page format

				pf.setOrientation(PageFormat.LANDSCAPE);
				pjob.setPrintable(panaff, pf); // associate to PanAff

				// if (pjob.printDialog(new HashPrintRequestAttributeSet())) { supprime
				// LANDSCAPE
				if (pjob.printDialog()) { // define printing
					try {
						pjob.print(); // print
					} catch (PrinterException pe) {
						JOptionPane.showMessageDialog(null, "Printer exception" + lnsep + pe);
					}
				}
				break;
		}
		panaff.endThread();
	}
	
	private void closeApplication() {
		if (checkForSave())
			System.exit(0);
	}

	/* getters and setters */
	
	public PanAff getPanAff() {
		return panaff;
	}
	
	public SPVProject getProject() {
		return project;
	}

	public ArrayList<String> getPathsList() {
		return pathListToSave;
	}
	
	public ArrayList<String> getAttributeOfFileList() {
		return AttributeOfFileToSave;
	}
	
	public ArrayList<String> getTypeOfFileList() {
		return typeOfFileToSave;
	}
	
	public ArrayList<String> getReverseYElementList() {
		return yReverseElementToSave;
	}
	
	public ArrayList<String> getHideElementList() {
		return hideElementToSave;
	}
	
	public String getPathOfPredFile() {
		return pathOfPredfileToSave;
	}
	
	public void setPathOfPredFile(String path) {
		this.pathOfPredfileToSave = path;
	}
	
	public String getAttributeOfPredFile() {
		return attributeOfPredFile;
	}
	
	public void setAttributefPredFile(String attribute) {
		this.attributeOfPredFile = attribute;
	}
	
	public String getTypeOfPredFile() {
		return typeOfPredFile;
	}
	
	public void setTypeOfPredFile(String type) {
		this.typeOfPredFile = type;
	}

	public String getPathOfExpfile() {
		return pathOfExpfileToSave;
	}
	
	public void setPathOfExpFile(String path) {
		pathOfExpfileToSave = path;
	}
	
	public void setTypeOfExpFile(String type) {
		this.typeOfExpFile = type;
	}

	public String getAttributeOfExpFile() {
		return attributeOfExpFile;
	}
	
	public void setAttributefExpFile(String attribute) {
		attributeOfExpFile = attribute;
	}

	public String getTypeOfExpFile() {
		return typeOfExpFile;
	}
	
	public int getPredfileToSave() {
		return PredfileToSave;
	}
	
	public int getExpfileToSave() {
		return ExpfileToSave;
	}
	
	public int getNbOfDataFile() {
		return nbdf;
	}
	
	public String getPredFileName() {
		return npredf;
	}
	
	public String getExpFileName() {
		return nexpf;
	}
	
	public void enableXUnshiftMenu(int n, boolean enable) {
		jmixunshift[n].setEnabled(enable);
	}
	
	public void enableYUnshiftMenu(int n, boolean enable) {
		jmiyunshift[n].setEnabled(enable);
	}
	
	public void enableHideMenu(int n, boolean enable) {
		jmihide[n].setEnabled(enable);
	}
	
	public void enableShowMenu(int n, boolean enable) {
		jmishow[n].setEnabled(enable);
	}
	
	public void setUnsave(boolean unsave) {
		if (this.project != null) {
			this.project.setUnsave(unsave);
		}
	}
}
