/*
 * (c) Copyright: Artenum SARL, 101-103 Boulevard Mac Donald,
 *                75019, Paris, France 2005.
 *                http://www.artenum.com
 *
 * License:
 *
 *  This program is free software; you can redistribute it
 *  and/or modify it under the terms of the Q Public License;
 *  either version 1 of the License.
 *
 *  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 Q Public License for more details.
 *
 *  You should have received a copy of the Q Public License
 *  License along with this program;
 *  if not, write to:
 *    Artenum SARL, 101-103 Boulevard Mac Donald,
 *    75019, PARIS, FRANCE, e-mail: contact@artenum.com
 */
package com.artenum.cassandra.action;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;

import com.artenum.cassandra.PreferenceListener;
import com.artenum.cassandra.launcher.DynamicClassLoader;
import com.artenum.cassandra.pipeline.PipeLineManager;
import com.artenum.cassandra.pipeline.io.PipelineToJythonExporter;
import com.artenum.cassandra.plugin.CassandraPluginIntegrator;
import com.artenum.cassandra.plugin.DynamicPluginMenu;
import com.artenum.cassandra.plugin.PluginManager;
import com.artenum.cassandra.ui.CassandraGUI;
import com.artenum.cassandra.ui.CassandraHelp;
import com.artenum.cassandra.ui.dialog.ExportPipelineDialog;
import com.artenum.cassandra.ui.dialog.ThreadedProgressBarDialog;
import com.artenum.cassandra.ui.popup.SaveDataSetToVtkFilePopupMenu;
import com.artenum.cassandra.util.BookmarkManager;
import com.artenum.cassandra.util.CassandraToolBox;
import com.artenum.cassandra.util.FileItem;
import com.artenum.cassandra.util.RecentFilesManager;

/**
 * <pre>
 * &lt;b&gt;Project ref           :&lt;/b&gt; CASSANDRA project
 * &lt;b&gt;Copyright and license :&lt;/b&gt; See relevant sections
 * &lt;b&gt;Status                :&lt;/b&gt; under development
 * &lt;b&gt;Creation              :&lt;/b&gt; 04/03/2005
 * &lt;b&gt;Modification          :&lt;/b&gt;
 * &lt;b&gt;Description  :&lt;/b&gt; Manage menu and toolbar actions of Cassandra
 * 
 * </pre>
 * <table cellpadding="3" cellspacing="0" border="1" width="100%">
 * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
 * <td><b>Version number</b></td>
 * <td><b>Author (name, e-mail)</b></td>
 * <td><b>Corrections/Modifications</b></td>
 * </tr>
 * <tr>
 * <td>0.1</td>
 * <td>Sebastien Jourdain, jourdain@artenum.com</td>
 * <td>Creation</td>
 * </tr>
 * </table>
 * 
 * @author Sebastien Jourdain, Julien Forest, Benoit Thiébault, Jérémie Turbet
 * @version 0.4
 */
public class CassandraActionListener implements ActionListener, PreferenceListener {
    public final static String LOAD_PLUGIN = "LOAD_PLUGIN";
    public final static String OPEN_VTK_FILE = "OPEN_VTK_FILE";
    public final static String OPEN_RECENT_VTK_FILE = "OPEN_RECENT_VTK_FILE";
    public final static String SAVE_VTK_FILE = "SAVE_VTK_FILE";
    public final static String IMPORT_DATA_FILE = "IMPORT_DATA_FILE";
    public final static String EXPORT_DATA_FILE = "EXPORT_DATA_FILE";
    public final static String SAVE_VTK_VIEW = "SAVE_VTK_VIEW";
    public final static String EXIT = "EXIT";
    public final static String ABOUT_CASSANDRA = "ABOUT_CASSANDRA";
    public final static String VIEW_RENDERER = "RENDERER";
    public final static String VIEW_CONSOLE = "CONSOLE";
    public final static String VIEW_PIPELINE = "PIPELINE";
    public final static String SET_BG_COLOR = "SET_BG_COLOR";
    public final static String SET_AMBIANT_LIGHT = "SET_AMBIANT_LIGHT";
    public final static String VIEW_AXIS = "AXIS";
    public final static String SET_TITLE = "SET_TITLE";
    public final static String IMPORT_PIPELINE_FROM_JYTHON = "import.pipeline.from.jython";
    public final static String EXPORT_PIPELINE_TO_JYTHON = "export.pipeline.to.jython";

    public final static String SET_VIEW_XY = "SET_VIEW_XY";
    public final static String SET_VIEW_YX = "SET_VIEW_YX";
    public final static String SET_VIEW_XZ = "SET_VIEW_XZ";
    public final static String SET_VIEW_ZX = "SET_VIEW_ZX";
    public final static String SET_VIEW_YZ = "SET_VIEW_YZ";
    public final static String SET_VIEW_ZY = "SET_VIEW_ZY";

    public final static String RESET_VIEW = "RESET_VIEW";
    public final static String PARALLEL_PROJECTION = "PARALLEL_PROJECTION";

    // pref
    public static final String PREF_PLUGIN_OPEN_DIR = "cassandra.pref.plugin.dir";
    public static final String PREF_VTK_FILE_OPEN_DIR = "cassandra.pref.vtk.file.dir";
    public static final String PREF_IMAGE_SAVE_DIR = "cassandra.pref.image.save.dir";
    public static final String PREF_AUTO_UPDATE_VTK_FILE_OPEN_DIR = "cassandra.pref.auto.update";
    
    private final PipeLineManager pipeLineManager;
    private final CassandraGUI view;
    private final PluginManager pluginManager;
    private Component parentComponent;
    private DynamicPluginMenu dynamicPluginMenu;
    private final ArrayList<String> pluginClassNameList;
    private JDialog about;
    private ExitAction exitAction;
    private Hashtable<String, Object> prefTable;
    private File pluginDir;
    private File openVtkFileDir;
    private File imageSaveDir;
    private boolean autoUpdateOpenVtkFileDir;
    private RecentFilesManager recentFileManager;

    /**
     * Main constructor of the central Cassandra Listener/controller for the menu and tool bar related actions.
     * 
     * @param view
     * @param pipeLineManager
     * @param pluginManager
     */
    public CassandraActionListener(final CassandraGUI view, final PipeLineManager pipeLineManager,
            final PluginManager pluginManager) {
        this.pipeLineManager = pipeLineManager;
        this.pluginManager = pluginManager;
        this.view = view;
        this.pluginClassNameList = new ArrayList<String>();
    }

    /**
     * process actions when performed.
     */
    @Override
    public void actionPerformed(final ActionEvent e) {

        final String actionCommand = e.getActionCommand();

        // plug-ins reloading action
        if (actionCommand.equals(LOAD_PLUGIN)) {
            final JFileChooser chooser = new JFileChooser();
            if (this.pluginDir != null) {
                chooser.setCurrentDirectory(this.pluginDir);
            }

            chooser.setMultiSelectionEnabled(true);
            chooser.setFileFilter(new FileFilter() {
                @Override
                public boolean accept(final File f) {
                    return f.isDirectory() || f.getName().endsWith(".jar");
                }

                @Override
                public String getDescription() {
                    return "Jar files";
                }
            });
            if (chooser.showOpenDialog(this.parentComponent) == JFileChooser.APPROVE_OPTION) {
                final File[] jarFiles = chooser.getSelectedFiles();
                for (int i = 0; i < jarFiles.length; i++) {
                    if (jarFiles[i].isFile()) {
                        DynamicClassLoader.getInstance().addJar(jarFiles[i]);
                    }
                }
                reloadPlugings();
            }

            // view control section
        } else if (actionCommand.equals(SET_VIEW_XY)) {
            this.pipeLineManager.getCassandraView().setXYView();
            this.pipeLineManager.validateViewAndGo();

        } else if (actionCommand.equals(SET_VIEW_YX)) {
            this.pipeLineManager.getCassandraView().setYXView();
            this.pipeLineManager.validateViewAndGo();

        } else if (actionCommand.equals(SET_VIEW_XZ)) {
            this.pipeLineManager.getCassandraView().setXZView();
            this.pipeLineManager.validateViewAndGo();

        } else if (actionCommand.equals(SET_VIEW_ZX)) {
            this.pipeLineManager.getCassandraView().setZXView();
            this.pipeLineManager.validateViewAndGo();

        } else if (actionCommand.equals(SET_VIEW_YZ)) {
            this.pipeLineManager.getCassandraView().setYZView();
            this.pipeLineManager.validateViewAndGo();

        } else if (actionCommand.equals(SET_VIEW_ZY)) {
            this.pipeLineManager.getCassandraView().setZYView();
            this.pipeLineManager.validateViewAndGo();

        } else if (actionCommand.equals(RESET_VIEW)) {
            this.pipeLineManager.getCassandraView().resetCamera();
            this.pipeLineManager.validateViewAndGo();

            // exit application section
        } else if (actionCommand.equals(EXIT)) {
            if (this.exitAction == null) {
                System.exit(0);
            } else {
                this.exitAction.exit();
            }

            // file related actions section
        } else if (actionCommand.equals(OPEN_VTK_FILE)) {
            vtkFileChooserAndLoad();

            // loading from the recent file menu
        } else if (actionCommand.equals(RecentFilesManager.RECENT_SELECTED_FILE_ACTION)) {
            final File recentFile = ((FileItem) e.getSource()).getFile();
            if (recentFile.isFile() && isVtkFile(recentFile)) {
                final ThreadedFileLoader loader = new ThreadedFileLoader(this.pipeLineManager, recentFile);
                loader.setProgressMonitor(new ThreadedProgressBarDialog());
                loader.start();
            } else {
                JOptionPane.showMessageDialog(new JFrame(),
                        "File not found or not format supported. Please select another file.", "File not supported.",
                        JOptionPane.PLAIN_MESSAGE);
                vtkFileChooserAndLoad();
            }

            // loading file from the bookmark
        } else if (actionCommand.equals(BookmarkManager.SELECTED_BOOKMARK_ACTION)) {
            final File bookmarkedFile = ((FileItem) e.getSource()).getFile();
            if (bookmarkedFile.isFile() && isVtkFile(bookmarkedFile)) {
                final ThreadedFileLoader loader = new ThreadedFileLoader(this.pipeLineManager, bookmarkedFile);
                loader.setProgressMonitor(new ThreadedProgressBarDialog());
                loader.start();
            } else {
                JOptionPane.showMessageDialog(new JFrame(),
                        "File not found or not format supported. Please select another file.", "File not supported.",
                        JOptionPane.PLAIN_MESSAGE);
                vtkFileChooserAndLoad();
            }

            // console related action section
        } else if (actionCommand.equals(VIEW_CONSOLE)) {
            if (((JCheckBoxMenuItem) e.getSource()).isSelected()) {
                this.view.showConsole();
            } else {
                this.view.hideConsole();
            }
            this.pipeLineManager.deepValidateView();

            // set the BG color of the 3D view
        } else if (actionCommand.equals(SET_BG_COLOR)) {
            final Color c = JColorChooser.showDialog(this.view, "Background color", Color.BLACK);
            if (c != null) {
                this.pipeLineManager.getCassandraView().setBackground(c);
            }
            this.pipeLineManager.validateViewAndGo();

            // set the ambient light for the 3D view
        } else if (actionCommand.equals(SET_AMBIANT_LIGHT)) {
            final Color c = JColorChooser.showDialog(this.view, "Ambiant light", Color.BLACK);
            if (c != null) {
                this.pipeLineManager.getCassandraView().setAmbiantLight(c);
            }
            this.pipeLineManager.validateViewAndGo();

            // add/set the title in the 3D view
        } else if (actionCommand.equals(SET_TITLE)) {
        } else if (actionCommand.equals(VIEW_AXIS)) {
            this.pipeLineManager.setAxisVisible(((JCheckBoxMenuItem) e.getSource()).isSelected());
            this.pipeLineManager.validateViewAndGo();

            // toggle to parallel/perspective projection
        } else if (actionCommand.equals(PARALLEL_PROJECTION)) {
            this.pipeLineManager.setParallelProjection(((JCheckBoxMenuItem) e.getSource()).isSelected());
            this.pipeLineManager.validateViewAndGo();

            // show/hide the pipeline editor
        } else if (actionCommand.equals(VIEW_PIPELINE)) {
            if (((JCheckBoxMenuItem) e.getSource()).isSelected()) {
                this.view.showPipeLine();
            } else {
                this.view.hidePipeLine();
            }
            this.pipeLineManager.deepValidateView();

            // save the view a picture view
        } else if (actionCommand.equals(SAVE_VTK_VIEW)) {
            final JFileChooser chooser = new JFileChooser();
            if (this.imageSaveDir != null) {
                chooser.setCurrentDirectory(this.imageSaveDir);
            }

            chooser.setFileFilter(new FileFilter() {
                @Override
                public boolean accept(final File f) {
                    if (f.isDirectory()) {
                        return true;
                    }

                    if (f.isFile()
                            && (f.getName().toLowerCase().endsWith(".tif") || f.getName().toLowerCase()
                                    .endsWith(".tiff"))) {
                        return true;
                    }

                    return false;
                }

                @Override
                public String getDescription() {
                    return "Tiff image file";
                }
            });
            chooser.showSaveDialog(this.view);
            final File fileToSave = chooser.getSelectedFile();
            this.imageSaveDir = fileToSave.getParentFile();
            if (fileToSave != null) {
                String filePath = fileToSave.getAbsolutePath();
                if (!(filePath.toLowerCase().endsWith(".tif") || filePath.toLowerCase().endsWith(".tiff"))) {
                    filePath += ".tiff";
                }
                //this.pipeLineManager.getCassandraView().HardCopy(filePath, 1);
                //more robust and generic
                this.pipeLineManager.getCassandraView().saveToTIFF(filePath);
            }

            // show the renderer
        } else if (actionCommand.equals(VIEW_RENDERER)) {
            if (((JCheckBoxMenuItem) e.getSource()).isSelected()) {
                this.view.showRenderer();
            } else {
                this.view.hideRenderer();
            }
            this.pipeLineManager.deepValidateView();

            // save the VTK file from the main menu
        } else if (actionCommand.equals(SAVE_VTK_FILE)) {

            final SaveDataSetToVtkFilePopupMenu saveMenu = new SaveDataSetToVtkFilePopupMenu(this.pipeLineManager,
                    this.pluginManager, null);
            saveMenu.setVisible(true);

            // give Cassandra information
        } else if (actionCommand.equals(ABOUT_CASSANDRA)) {
            if (this.about == null) {
                this.about = new CassandraHelp(this.view);
            }

            this.about.setLocationRelativeTo(null);
            this.about.setVisible(true);

            // export the whole pipeline to a Jython file
        } else if (actionCommand.equals(EXPORT_PIPELINE_TO_JYTHON)) {

            final ExportPipelineDialog dialog = new ExportPipelineDialog(null, this.pipeLineManager);
            final int answer = dialog.showMessageDialog();
            System.out.println("answer = " + answer);
            if (answer == 0) {

                final PipelineToJythonExporter exporter = new PipelineToJythonExporter();
                exporter.setPipeline(this.pipeLineManager);
                exporter.setSelectedVtkObjectList(this.pipeLineManager.getVtkObjectHashtable());

                exporter.setOutputFile(dialog.getSelectedFile());
                exporter.write();
            } else {
                System.out.println("Nothing selected");
            }

        } else if (actionCommand.equals(IMPORT_PIPELINE_FROM_JYTHON)) {

            final JFileChooser chooser = new JFileChooser();
            final int answer = chooser.showOpenDialog(this.view);
            if (answer == 0) {
                final File selectedFile = chooser.getSelectedFile();
                System.out.println(selectedFile.getName());
                this.view.getPyConsole().executePythonFile(selectedFile);
            }

        } else {
            System.out.println(actionCommand);
        }
    }

    /**
     * Helper to check if the selected file is a Vtk file.
     * 
     * @param file
     * @return
     */
    public boolean isVtkFile(final File file) {
        final FileFilter filter = new FileFilter() {
            @Override
            public boolean accept(final File f) {
                return f.isDirectory() || f.getName().endsWith(".vtk") || f.getName().endsWith(".vtu");
            }

            @Override
            public String getDescription() {
                return "Vtk files (.vtk, .vtu...)";
            }
        };
        return (filter.accept(file));
    }

    /**
     * Open a file chooser preset for VTK files and load-it.
     */
    public void vtkFileChooserAndLoad() {
        final JFileChooser chooser = new JFileChooser();
        if (this.openVtkFileDir != null) {
            chooser.setCurrentDirectory(this.openVtkFileDir);
        }

        chooser.setMultiSelectionEnabled(true);
        chooser.setFileFilter(new FileFilter() {
            @Override
            public boolean accept(final File f) {
                return f.isDirectory() || f.getName().endsWith(".vtk") || f.getName().endsWith(".vtu");
            }

            @Override
            public String getDescription() {
                return "Vtk files (.vtk, .vtu...)";
            }
        });

        if (chooser.showOpenDialog(this.view) == JFileChooser.APPROVE_OPTION) {
            final File[] vtkFiles = chooser.getSelectedFiles();
            CassandraToolBox.sortFileList(vtkFiles);

            final ThreadedFileLoader loader = new ThreadedFileLoader(this.pipeLineManager);
            loader.setProgressMonitor(new ThreadedProgressBarDialog());
            for (int i = 0; i < vtkFiles.length; i++) {
                loader.addFileToLoad(vtkFiles[i]);
            }
            loader.start();

            for (int i = 0; i < vtkFiles.length; i++) {
                if (this.autoUpdateOpenVtkFileDir) {
                    this.openVtkFileDir = vtkFiles[i].getParentFile();
                    if (this.recentFileManager != null) {
                        this.recentFileManager.addFile(vtkFiles[i]);
                    }
                }
            }
        }
    }

    /**
     * Reload the plugings and update the menus. 
     */
    public void reloadPlugings() {
        // Update menus
        for (final Iterator<String> i = DynamicClassLoader.getInstance().getPluginIntegratorList().iterator(); i
                .hasNext();) {
            final String className = i.next();
            //System.out.println("Integrating: " + className);
            
            if (!this.pluginClassNameList.contains(className)) {
                this.pluginClassNameList.add(className);
                try {    	
                    ((CassandraPluginIntegrator) DynamicClassLoader.getInstance().getPluginIntegrator(className))
                            .integratePluginsInFramework(this.pipeLineManager, this.pluginManager,
                                    this.dynamicPluginMenu);
                } catch (final Exception e1) {
                    e1.printStackTrace();
                }
            } 
            
            //else {
            //	System.out.println("Already in class list.");
            //}
        }
    }

    public void setDynamicPluginMenu(final DynamicPluginMenu dynaPluginMenu) {
        this.dynamicPluginMenu = dynaPluginMenu;
    }

    public void setParentComponent(final Component parentComponent) {
        this.parentComponent = parentComponent;
    }

    /**
     * Set the exit action of the application.
     * 
     * @param exitAction
     */
    public void setExitAction(final ExitAction exitAction) {
        this.exitAction = exitAction;
    }

    @Override
    public void setPreferences(final Hashtable<String, Object> prefTable) {
        this.prefTable = prefTable;
    }

    @Override
    public void preferenceChanged() {
        if (this.prefTable != null) {
            try {
                if (this.prefTable.get(PREF_AUTO_UPDATE_VTK_FILE_OPEN_DIR).equals("false")) {
                    this.autoUpdateOpenVtkFileDir = false;
                } else {
                    this.autoUpdateOpenVtkFileDir = true;
                }
            } catch (final Exception e) {
                System.out
                        .println("Impossible to change the following property: " + PREF_AUTO_UPDATE_VTK_FILE_OPEN_DIR);
                this.prefTable.put(PREF_AUTO_UPDATE_VTK_FILE_OPEN_DIR, "false");
            }

            try {
                this.imageSaveDir = (File) this.prefTable.get(PREF_IMAGE_SAVE_DIR);
            } catch (final Exception e) {
                System.out.println("Impossible to change the following property: " + PREF_IMAGE_SAVE_DIR);
            }

            try {
                this.pluginDir = (File) this.prefTable.get(PREF_PLUGIN_OPEN_DIR);
            } catch (final Exception e) {
                System.out.println("Impossible to change the following property: " + PREF_PLUGIN_OPEN_DIR);
            }

            try {
                this.openVtkFileDir = (File) this.prefTable.get(PREF_VTK_FILE_OPEN_DIR);
            } catch (final Exception e) {
                System.out.println("Impossible to change the following property: " + PREF_VTK_FILE_OPEN_DIR);
                this.openVtkFileDir = new File(".");
            }
        }
    }

    /**
     * @return the manager of recently loaded files.
     */
    public RecentFilesManager getRecentFileManager() {
        return this.recentFileManager;
    }

    /**
     * Set the manager of recently loaded files.
     * 
     * @param recentFileManager
     */
    public void setRecentFileManager(final RecentFilesManager recentFileManager) {
        this.recentFileManager = recentFileManager;
    }
}
