// $Id: PmiiController.java,v 1.11 2004/09/18 12:21:10 m31 Exp $
//
// This file is part of the program suite "Principia Mathematica II"
// which is a working prototype for the main project:
// "Hilbert II" - http://www.qedeq.org
//
// Copyright 2001-2004  Michael Meyling <mime@qedeq.org>.
//
// "Principia Mathematica II" is free software; you can redistribute
// it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

package com.meyling.principia.gui;

import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;


import com.meyling.principia.config.Settings;
import com.meyling.principia.gui.jtree.NothingSelectedException;
import com.meyling.principia.gui.jtree.PmiiTreeCtrl;
import com.meyling.principia.html.Module2Html;
import com.meyling.principia.html.Module2OldHtml;
import com.meyling.principia.io.BrowserLauncher;
import com.meyling.principia.io.DataDictionary;
import com.meyling.principia.io.ParsingException;
import com.meyling.principia.io.Utility;
import com.meyling.principia.latex.Module2Latex;
import com.meyling.principia.log.Logger;
import com.meyling.principia.log.Trace;
import com.meyling.principia.module.Module;
import com.meyling.principia.module.ModuleContext;
import com.meyling.principia.module.ModuleProperties;


/**
 * Controller for a the gui application.
 *
 * A Controller, which represents the classes connecting the model and the view, and is used to
 * communicate between classes in the model and view.
 * Connects the models and the views.  Handles program flow.
 * 
 * @version $Revision: 1.11 $
 * @author  Michael Meyling
 */
public final class PmiiController {

    private PmiiTreeCtrl treeCtrl;
    
    private final Action addAction;

    private final Action addFileAction;

    private final Action removeAllAction;

    private final Action removeLocalBufferAction;

    private final Action addPreviouslyCheckedAction;

    private final Action addAllModulesFromQedeqAction;

    private final Action makeHtmlAction;

    private final Action makeAllHtmlAction;

    private final Action makeLatexAction;

    private final Action makeAllLatexAction;

    private final Action deleteAction;

    private final Action createNewModuleAction;

    private final Action saveModuleAction;

    private final Action compressAction;

    private final Action compressAllAction;

    private final Action reduceAction;

    private final Action reduceAllAction;

    private final List moduleHistory;
    
    private final JFrame main;

    private final static int MAXIMUM_MODULE_HISTORY = 10; // TODO put into pmii.properties


    public PmiiController(final JFrame main) {

        this.main = main;
        
        this.addAction = new AddAction();
        this.addFileAction = new AddFileAction();
        this.addPreviouslyCheckedAction = new AddPreviouslyCheckedAction();
        this.addAllModulesFromQedeqAction = new AddAllModulesFromQedeqAction();
        this.removeAllAction = new RemoveAllAction();
        this.removeLocalBufferAction = new RemoveLocalBufferAction();
        this.makeHtmlAction = new MakeHtmlAction();
        this.makeAllHtmlAction = new MakeAllHtmlAction();
        this.makeLatexAction = new MakeLatexAction();
        this.makeAllLatexAction = new MakeAllLatexAction();
        this.deleteAction = new DeleteAction();
        this.createNewModuleAction = new CreateNewModuleAction();
        this.saveModuleAction = new SaveModuleAction();
        this.compressAction = new CompressAction();
        this.compressAllAction = new CompressAllAction();
        this.reduceAction = new ReduceAction();
        this.reduceAllAction = new ReduceAllAction();

        this.moduleHistory = Settings.getInstance().getModuleHistory();
        Message.init(main);
        Message.getInstance().setDirectResponse(Settings.getInstance().isDirectResponse());
        
        // TODO Q & D version dependencies etc.
        if (this.moduleHistory.size() == 0) {
            this.moduleHistory.add("http://www.qedeq.org/0_00_53/propaxiom_1.00.00_1.00.00.qedeq");
            this.moduleHistory.add("http://www.qedeq.org/0_00_53/proptheo1_1.00.00_1.00.00.qedeq");
            this.moduleHistory.add("http://www.qedeq.org/0_00_53/prophilbert3_1.00.00_1.00.00.qedeq");
            this.moduleHistory.add("http://www.qedeq.org/0_00_53/properror1_1.00.00_1.00.00.qedeq");
        }
    }


    public final void setTreeCtrl(final PmiiTreeCtrl treeCtrl) {
        this.treeCtrl = treeCtrl;
    }


    public final void setStatusField(final JTextField statusField) {
        Message.getInstance().setStatusField(statusField);
    }


    public final Action getAddAction() {
        return this.addAction;
    }


    public final Action getAddFileAction() {
        return this.addFileAction;
    }

    public final Action getHtmlAction() {
        return this.makeHtmlAction;
    }

    public final Action getHtmlAllAction() {
        return this.makeAllHtmlAction;
    }

    public final Action getLatexAction() {
        return this.makeLatexAction;
    }

    public final Action getLatexAllAction() {
        return this.makeAllLatexAction;
    }

    public final Action getRemoveAllAction() {
        return this.removeAllAction;
    }

    public final Action getRemoveLocalBufferAction() {
        return this.removeLocalBufferAction;
    }

    public final Action getAddPreviouslyCheckedAction() {
        return this.addPreviouslyCheckedAction;
    }


    public final Action getAddAllModulesFromQedeqAction() {
        return this.addAllModulesFromQedeqAction;
    }

    public final Action getDeleteAction() {
        return this.deleteAction;
    }

    public final Action getCreateNewModuleAction() {
        return this.createNewModuleAction;
    }

    public final Action getSaveModuleAction() {
        return this.saveModuleAction;
    }

    public final Action getCompressAction() {
        return this.compressAction;
    }

    public final Action getCompressAllAction() {
        return this.compressAllAction;
    }

    public final Action getReduceAction() {
        return this.reduceAction;
    }

    public final Action getReduceAllAction() {
        return this.reduceAllAction;
    }


    /**
     * This method returns a string from the resource bundle.
     */
    public String getString(String key) {
        return DataDictionary.getInstance().getString(key);
    }


    /**
     * Returns a mnemonic from the resource bundle. Typically used as
     * keyboard shortcuts in menu items.
     */
    public char getMnemonic(String key) {
        return DataDictionary.getInstance().getMnemonic(key);
    }




    /**
      * Load qedeq module file.
      */
    final class AddAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            Trace.trace(this, "actionPerformed", e);


            // Messages
            Object[]      message = new Object[2];
            message[0] = "Please choose or enter a module URL:";

            JComboBox cb = new JComboBox();
            for (int i = 0; i < moduleHistory.size() && i < MAXIMUM_MODULE_HISTORY; i++) {
                cb.addItem(moduleHistory.get(i));
            }
            cb.setEditable(true);
            message[1] = cb;


            // Options
            String[] options = { "Ok", "Cancel"};
            int result = JOptionPane.showOptionDialog(
                PmiiController.this.main,       // the parent that the dialog blocks
                message,                        // the dialog message array
                "Loading of a module",          // the title of the dialog window
                JOptionPane.CANCEL_OPTION,      // option type
                JOptionPane.QUESTION_MESSAGE,   // message type
                null,                           // optional icon, use null to use the default icon
                options,                        // options string array, will be made into buttons
                options[1]                      // option that should be made into a default button
            );
            final String url;
            switch(result) {
            case 0:     // ok
                url = (String) cb.getSelectedItem();
                addToModuleHistory(url);
                break;
            case 1:     // cancel, fallthrough default
            default:
                return;
            }
            final Thread thread = new Thread() {
                public void run() {
                    try {
                        Logger.getInstance().logRequest("Load module \"" + url + "\"");
                        final Module module
                            = ModuleContext.getInstance().loadModule(url);

                        Message.getInstance().success("Module \"" + module.getModuleFileName()
                            + "\" was successfully loaded and checked.");
                    } catch (IOException ex) {
                        Trace.trace(PmiiController.this, "actionPerformed", ex);
                        Message.getInstance().failure(ex.getMessage(), "IO access problems");
                    } catch (IllegalArgumentException ex) {
                        Trace.trace(PmiiController.this, "actionPerformed", ex);
                        Message.getInstance().failure(ex.getMessage(), "unknown reason");
                    } catch (ParsingException ex) {
                        Trace.trace(PmiiController.this, "actionPerformed", ex);
                        Trace.trace(PmiiController.this, "actionPerformed", (new File(ex.getAddress()))
                            .getAbsolutePath() + ":" + ex.getRow() + ":"
                            + ex.getColumn() + ":");
                        Trace.trace(PmiiController.this, "actionPerformed", ex.getDescription());
                        Trace.trace(PmiiController.this, "actionPerformed", ex.getLine());
                        final StringBuffer pointer = Utility.getSpaces(ex.getColumn() - 1);
                        pointer.append("^");
                        Trace.trace(PmiiController.this, "actionPerformed", pointer);
                        Message.getInstance().failure(ex.getMessage(), "parsing problem:\n" +
                            (new File(ex.getAddress())).getAbsolutePath() + ":" + ex.getRow() + ":"
                            + ex.getColumn() + ":" + "\n"
                            + ex.getLine());
                    }
                }
            };
            thread.setDaemon(true);
            thread.start();
        }

    }


    /**
      * Load new module from local file.
      */
    final class AddFileAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            Trace.trace(this, "actionPerformed", e);
            JFileChooser chooser = new JFileChooser(
                        Settings.getInstance().getStartDirectory());
            final FileFilter filter = new FileFilter() {
                public boolean accept(File f) {
                    if (f.isDirectory() || f.getPath().endsWith(".qedeq")) {
                        return true;
                    }
                    return false;
                }

                // description of this filter
                public String getDescription() {
                    return "Qedeq Modules";
                }
            };

            chooser.setFileFilter(filter);
            final int returnVal = chooser.showOpenDialog(PmiiController.this.main);

            if (returnVal != JFileChooser.APPROVE_OPTION) {
                return;
            }
            final String url;
            
            try {
//              TODO causes problems: leads to "%20" entries for spaces
//                url = chooser.getSelectedFile().toURI().toURL().toString();
//              these must be converted like:
//                "URI File=" + new File(new URI(this.address)).getAbsoluteFile()    
                url = chooser.getSelectedFile().toURL().toString(); 
            } catch (MalformedURLException ex) {
                Trace.trace(this, "actionPerformed", ex);
                return;
            }

            addToModuleHistory(url);
            final Thread thread = new Thread() {
                public void run() {
                    try {
                        Logger.getInstance().logRequest("Load module \"" + url + "\"");
                        final Module module
                            = ModuleContext.getInstance().loadModule(url);
                        Message.getInstance().success("Module \"" + module.getModuleFileName()
                            + "\" was successfully loaded and checked.");
                    } catch (IOException ex) {
                        Trace.trace(PmiiController.this, "actionPerformed", ex);
                        Message.getInstance().failure(ex.getMessage(), "IO access problem");
                    } catch (IllegalArgumentException ex) {
                        Trace.trace(PmiiController.this, "actionPerformed", ex);
                        Message.getInstance().failure(ex.getMessage(), "unknown reason");
                    } catch (ParsingException ex) {
                        Trace.trace(PmiiController.this, "actionPerformed", ex);
                        Trace.trace(PmiiController.this, "actionPerformed", (new File(ex.getAddress()))
                            .getAbsolutePath() + ":" + ex.getRow() + ":"
                            + ex.getColumn() + ":");
                        Trace.trace(PmiiController.this, "actionPerformed", ex.getDescription());
                        Trace.trace(PmiiController.this, "actionPerformed", ex.getLine());
                        final StringBuffer pointer = Utility.getSpaces(ex.getColumn() - 1);
                        pointer.append("^");
                        Trace.trace(PmiiController.this, "actionPerformed", pointer);
                        Message.getInstance().failure(ex.getMessage(), 
                            (new File(ex.getAddress())).getAbsolutePath() + ":" + ex.getRow() + ":"
                            + ex.getColumn() + ":" + "\n"
                            + ex.getLine());
                    }
                }
            };
            thread.setDaemon(true);
            thread.start();
        }

    }


    /**
      * Add all previously (in the session before) succesfully checked modules.
      */
    final class AddPreviouslyCheckedAction extends AbstractAction {
        
        
        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            Trace.trace(this, "actionPerformed", e);

            final Thread thread = new Thread() {
                public void run() {
                    Logger.getInstance().logRequest(
                        "Load all previously (the session before this one) checked modules");
                    final boolean success = ModuleContext.getInstance()
                        .loadPreviouslyCheckedModules();
                    if (success) {
                        Message.getInstance().success(
                            "Previously checked modules successfully loaded and checked again.");
                    } else {
                        Message.getInstance().failure("Some modules couldn't be loaded and checked.",
                            "see detailed status entries");
                    }                 }
            };
            thread.setDaemon(true);
            thread.start();


        }
    }



    /**
      * Add all modules from the qedeq web page.
      */
    final class AddAllModulesFromQedeqAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            Trace.trace(this, "actionPerformed", e);

            final Thread thread = new Thread() {
                public void run() {
                    Logger.getInstance().logRequest("Load all modules from \"http://www.qedeq.org\"");
                    final boolean success = ModuleContext.getInstance()
                        .loadAllModulesFromQedeq();
                    if (success) {
                        JOptionPane.showMessageDialog(
                            PmiiController.this.main,
                            "All modules from project page successfully loaded and checked again.",
                            "Success",
                            JOptionPane.INFORMATION_MESSAGE,
                            null
                        );
                    } else {
                        Message.getInstance().failure("Some modules couldn't be loaded and checked.",
                        "see detailed status entries");
                    }                 }
            };
            thread.setDaemon(true);
            thread.start();
        }

    }



    /**
      * Remove all qedeq module files from memory.
      */
    final class RemoveAllAction extends AbstractAction {

        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            Trace.trace(this, "actionPerformed", e);

            final Thread thread = new Thread() {
                public void run() {
                    ModuleContext.getInstance().removeAllModules();
                }
            };
            thread.setDaemon(true);
            thread.start();
        }
    }



    /**
     * Remove all qedeq module files from local buffer.
     */
    class RemoveLocalBufferAction extends AbstractAction {


        /* inherited 
         */
        public void actionPerformed(final ActionEvent e) {
            Trace.trace(this, "actionPerformed", e);

            final Thread thread = new Thread() {
                public void run() {
                    try {
                        Logger.getInstance().logRequest("Clear local buffer from all qedeq files.");
                        ModuleContext.getInstance().clearLocalBuffer();
                        Message.getInstance().success(
                            "Local buffer was cleared.");
                    } catch (IOException e) {
                        Trace.trace(PmiiController.this, "actionPerformed", e);
                        Message.getInstance().failure(e.getMessage(), "IO access problem");
                    }
                }
            };
            thread.setDaemon(true);
            thread.start();
        }
    }



    /**
     *  Create HTML file out of selected qedeq module file.
     */
    class MakeHtmlAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final ModuleProperties prop;
                try {
                    prop = PmiiController.this.treeCtrl.getSelected();
                } catch (NothingSelectedException ex) {
                    Message.getInstance().failure("No qedeq module selected!", 
                        "one qedeq module must be selected in the tree");
                    return;
                }
    
                final Thread thread = new Thread() {
                    public void run() {

                        try {
                            Logger.getInstance().logRequest("Generate HTML for \"" 
                                + prop.getAddress() + "\"");
                            {
                                final Module2OldHtml converter = new Module2OldHtml(prop.getAddress());
                                converter.writeModule();
                            } 
                            {
                                final Module2Html converter = new Module2Html(prop.getAddress());
                                converter.writeModule();
                            }
                            Message.getInstance().success("HTML was generated for \"" 
                                + prop.getAddress() + "\"");
                            final String addr;
                            if (Settings.getInstance().isOldHtml()) {
                                addr = prop.getModuleAddress().getGeneratedName("o.html")
                                    .replace('/', File.separatorChar);
                            } else {
                                addr = prop.getModuleAddress().getGeneratedName("html")
                                .replace('/', File.separatorChar);
                            }
                            Trace.trace(PmiiController.this, METHOD, "addr=" + addr);
                            if (Settings.getInstance().isAutoStartHtmlBrowser()) {
                                BrowserLauncher.openURL(addr);
                            }
                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(), 
                                "parsing of qedeq module failed");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     *  Create compressed file out of selected qedeq module file.
     */
    class CompressAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final ModuleProperties prop;
                try {
                    prop = PmiiController.this.treeCtrl.getSelected();
                } catch (NothingSelectedException ex) {
                    Message.getInstance().failure("No qedeq module selected!", 
                        "one qedeq module must be selected in the tree");
                    return;
                }
    
                final Thread thread = new Thread() {
                    public void run() {

                        try {
                            Logger.getInstance().logRequest("Generate compressed qedeq module for \"" 
                                + prop.getAddress() + "\"");
                            ModuleContext.getInstance().compress(prop.getAddress());
                            Message.getInstance().success("Compressing was successfull for \"" 
                                + prop.getAddress() + "\"");
                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(), 
                                "parsing of qedeq module failed");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     *  Create &quot;compressed&quot; qedeq files out of all checked qedeq module files.
     */
    class CompressAllAction extends AbstractAction {


        /* inherited
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final Thread thread = new Thread() {
                    public void run() {
                        try {
                            Logger.getInstance().logRequest("Generate compressed modules for all checked modules.");
                            final List checked = ModuleContext.getInstance().getAllCheckedModules();
                            for (int i = 0; i < checked.size(); i++) {
                                final String address = (String) checked.get(i);
                                ModuleContext.getInstance().compress(address);
                            }
                            Message.getInstance().success(
                                "Compressed qedeq files were generated for all checked modules.");

                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "qedeq module parsing problem");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     *  Create HTML files out of all checked qedeq module files.
     */
    class MakeAllHtmlAction extends AbstractAction {


        /* inherited
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final Thread thread = new Thread() {
                    public void run() {
                        try {
                            Logger.getInstance().logRequest("Generate HTML for all checked modules.");
                            final List checked = ModuleContext.getInstance().getAllCheckedModules();
                            for (int i = 0; i < checked.size(); i++) {
                                final String address = (String) checked.get(i);
                                {
                                    final Module2OldHtml converter = new Module2OldHtml(address);
                                    converter.writeModule();
                                }
                                {
                                    final Module2Html converter = new Module2Html(address);
                                    converter.writeModule();
                                }
                            }
                            Message.getInstance().success(
                                "HTML files were generated for all checked modules.");

                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "qedeq module parsing problem");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     * Create LaTeX file out of selected qedeq module file.
     */
    class MakeLatexAction extends AbstractAction {


        /* inherited
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final ModuleProperties prop;
                try {
                    prop = PmiiController.this.treeCtrl.getSelected();
                } catch (NothingSelectedException ex) {
                    Message.getInstance().failure("No qedeq module selected!",
                        "in the tree one qedeq module must be selected");
                    return;
                }
    
                final Thread thread = new Thread() {
                    public void run() {

                        try {
                            Logger.getInstance().logRequest("Generate Latex for \"" 
                                + prop.getAddress() + "\"");
                            final Module2Latex converter = new Module2Latex(prop.getAddress());
                            converter.writeModule();
                            Message.getInstance().success("LaTeX was generated for." 
                                + prop.getAddress() + "\"");
                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "parsing of qedeq module failed");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     * Create LaTeX files out of all checked qedeq module files.
     */
    class MakeAllLatexAction extends AbstractAction {


        /* inherited
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final Thread thread = new Thread() {
                    public void run() {
                        try {
                            Logger.getInstance().logRequest(
                                "Generate Latex for all checked modules"); 
                            final List checked = ModuleContext.getInstance().getAllCheckedModules();
                            for (int i = 0; i < checked.size(); i++) {
                                final String address = (String) checked.get(i);
                                final Module2Latex converter = new Module2Latex(address);
                                converter.writeModule();
                            }
                            Message.getInstance().success("LaTeX files were generated.");

                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "qedeq module parsing problem");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     *  Create reduced qedeq module file out of selected qedeq module file.
     */
    class ReduceAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final ModuleProperties prop;
                try {
                    prop = PmiiController.this.treeCtrl.getSelected();
                } catch (NothingSelectedException ex) {
                    Message.getInstance().failure("No qedeq module selected!", 
                        "one qedeq module must be selected in the tree");
                    return;
                }
    
                final Thread thread = new Thread() {
                    public void run() {

                        try {
                            Logger.getInstance().logRequest("Generate reduced qedeq module for \"" 
                                + prop.getAddress() + "\"");
                            ModuleContext.getInstance().reduceToBasicRules(prop.getAddress());
                            Message.getInstance().success("Reducing was successfull for \"" 
                                + prop.getAddress() + "\"");
                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(), 
                                "parsing of qedeq module failed");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     *  Create reduced qedeq files out of all checked qedeq module files. Those reduced qedeq
     *  modules use only the basic rules.
     */
    class ReduceAllAction extends AbstractAction {


        /* inherited
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final Thread thread = new Thread() {
                    public void run() {
                        try {
                            Logger.getInstance().logRequest("Generate reduced modules for all checked modules.");
                            final List checked = ModuleContext.getInstance().getAllCheckedModules();
                            for (int i = 0; i < checked.size(); i++) {
                                final String address = (String) checked.get(i);
                                ModuleContext.getInstance().reduceToBasicRules(address);
                            }
                            Message.getInstance().success(
                                "Reduced qedeq files were generated for all checked modules.");

                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "qedeq module parsing problem");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "IO access problem");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     *  Delete selected qedeq module file and all dependent ones.
     */
    class DeleteAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final ModuleProperties prop;
                try {
                    prop = PmiiController.this.treeCtrl.getSelected();
                } catch (NothingSelectedException ex) {
                    Message.getInstance().failure("No qedeq module selected!", 
                        "one qedeq module must be selected in the tree");
                    return;
                }
    
                final Thread thread = new Thread() {
                    public void run() {
                        try {
                            Logger.getInstance().logRequest("Delete qedeq module \"" 
                                + prop.getAddress() + "\"");
                            ModuleContext.getInstance().removeModuleAndDependents(prop);
                            Message.getInstance().success("Following qede module was deleted: \"" 
                                + prop.getAddress() + "\"");
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
      * Create a new qedeq module per template.
      */
    final class CreateNewModuleAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                Trace.trace(this, METHOD, e);
    
                final String url;
                try {
                    final File dir = new File(Settings.getInstance().getLocalModulesDirectory());
                    final String templateName = "module_template.qedeq";
                    final File templateFile = new File(dir, templateName);
                    final StringBuffer template = new StringBuffer();
                    if (!templateFile.canRead()) {
                        final InputStream input = this.getClass().getResourceAsStream(
                            "/com/meyling/principia/resources/module_template.qedeq");
                        if (input == null) {
                            throw new IllegalArgumentException("ressource missing!");
                        }
                        Utility.loadStream(input, template);
                        templateFile.getParentFile().mkdirs();
                        Utility.saveFile(templateFile, template);
                    } else {
                        Utility.loadFile(templateFile, template);
                    }
                    File moduleFile;
                    String replacement;
                    int i = 0;
                    do {
                        replacement = (i < 10 ? "0" + i : "" + i);
                        final String name = Utility.replace("new_1.00.@number@_1.02.00.qedeq", 
                            "@number@", replacement);
                        moduleFile = new File(dir, name);
                        i++;
                    } while (moduleFile.exists());
                    moduleFile.getParentFile().mkdirs();
                    Utility.saveFile(moduleFile, Utility.replace(template.toString(), 
                        "@number@", replacement));
                    url = moduleFile.toURL().toString(); 
                } catch (Exception ex) {
                    Trace.trace(this, METHOD, ex);
                    return;
                }
                
                addToModuleHistory(url);
                final Thread thread = new Thread() {
                    public void run() {
                        final String METHOD = "actionPerformed$run";
                        Trace.traceBegin(this, METHOD);
                        try {
                            Logger.getInstance().logRequest("Load module \"" + url + "\"");
                            final Module module
                                = ModuleContext.getInstance().loadModule(url);
                            Message.getInstance().success("Module \"" + module.getModuleFileName()
                                + "\" was successfully loaded and checked.");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(), "IO access problem");
                        } catch (IllegalArgumentException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(), "unknown reason");
                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Trace.trace(PmiiController.this, METHOD, (new File(ex.getAddress()))
                                .getAbsolutePath() + ":" + ex.getRow() + ":"
                                + ex.getColumn() + ":");
                            Trace.trace(PmiiController.this, METHOD, ex.getDescription());
                            Trace.trace(PmiiController.this, METHOD, ex.getLine());
                            final StringBuffer pointer = Utility.getSpaces(ex.getColumn() - 1);
                            pointer.append("^");
                            Trace.trace(PmiiController.this, METHOD, pointer);
                            Message.getInstance().failure(ex.getMessage(), 
                                (new File(ex.getAddress())).getAbsolutePath() + ":" + ex.getRow() + ":"
                                + ex.getColumn() + ":" + "\n"
                                + ex.getLine());
                        } catch (RuntimeException ex) {
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(), "unknown reason");
                        }
                        Trace.traceEnd(this, METHOD);
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } catch (RuntimeException ex) {
                Trace.trace(PmiiController.this, METHOD, ex);
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }


    /**
     *  Save selected qedeq module file.
     */
    class SaveModuleAction extends AbstractAction {


        /* inherited 
         */
        public final void actionPerformed(final ActionEvent e) {
            final String METHOD = "actionPerformed";
            Trace.traceBegin(this, METHOD);
            try {
                final ModuleProperties prop;
                try {
                    prop = PmiiController.this.treeCtrl.getSelected();
                } catch (NothingSelectedException ex) {
                    Message.getInstance().failure("No local qedeq module selected!", 
                        "one local qedeq module must be selected in the tree");
                    return;
                }
                if (!prop.getModuleAddress().isFileAddress()) {
                    Message.getInstance().failure("No local qedeq module selected!", 
                        "only local qedeq module could be saved");
                    return;
                }
                final Thread thread = new Thread() {
                    public void run() {
                        try {
                            Logger.getInstance().logRequest("Save qedeq module \"" 
                                + prop.getAddress() + "\"");
                            final String edited = PmiiController.this.treeCtrl.getEditedQedeq();
                            ModuleContext.getInstance().removeModuleAndDependents(prop);
                            Utility.saveFile(prop.getModuleAddress().getLocalName(), edited);
                            ModuleContext.getInstance().loadModule(prop.getAddress());
                            final ModuleProperties newProp = ModuleContext.getInstance()
                                .getModuleProperties(prop.getAddress());
                            PmiiController.this.treeCtrl.setSelected(newProp);
                            Message.getInstance().success("Following qede module was saved: \"" 
                                + prop.getAddress() + "\"");
                        } catch (IOException ex) {
                            Trace.trace(PmiiController.this, "actionPerformed", ex);
                            Message.getInstance().failure(ex.getMessage(), "IO access problem");
                        } catch (IllegalArgumentException ex) {
                            Trace.trace(PmiiController.this, "actionPerformed", ex);
                            Message.getInstance().failure(ex.getMessage(), "unknown reason");
                        } catch (ParsingException ex) {
                            Trace.trace(PmiiController.this, "actionPerformed", ex);
                            Trace.trace(PmiiController.this, "actionPerformed", (new File(ex.getAddress()))
                                .getAbsolutePath() + ":" + ex.getRow() + ":"
                                + ex.getColumn() + ":");
                            Trace.trace(PmiiController.this, "actionPerformed", ex.getDescription());
                            Trace.trace(PmiiController.this, "actionPerformed", ex.getLine());
                            final StringBuffer pointer = Utility.getSpaces(ex.getColumn() - 1);
                            pointer.append("^");
                            Trace.trace(PmiiController.this, "actionPerformed", pointer);
                            Message.getInstance().failure(ex.getMessage(), 
                                (new File(ex.getAddress())).getAbsolutePath() + ":" + ex.getRow() + ":"
                                + ex.getColumn() + ":" + "\n"
                                + ex.getLine());
                        } catch (RuntimeException ex) {
                            PmiiController.this.treeCtrl.setSelected(prop);
                            Trace.trace(PmiiController.this, METHOD, ex);
                            Message.getInstance().failure(ex.getMessage(),
                                "unknown reason");
                        }
                    }
                };
                thread.setDaemon(true);
                thread.start();
            } finally {
                Trace.traceEnd(this, METHOD);
            }
        }

    }



    

    /**
     * Add entry to module history. The history is also saved in
     * the config file.
     * 
     * @param   url
     */
    private void addToModuleHistory(final String url) {
        moduleHistory.add(0, url);
        for (int i = 1; i < moduleHistory.size(); i++) {
            if (url.equals(moduleHistory.get(i))) {
                moduleHistory.remove(i);
                i--;
            }
        }
        for (int i = moduleHistory.size() - 1; i >= PmiiController.MAXIMUM_MODULE_HISTORY; i--) {
            moduleHistory.remove(i);
        }
        Settings.getInstance().saveModuleHistory(moduleHistory);
    }

    

}

