package de.renew.netcomponents;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.net.URL;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Vector;
import javax.swing.AbstractButton;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;

import CH.ifa.draw.io.DrawingFileHelper;
import CH.ifa.draw.io.StatusDisplayer;
import CH.ifa.draw.standard.StandardDrawingView;
import CH.ifa.draw.standard.ToolButton;
import CH.ifa.draw.util.DrawingHelper;
import CH.ifa.draw.util.Palette;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.draw.ui.ontology.DrawingEditor;
import de.renew.draw.ui.ontology.Tool;
import de.renew.gui.GuiPlugin;
import de.renew.util.StringUtil;
import de.renew.windowmanagement.Workbench;


/**
 * ComponentsTool.java
 * <p>
 * The ComponentsTool extends the usual functionality of the CPNApplication by
 * adding a new palette to the gui. It reads the drawings - which hold the
 * Net-Components - from the ".../tools" directory without opening them.
 * Instead, the figures will be inserted into the current drawing when using the tools.
 * To create the palette there have to be - in addition to each tool drawing in
 * the ".../tools" directory - also three gif images located in
 * ".../tools/images" directory. If not a blank image will be associated with
 * the net-component
 * </p>
 * <pre>
 * 0.3 Now sequence is determined by the &quot;.../tools/.sequence&quot; file
 * 0.4 The tools directory is now determined by the user property &quot;user.toolsdir&quot;
 *     which defines the parent directory  of tools. So the tools are now in
 *     user.toolsdir+&quot;tools/&quot;. If no user.toolsdir is specified the current
 *     directory is parent directory to tools.
 * 0.5 The ComponentsTool has now been adapted to the plugin architecture of renew.
 * 0.6 Label added, which consists in the tools-directory path, so far.
 *     The ComponentsTool can now remove itself from the toolsPanel.
 *     toString method added which displays the Label.
 * 0.7 loadDrawing changed to loadDrawingWithoutGUI (DrawApplication)
 * 0.8 remove detached palette solved
 * 0.9 fixed generic image substitution
 * 0.10 changed signature of createTools (added Name of Palette)
 *      changed signature of Constructor and added legacy constructor
 * </pre>
 *
 * @author Lawrence Cabac
 * @version 0.10, December 2006
 */
public class ComponentsTool implements StatusDisplayer {
    /**
     * The log4j logger.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(ComponentsTool.class);

    /**
     * The toolsdir property name.
     */
    protected static final String TOOLDIRPROPERTY = "user.toolsdir";

    /**
     * The panel that contains the palettes / ComponentsTools.
     */


    // private Panel toolPanel;

    /**
     * The menuFrame of the CPN application / Renew GUI.
     */


    // private Frame menuFrame;

    /**
     * The CPN application / Renew GUI
     */


    // private CPNApplication cpnapp;

    /**
     * The palette that holds the tool buttons of this ComponentsTool.
     */
    private Palette _palette;

    /**
     * The signifier of this ComponentsTool.
     */
    private String _label;

    /**
     * Shows if the given toolsdir directory contains some drawings and button
     * images.
     */
    private boolean _toolsDirCheck = false;

    /**
     * Shows if the given toolsdir directory contains some drawings and button
     * images.
     */
    private boolean _toolsDirIsSet = false;

    /**
     * Constructor for ComponentsTool. It has to know the CPN application. The
     * tools directory is determined by the "user.toolsdir" java commandlind
     * argument.
     *
     * @param workbench Interface which provides different interactions methods for the two
     *                      classes WorkbenchImpl and WorkbenchProxy
     */
    public ComponentsTool(Workbench workbench) {
        // get the toolPanel and the menuFrame from the CPNApplication
        // this.cpnapp = cpnapp;
        // this.toolPanel = cpnapp.getToolsPanel();
        // this.menuFrame = cpnapp.menuFrame();
        // *** still have to decide where to put those
        String dirName = System.getProperty(TOOLDIRPROPERTY);
        LOGGER.debug(dirName);
        if (dirName == null || !new File(dirName).exists()) {
            URL location = ComponentsToolPlugin.getLocation();
            String toolsLoc = new File(location.getFile()).getAbsolutePath();
            toolsLoc = toolsLoc.substring(0, toolsLoc.lastIndexOf(File.separator)) + File.separator
                + "tools";
            if (new File(toolsLoc).exists()) {
                dirName = toolsLoc;
                _toolsDirIsSet = true;
                String statusMessage = "The " + TOOLDIRPROPERTY
                    + " property is not set or not valid. Trying plugin location...";
                GuiPlugin.getCurrent().showStatus(statusMessage);
                LOGGER.debug(statusMessage);
            } else {
                _toolsDirIsSet = false;
                dirName = "";
                LOGGER.info(
                    "Netcomponents: The " + TOOLDIRPROPERTY + " is not set! \n"
                        + "Override by setting the " + TOOLDIRPROPERTY + " property.");

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(
                        "netcomponents: using default tooldir '" + dirName + "'."
                            + "Override by setting the " + TOOLDIRPROPERTY + " property.");
                }
            }
        } else {
            _toolsDirIsSet = true;
        }

        setLabel("default " + dirName);
        createTools(dirName, "Components", null, workbench);
    }

    // public ComponentsTool(String dirName) {
    // new ComponentsTool(dirName, dirName,null);
    // }

    /**
     * Constructor for ComponentsTool. It has to know the CPN application and
     * the path name of the tools directory as a String.
     *
     * @param dirName -
     *                the path name of the tools' directory (absolut)
     * @param paletteName -
     *                    the name of the palette to be created
     * @param plugin -
     *               the plugin that offers the components
     * @param workbench -
     *                an interface which provides different interactions methods for the two
     *                classes (WorkbenchImpl and WorkbenchProxy)
     */
    public ComponentsTool(
        String dirName, String paletteName, ComponentsPluginExtender plugin, Workbench workbench)
    {
        // get the toolPanel and the menuFrame from the CPNApplication
        // this.cpnapp = cpnapp;
        // this.toolPanel = cpnapp.getToolsPanel();
        // this.menuFrame = cpnapp.menuFrame();
        // this._plugin = plugin;
        _toolsDirIsSet = true;
        setLabel(dirName);
        createTools(dirName, paletteName, plugin, workbench);
    }

    /**
     * Method createTools. Adds the Renew Drawings of the specified dirctory
     * into this ComponentsTool, adds the images from "[user.toolsdir]/images"
     * to the palette sorted as specified in "[user.toolsdir]/.sequence" and
     * adds the palette to the toolsPanel.
     *
     * @param dirName -
     *                the tools directory name.
     *
     * TODO: remove workbench, it´s unnecessary
     */
    void createTools(
        String dirName, String paletteName, ComponentsPluginExtender plugin, Workbench workbench)
    {
        // create tools for Net-Components
        GuiPlugin starter = GuiPlugin.getCurrent();
        if (starter == null) {
            LOGGER.error("ComponentsPlugin: no GuiPlugin available!");
            //NOTICEnull add return statement since the following code would crash
            return;
        }
        //        JFrame frame = starter.getGuiFrame();
        //        if (frame == null) {
        //            logger.error("NetComponents: could not create Tools: no GUI available.");
        //        }
        _palette = new Palette(paletteName);

        LinkedHashMap<AbstractButton, Component> toolsList = new LinkedHashMap();
        File dir = new File(dirName);
        File imageDir = new File(dirName + File.separator + "images");


        // read the sequence of the buttons in the palette
        Vector<String> vSeq = new Vector<>();

        try {
            BufferedReader seq =
                new BufferedReader(new FileReader(dirName + File.separator + ".sequence"));

            while (seq.ready()) {
                vSeq.add(seq.readLine());
            }

            seq.close();
        } catch (Exception e) {
            LOGGER.error(
                "The File" + dirName + File.separator + ".sequence" + " could not be found " + e);
        }

        // check if "tools" directory exists
        if (dir.isDirectory()) {
            if (imageDir.isDirectory()) {
                // add all renew drawings that are not in sequence list
                String[] fileNames = dir.list();

                for (int i = 0; i < fileNames.length; i++) {
                    String s = StringUtil.getFilename(fileNames[i]);
                    String ext = StringUtil.getExtension(fileNames[i]);

                    // check if file is a renew drawing
                    if ((new File(dirName + File.separator + fileNames[i])).isFile()
                        && "rnw".equals(ext)) {
                        if (!vSeq.contains(s)) {
                            vSeq.add(s);
                        }
                    }
                }
                if ((new File(dirName + File.separator + "images" + File.separator + "generic.gif"))
                    .exists()) {
                    Iterator<String> it = vSeq.iterator();

                    while (it.hasNext()) {
                        String toolName = it.next();
                        String fileName = toolName + ".rnw";

                        Drawing drawing =
                            DrawingFileHelper.loadDrawing(new File(dirName, fileName), this);
                        Drawing drawing2 = null;

                        // We have to clone the drawing, because we will later move the figures to fit onto the screen.
                        try {
                            drawing2 = DrawingHelper.cloneDrawing(drawing);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        ToolButton button = createToolButton(toolName, dirName, starter, drawing2);

                        if (button != null) {
                            _palette.add(button);

                            FigureEnumeration figures = drawing.figures();
                            Vector<Figure> figureVector = new Vector<Figure>();
                            Figure figure;
                            Point center;
                            Rectangle displayBox;
                            int upperBounds = 0;
                            int leftBounds = 0;

                            while (figures.hasMoreElements()) {
                                figure = figures.nextFigure();
                                center = figure.center();
                                displayBox = figure.displayBox();

                                int newUpperBounds = center.y - displayBox.height / 2;
                                int newLeftBounds = center.x - displayBox.width / 2;

                                if (newUpperBounds < upperBounds) {
                                    upperBounds = newUpperBounds;
                                }
                                if (newLeftBounds < leftBounds) {
                                    leftBounds = newLeftBounds;
                                }

                                figureVector.add(figure);
                            }

                            StandardDrawingView.moveFigures(
                                figureVector, Math.abs(leftBounds), Math.abs(upperBounds));
                            Dimension d = drawing.defaultSize();
                            BufferedImage image =
                                new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
                            Graphics g = image.createGraphics();
                            drawing.draw(g);

                            JLabel label = new JLabel(new ImageIcon(image));
                            final JScrollPane scrollPane = new JScrollPane(
                                label, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
                            scrollPane.getViewport()
                                .setScrollMode(javax.swing.JViewport.BLIT_SCROLL_MODE);
                            scrollPane.getVerticalScrollBar().setUnitIncrement(20);
                            scrollPane.getHorizontalScrollBar().setUnitIncrement(20);
                            scrollPane.setPreferredSize(new Dimension(300, 300));

                            button.addAdditionalComponent(scrollPane);


                            //toolsList.put(button.button(), scrollPane);
                        }

                        // at least one drawing is loaded, so we presume that the
                        // directory is a tools directory
                        _toolsDirCheck = true;
                    }

                    // try to add additional buttons
                    if (plugin != null) {
                        Vector<ToolButton> additionalButtons = plugin.getAdditionalButtons();
                        if (additionalButtons != null) {
                            Iterator<ToolButton> ite = additionalButtons.iterator();
                            while (ite.hasNext()) {
                                ToolButton toolButton = ite.next();
                                _palette.add(toolButton);
                            }
                        }
                    }

                    // while it has next
                    // do not bother if no tool is added.
                    if (toolsDirIsValid()) {
                        starter.getPaletteHolder().addPalette(_palette);

                        //workbench.registerToolbarItems(paletteName,toolsList,true);
                    }
                } // end of if "generic.gif"
                else {
                    LOGGER.error(
                        "The directory " + dirName + File.separator + "images seems to exist");
                    LOGGER.error("but it does not contain the files generic.gif");
                    LOGGER.error(
                        "So I think that the toolsdir variable is not set to a proper toolsdir directory");
                    LOGGER.error("or the selected directory does not match the requirements.");
                    LOGGER.error("A proper toolsdir directory contains the images directory  ");
                    LOGGER.error(
                        "which contains at least the file generic.gif as fallback and a .sequence file.");
                    LOGGER.error("");
                }
            } // images dir exists
            else {
                LOGGER
                    .error("The directory " + dirName + File.separator + "images does not exist!");
            }
        } // tools dir exists
        else {
            LOGGER.error("The directory " + dirName + " does not exist!");
        }
    }

    private ToolButton createToolButton(
        String toolName, String dirName, GuiPlugin starter, Drawing drawing)
    {
        Tool tool;
        String fileName = toolName + ".rnw";
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(ComponentsTool.class.getName() + ": dirName: " + dirName);
            LOGGER.debug(ComponentsTool.class.getName() + ": fileName: " + fileName);
        }

        if (drawing == null) {
            return null;
        }
        tool = createInsertionTool(starter, drawing);

        // String toolName = StringUtil.getFilename(fileName);
        String imageName = "";

        // check Images
        // in case of missing gifs use default, plain button
        // generic (old was: 1.gif 2.gif 3.gif)
        String toolBase = dirName + File.separator + "images" + File.separator;
        File imageFile = (new File(toolBase + toolName + ".gif"));
        File selFile = new File(toolBase + toolName + ".gif");
        if (imageFile.exists() && selFile.exists()) {
            imageName = toolName;
        } else {
            imageName = "generic";
            LOGGER.warn(
                "File does not exist: " + imageFile.getAbsolutePath()
                    + " trying to load generic.gif instead.");
        }

        ToolButton button = starter.getPaletteHolder()
            .createToolButton(toolBase + imageName, "NC " + toolName, tool);
        return button;
    }

    private Tool createInsertionTool(GuiPlugin starter, Drawing drawing) {
        DrawingEditor editor = starter.getDrawingEditor();
        Tool tool;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("ComponentsTool: " + drawing);
        }

        // have to put all the figures each by each into the Vector
        // any other way? ****
        FigureEnumeration fe = drawing.figures();
        Vector<Figure> v = new Vector<Figure>();

        while (fe.hasMoreElements()) {
            v.addElement(fe.nextElement());
        }

        tool = new InsertionTool(editor, v);
        return tool;
    }

    // createTools

    /**
     * Method remove. Removes the ComponentsTool form the toolsPanel.
     */
    public void remove() {
        GuiPlugin starter = GuiPlugin.getCurrent();
        if (starter != null) {
            starter.getPaletteHolder().removePalette(_palette);
        }
    }

    /**
     * Setter-Method for Label. Method sets the private String label to the given String s.
     *
     * @param s The string which would be set as the label
     */
    private void setLabel(String s) {
        _label = s;
    }

    /**
     * Gets the label, which is the signifier of this ComponentsTool.
     *
     * @return the label, which is the signifier of this ComponentsTool.
     */
    public String getLabel() {
        return _label;
    }

    @Override
    public String toString() {
        return getLabel();
    }

    /**
     * Returns a boolean if the tools directory is set.
     *
     * @return boolean if the tools directory is set.
     */
    public boolean toolsDirIsSet() {
        return _toolsDirIsSet;
    }

    /**
     * Returns if the tools directory is valid.
     *
     * @return boolean if the tools directory is valid.
     */
    public boolean toolsDirIsValid() {
        return _toolsDirCheck;
    }

    @Override
    public void showStatus(String message) {
        LOGGER.warn(ComponentsTool.class.getSimpleName() + ": A file could not be loaded.");
        LOGGER.warn(
            ComponentsTool.class.getSimpleName() + ": Turn on debug to display more information.");
        LOGGER.warn(
            ComponentsTool.class.getSimpleName()
                + ": To do so add line log4j.logger.de.renew.netcomponents.ComponentsTool=DEBUG");
        LOGGER.warn(
            ComponentsTool.class.getSimpleName()
                + ": to your log4j configuration in e.g. ~/.log4j.properties.");
        LOGGER.debug(message);
    }
}