package org.artisanlogiciel.games.maze.gui;

import org.artisanlogiciel.games.maze.*;
import org.artisanlogiciel.games.maze.persist.MazePersistRaw;
import org.artisanlogiciel.games.maze.persist.MazePersistWorldEdit;
import org.artisanlogiciel.games.maze.solve.SolvingModel;
import org.artisanlogiciel.games.stl.Maze3dParams;
import org.artisanlogiciel.games.stl.Wall3dStream;
import org.artisanlogiciel.graphics.Drawing;
import org.artisanlogiciel.graphics.SvgWriter;
import org.artisanlogiciel.osm.OsmReader;
import org.artisanlogiciel.osm.convert.OsmToDrawing;
import org.artisanlogiciel.xpm.Xpm;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Locale;

/**
 * Display is Main JFrame for this tool
 **/
public class Display extends JFrame
implements StatusListener
{
    // to please eclipse, not supposed to be serialized
    private static final long serialVersionUID = 8500214871372184418L;


    LabyLayers layers = new LabyLayers();
    int layer = 0;

    MazeComponent maze;
    MazeControler controler;
    LabyModel model;
    boolean autoSize;
    boolean statusEnable = true;
    boolean mGrow = false;

    MazeParams params = null;
    JTextField statusField = null;

    Maze3dSettings stlsettings;

    Display(LabyModel model, int W, int H, MazeParams params) {
        super(MazeDefault.labels.getString("title"));
        if (params != null) {
            // fixedParams = new MazeParamsFixed(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth());
            this.params = params;
        }
        this.model = model;
        maze = createMazeComponent(model, W, H);

        Container con = this.getContentPane();
        JScrollPane scrollableMaze = new JScrollPane(maze);
        con.add(scrollableMaze, BorderLayout.CENTER);
        controler = new MazeControler(params);
        con.add(controler.getMoveControl(), BorderLayout.NORTH);
        con.add(controler.getGenerationControl(), BorderLayout.SOUTH);

        /*
        scrollableMaze.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                if (autoSize) {
                    maze.getAutoSize();
                }
            }
        });
        */


        model.setMazeListener(maze);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(W, H, W, H);
        setVisible(true);
    }

    private MazeComponent createMazeComponent(LabyModel model, int W, int H) {
        MazeCellParameters cp = new MazeCellParameters(model.getWidth(), model.getHeight(), W, H, 0, 0);
        MazeComponent comp = new MazeComponent(model, cp, this);
        Xpm xpm = new Xpm();
        try {
            xpm.parse(new FileInputStream(MazeDefault.getInstance().getXpmBackground()));
            comp.setXpm(xpm);
        }
        catch(Exception e)
        {
            System.err.println("Missing background file " + e);
        }
        return comp;
    }

    void recreateModel() {
        // recreate labyrinth
        if (params != null) {

            // Keep current model to be able to complete a manualy edited one
            maze.resetWallsProvider(model);
            model.setMazeListener(maze);

            model.reset();

            // we are in GUI event thread, need to release it and do it outside.
            new Thread() {
                public void run() {
                    model.generateWithEntry(0, 0);
                    model.addEntryOrExit(-1, 0);
                    model.addEntryOrExit(params.getWidth(), params.getHeight() - 1);
                }
            }.start();
        }
    }

    void setModel( LabyModel model)
    {
        this.model = model;
        layers.addLabyModel(layer, model);
    }

    void resetModel() {
        // recreate labyrinth
        if (params != null) {
            params = controler.getSettings().resetParams();
            setModel(new LabyModel(params));
            refresh();
        }
    }

    void refresh()
    {
        // reinit labyrinth view
        if (params != null) {
            maze.resetWallsProvider(model);
            model.setMazeListener(maze);

            // we are in GUI event thread, need to release it and do it outside.
            new Thread() {
                public void run() {
                    maze.changed(null, null, model);
                }
            }.start();
        }
    }

    void resolve() {

        SolvingModel solving = new SolvingModel(model);
        // should transform current model to be a SolvingModel
        model = solving;
        model.reset();
        solving.resolve(solving.getWidth() - 1, solving.getHeight() - 1, maze);
    }

    boolean reverse(boolean grow) {
        boolean done = model.reverse(grow);
        if ( done ) {
            refresh();
        }
        return done;
    }

    void goNorth() {
        maze.goNorth();
    }

    void goSouth() {
        maze.goSouth();
    }

    void goEast() {
        maze.goEast();
    }

    void goWest() {
        maze.goWest();
    }

    void setWallSize(int size) {
        maze.setWallSize(size);
    }

    void setAutoSize(boolean autoSize) {
        this.autoSize = autoSize;
        if (autoSize) {
            maze.getAutoSize();
        }
    }



    // to allow to log / write somewher into screen...
    void writeSentence(String pSentence) {
        // TODO
        addStatus(pSentence);
    }

    void writeError(String pError) {
        System.err.println(pError);
    }

    public Drawing createDrawing() {
        return new DrawingGenerator(model).createDrawing();
    }

    void saveImc() {
        Drawing d = createDrawing();

        if (d != null) {
            File outfile = getFileForExtension("imc");
            writeSentence("Saving to " + outfile + " ...");
            try {
                DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile));
                d.saveLinesKompressed(out);
                out.flush();
                out.close();
                writeSentence("... Done.");
            } catch (IOException io) {
                io.printStackTrace(System.err);
            }
        }
    }

    File getFileForExtension(final String extension)
    {
        return new File(params.getSaveDir(), params.getName() + "." + extension);
    }

    void saveSvg() {
        Drawing d = createDrawing();

        if (d != null) {
            File outfile = getFileForExtension("svg");
            writeSentence("Saving to " + outfile + " ...");
            try {
                DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile));
                SvgWriter writer = new SvgWriter(d.getInternLines());
                writer.writeTo(out);
                out.flush();
                out.close();
                writeSentence("... Done.");
            } catch (IOException io) {
                io.printStackTrace(System.err);
            }
        } else {
            writeError("drawing creation failed");
        }
    }


    void savePng() {
        File file = getFileForExtension("png");
        // BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_ARGB);
        BufferedImage bi = new BufferedImage(maze.getSize().width, maze.getSize().height, BufferedImage.TYPE_INT_ARGB);
        Graphics g = bi.createGraphics();
        // this.paint(g);
        maze.paint(g);
        g.dispose();
        try {
            ImageIO.write(bi, "png", file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void saveText() {
        File outfile = getFileForExtension("txt");
        writeSentence("Saving to " + outfile + " ...");
        try {
            DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile));
            out.write(model.toLabyMap().toString().getBytes());
            out.flush();
            out.close();
            writeSentence("... Done.");
        } catch (IOException io) {
            io.printStackTrace(System.err);
        }
    }


    public void addStatus(String pStatus)
    {
        if ( statusEnable ) {
            statusField.setText(pStatus);
        }
    }


    private class MazeControler extends JPanel {
        /**
         *
         */
        private static final long serialVersionUID = 1L;

        private MazeSettings settings = null;

        private void setMazeName(String pName) {
            MazeParamsFixed p = (MazeParamsFixed) params;
            p.setName(pName);
        }

        private JMenu createLoadingMenu() {
            JMenu loadMenu = new JMenu(MazeDefault.labels.getString("load") );

            final JTextField loadName = new JTextField("newlaby");

            loadMenu.add(loadName);

            Action loadRawAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("load raw");

                    String filename = loadName.getText();

                    if ((filename.length() > 0)) {
                        setMazeName(filename);
                        loadRaw();
                        refresh();
                    }

                }
            };
            JButton loadRawButton = new JButton(MazeDefault.labels.getString("load" ) + " raw");
            loadRawButton.addActionListener(loadRawAction);
            loadMenu.add(loadRawButton);

            Action loadImcAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("load Imc");

                    String filename = loadName.getText();

                    if ((filename.length() > 0)) {
                        setMazeName(filename);
                        loadImc(false);
                        refresh();
                    }

                }
            };
            JButton loadImcButton = new JButton(MazeDefault.labels.getString("load" ) + " imc");
            loadImcButton.addActionListener(loadImcAction);

            loadMenu.add(loadImcButton);

            Action loadDrawingAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("load Drawing");

                    String filename = loadName.getText();

                    if ((filename.length() > 0)) {
                        setMazeName(filename);
                        loadDrawing(false);
                        refresh();
                    }

                }
            };
            JButton loadDrawingButton = new JButton(MazeDefault.labels.getString("load" ) + " Drawing");
            loadDrawingButton.addActionListener(loadDrawingAction);

            loadMenu.add(loadDrawingButton);

            Action loadOsmAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("load Osm");

                    String filename = loadName.getText();

                    if ((filename.length() > 0)) {
                        setMazeName(filename);
                        // UTM 32T ? approx (long,lat) to meter
                        loadOsm(false, 78000,78000);
                        refresh();
                    }

                }
            };
            loadMenu.add(newLoadButton("osm", loadOsmAction));

            loadMenu.add(newLoadButton("we",
                    new AbstractAction() {
                        public void actionPerformed(ActionEvent evt) {
                            //
                            addStatus("load WoldEdit");

                            String filename = loadName.getText();

                            if ((filename.length() > 0)) {
                                setMazeName(filename);
                                loadWorldEdit(false);
                                refresh();
                            }

                        }
                    }
                    ));
            return loadMenu;
        }

        private JMenu createSavingMenu() {
            final JTextField saveName = new JTextField("newlaby");

            final JButton savePngButton = newSaveButton( "png",
                new AbstractAction() {
                    public void actionPerformed(ActionEvent evt) {
                    //
                        addStatus("save png");
                        setMazeName(saveName.getText());
                        savePng();
                    }
                });

            final JButton saveSvgButton = newSaveButton("svg",
                new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    writeSentence("save svg");
                    setMazeName(saveName.getText());
                    saveSvg();
                }
            });

            final JButton saveButton = newSaveButton("raw",
                new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    setMazeName(saveName.getText());
                    MazeParamsFixed p = (MazeParamsFixed) params;
                    save(p, model);
                }
            });

            Action saveImcAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    setMazeName(saveName.getText());
                    saveImc();
                }
            };
            final JButton saveImcButton = newSaveButton("imc", saveImcAction);

            Action saveStlAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    setMazeName(saveName.getText());
                    MazeParamsFixed p = (MazeParamsFixed) params;
                    saveStl(p, model,stlsettings.createParams());
                }
            };
            final JButton saveStlButton = newSaveButton("stl", saveStlAction);

            Action saveTextAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    setMazeName(saveName.getText());
                    saveText();
                }
            };
            final JButton saveTextButton = newSaveButton("txt", saveTextAction);

            final JButton saveWorleEditButton = newSaveButton( "we",
                    new AbstractAction() {
                        public void actionPerformed(ActionEvent evt) {
                            setMazeName(saveName.getText());
                            saveWorldEdit();
                        }
                    });

            stlsettings = new Maze3dSettings(new Maze3dParams());

            JMenu saveMenu = new JMenu(MazeDefault.labels.getString("save") );
            saveMenu.add(saveName);
            saveMenu.add(saveSvgButton);
            saveMenu.add(savePngButton);
            saveMenu.add(saveButton);
            saveMenu.add(stlsettings);
            saveMenu.add(saveStlButton);
            saveMenu.add(saveImcButton);
            saveMenu.add(saveTextButton);
            saveMenu.add(saveWorleEditButton);

            return saveMenu;

        }

        private JButton newActionButton(String actionName, String format, Action action) {
            final JButton saveTextButton = new JButton(MazeDefault.labels.getString(actionName) + " " + format);
            saveTextButton.addActionListener(action);
            return saveTextButton;
        }

        private JButton newSaveButton(String format, Action action) {
            return newActionButton("save",format,action);
        }

        private JButton newLoadButton(String format, Action action) {
            return newActionButton("load",format,action);
        }

        private JPanel createResolveQuitBar() {
            JPanel resolveQuitBar = new JPanel(new FlowLayout());

            JButton buttonCreate = new JButton(MazeDefault.labels.getString("create"));
            buttonCreate.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    recreateModel();
                }
            });
            resolveQuitBar.add(buttonCreate);

            JButton resolveButton = new JButton(MazeDefault.labels.getString("resolve"));
            resolveButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("Resolving");
                    resolve();
                }
            });

            resolveQuitBar.add(resolveButton);

            JButton reverseButton = new JButton(MazeDefault.labels.getString("reverse"));
            reverseButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("Reversing");
                    mGrow ^= reverse(mGrow);
                }
            });

            resolveQuitBar.add(reverseButton);


            final JButton quitButton = new JButton(MazeDefault.labels.getString("quit"));
            Action quitAction = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("quit");
                    System.exit(0);
                }
            };
            quitButton.addActionListener(quitAction);

            resolveQuitBar.add(quitButton);

            JButton buttonReset = new JButton("reset");//MazeDefault.labels.getString("reset"));
            buttonReset.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    resetModel();
                }
            });
            resolveQuitBar.add(buttonReset);

            return resolveQuitBar;

        }

        public MazeControler(MazeParams params) {
            // super(new BorderLayout());
            super();
            BoxLayout layout = new BoxLayout(this, BoxLayout.Y_AXIS);
            setLayout(layout);

            JMenuBar menuBar = new JMenuBar();
            menuBar.add(createSavingMenu());
            menuBar.add(createLoadingMenu());
            add(menuBar);

            add(createResolveQuitBar());

            JPanel controlMovesPanel = new JPanel(new BorderLayout());
            settings = new MazeSettings(params);
            @SuppressWarnings("serial")
            Action goNorth = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("go North");
                    goNorth();
                }
            };
            JButton north = addDirection(this, MazeDefault.labels.getString("north"), "UP", goNorth);

            @SuppressWarnings("serial")
            Action goEast = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("go East");
                    goEast();
                }
            };

            JButton east = addDirection(this, MazeDefault.labels.getString("east"), "RIGHT", goEast);

            @SuppressWarnings("serial")
            Action goWest = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("go West");
                    goWest();
                }
            };
            JButton west = addDirection(this, MazeDefault.labels.getString("west"), "LEFT", goWest);

            @SuppressWarnings("serial")
            Action goSouth = new AbstractAction() {
                public void actionPerformed(ActionEvent evt) {
                    //
                    addStatus("go South");
                    goSouth();
                }
            };
            JButton south = addDirection(this, MazeDefault.labels.getString("south"), "DOWN", goSouth);

            controlMovesPanel.add(north, BorderLayout.NORTH);
            controlMovesPanel.add(west, BorderLayout.WEST);
            controlMovesPanel.add(east, BorderLayout.EAST);
            controlMovesPanel.add(south, BorderLayout.SOUTH);

            JPanel controlPanel = new JPanel();

            controlPanel.add(controlMovesPanel,BorderLayout.LINE_START);
            statusField = new JTextField("",20);
            controlPanel.add(statusField, BorderLayout.CENTER);
            add(controlPanel, BorderLayout.NORTH);

            // control display panel contains controls for display
            final JPanel controlDisplayPanel = new JPanel(new BorderLayout());

            add(controlDisplayPanel);
            // zoom , autozoom control
            add(buildResizeControl());
            // layers control
            add(buildLayerControl());
            add(settings);

        }

        private JPanel buildResizeControl()
        {
            final JCheckBox showAll = new JCheckBox("show all");
            showAll.addChangeListener(new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    maze.setShowAll(showAll.isSelected());
                }
            });

            final JSlider slider = new JSlider(2, 40);
            slider.addChangeListener(new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    setWallSize(slider.getValue());
                }
            });
            final JCheckBox autoSlide = new JCheckBox("autoslide");
            autoSlide.addChangeListener(new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    setAutoSize(autoSlide.isSelected());
                }
            });

            JPanel resizecontrol = new JPanel(new FlowLayout());
            resizecontrol.add(showAll);
            resizecontrol.add(slider);
            resizecontrol.add(autoSlide);

            return resizecontrol;

        }

        private JPanel buildLayerControl()
        {

            int layers = 10;

            final JTextField layersText = new JTextField("" + layers);

            final JSlider layerSlide = new JSlider( JSlider.HORIZONTAL, 0,layers, 0);
            layerSlide.addChangeListener(new ChangeListener() {
                public void stateChanged(ChangeEvent e) {
                    int r = setLayer(layerSlide.getValue());
                    //layerSlide.setMaximum(r);
                }
            });
            layerSlide.setMajorTickSpacing(1);
            layerSlide.setPaintTicks(true);

            JPanel layerControl = new JPanel(new FlowLayout());
            layerControl.add(new JLabel("layers"));
            layerControl.add(layersText);
            layerControl.add(layerSlide);

            return layerControl;

        }

        private JPanel getMoveControl() {
            return this;
        }

        private JPanel getGenerationControl() {
            return settings;
        }

        public MazeSettings getSettings() {
            return settings;
        }

    }

    private void loadRaw() {
        File infile = new File(params.getSaveDir(), params.getName() + ".raw");
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream(infile);
            setModel(new MazePersistRaw().parseInputStream("raw",inputStream));
        } catch (IOException io) {
            io.printStackTrace(System.err);
            statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath());
        }
        finally
        {
            if (inputStream != null )
            {
                // cleanup
                try {
                    inputStream.close();
                }
                catch (Exception any)
                {
                    // don't care really
                }
            }
        }
    }

    private void loadOsm(boolean add, int mulx, int muly) {
        if ( maze != null ) {
            new Thread() {
                @Override
                public void run() {
                    File infile = new File(params.getSaveDir(), params.getName() + ".osm");
                    FileInputStream inputStream = null;
                    try {
                        // TODO really use InputStream and not pathname
                        OsmReader reader = new OsmReader(infile.getCanonicalPath());
                        reader.read();
                        OsmToDrawing converter = new OsmToDrawing(reader, mulx,muly);
                        Drawing drawing = converter.getDrawing(reader.getWays());
                        statusEnable = false;
                        maze.addDrawing(drawing,add);
                        statusEnable = true;
                    } catch (IOException io) {
                        io.printStackTrace(System.err);
                        statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath());
                    } finally {
                        if (inputStream != null) {
                            // cleanup
                            try {
                                inputStream.close();
                            } catch (Exception any) {
                                // don't care really
                            }
                        }
                    }
                }
            }.start();
        }
    }

    private void loadImc(boolean add) {
        if ( maze != null ) {
            new Thread() {
                @Override
                public void run() {
                    File infile = new File(params.getSaveDir(), params.getName() + ".imc");
                    FileInputStream inputStream = null;
                    try {
                        inputStream = new FileInputStream(infile);
                        // TODO
                        // model = new MazePersistRaw().parseInputStream("raw",inputStream);
                        Drawing drawing = new Drawing();
                        drawing.loadLinesExpanded(new DataInputStream(inputStream));
                        statusEnable = false;
                        maze.addDrawing(drawing,add);
                        statusEnable = true;
                    } catch (IOException io) {
                        io.printStackTrace(System.err);
                        statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath());
                    } finally {
                        if (inputStream != null) {
                            // cleanup
                            try {
                                inputStream.close();
                            } catch (Exception any) {
                                // don't care really
                            }
                        }
                    }
                }
            }.start();
        }
    }

    private void loadDrawing(boolean add) {
        if ( maze != null ) {
            new Thread() {
                @Override
                public void run() {
                    File infile = new File(params.getSaveDir(), params.getName() + ".drawing");
                    FileInputStream inputStream = null;
                    try {
                        inputStream = new FileInputStream(infile);
                        // TODO
                        // model = new MazePersistRaw().parseInputStream("raw",inputStream);
                        Drawing drawing = new Drawing();
                        drawing.loadLines(new DataInputStream(inputStream));
                        statusEnable = false;
                        maze.addDrawing(drawing,add);
                        statusEnable = true;
                    } catch (IOException io) {
                        io.printStackTrace(System.err);
                        statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath());
                    } finally {
                        if (inputStream != null) {
                            // cleanup
                            try {
                                inputStream.close();
                            } catch (Exception any) {
                                // don't care really
                            }
                        }
                    }
                }
            }.start();
        }
    }

    private void loadWorldEdit(boolean add) {
        if ( maze != null ) {
                    File infile = new File(params.getSaveDir(), params.getName() + ".we");
                    FileInputStream inputStream = null;
                    try {
                        inputStream = new FileInputStream(infile);
                        LabyLayers newLayers = new MazePersistWorldEdit().parseInputStream("we",inputStream);
                        if ( ! newLayers.isEmpty()) {
                            int l = layer;
                            for (int i = newLayers.getMin(); i <= newLayers.getMax(); i++) {
                                LabyModel m = newLayers.getLayer(i);
                                if (m != null) {
                                    System.out.println("add layer " + l);
                                    layers.addLabyModel(l, m);
                                    l++;
                                }
                            }
                            setModel(layers.getLayer(layer));
                        }
                    } catch (IOException io) {
                        io.printStackTrace(System.err);
                        statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath());
                    } finally {
                        if (inputStream != null) {
                            // cleanup
                            try {
                                inputStream.close();
                            } catch (Exception any) {
                                // don't care really
                            }
                        }
                    }
        }
    }

    private JButton addDirection(final JPanel panel, final String direction, String key, Action goAction) {
        String actionName = "go" + direction;
        JButton button = new JButton(direction);
        button.addActionListener(goAction);
        KeyStroke keystroke = KeyStroke.getKeyStroke(key);
        // attach keys to maze
        maze.getInputMap().put(keystroke, actionName);
        maze.getActionMap().put(actionName, goAction);
        return button;
    }

    private static void setupDisplay(LabyModel model, int W, int H, MazeParams params) {
        Display display = new Display(model, W, H, params);
    }

    private void saveWorldEdit() {
        File outfile = getFileForExtension("we");
        if (!outfile.exists()) {
            addStatus("Saving we to " + outfile + " ...");
            try {
                FileOutputStream out = new FileOutputStream(outfile);
                new MazePersistWorldEdit(model).streamOut("we", out);
                out.close();
                addStatus("... Done.");
            } catch (IOException io) {
                io.printStackTrace(System.err);
            }
        } else {
            addStatus("we file " + outfile + " already exists");
        }

    }

    public void saveStl(MazeParamsFixed params, LabyModel model, Maze3dParams wallparams) {
        File outfile = getFileForExtension("stl");
        if (!outfile.exists()) {
            addStatus("Saving stl to " + outfile + " ...");
            try {
                FileOutputStream out = new FileOutputStream(outfile);
                new Wall3dStream(params.getName(), model, out, wallparams).stream();
                out.close();
                addStatus("... Done.");
            } catch (IOException io) {
                io.printStackTrace(System.err);
            }
        } else {
            addStatus("stl file " + outfile + " already exists");
        }

    }

    public void save(MazeParamsFixed params, LabyModel model) {
        File outfile = getFileForExtension("raw");
        if (!outfile.exists()) {
            addStatus("Saving to " + outfile + " ...");
            try {
                FileOutputStream out = new FileOutputStream(outfile);
                MazePersistRaw persist = new MazePersistRaw(model);
                persist.streamOut("raw", out);
                out.flush();
                out.close();
                addStatus("... Done.");
            } catch (IOException io) {
                io.printStackTrace(System.err);
            }
        } else {
            addStatus("" + outfile + " already exists");
        }
    }

    private int setLayer(int x)
    {
        addStatus("set layer " + x);
        LabyModel layermodel = layers.getLayer(x);
        if ( layermodel == null )
        {
            // clone it
            model = new LabyModel(model);
            layers.addLabyModel(x, model);
        }
        else
        {
            model = layermodel;
        }
        layer = x;
        refresh();
        return layer;
    }

    public static void main(String pArgs[]) {
        LabyModel model = null;
        int W = 600;
        int H = 400;

        System.out.println("Default Locale " + Locale.getDefault());

        if ((pArgs.length > 0) && (pArgs[0].length() > 0)) {
            try {
                model = new MazePersistRaw().parseInputStream("raw",new FileInputStream(pArgs[0]));
            } catch (IOException io) {
                io.printStackTrace(System.err);
                System.exit(1);
            }

            setupDisplay(model, W, H, null);
        } else {
            MazeParamsFixed params = new MazeParamsFixed(MazeDefault.getInstance().getParams());
            model = new LabyModel(params);

            setupDisplay(model, W, H, params);

	    /*
            model.generateWithEntry(0, 0);

            model.addEntryOrExit(-1, 0);
            model.addEntryOrExit(params.getWidth(), params.getHeight() - 1);

            addStatus("Generation completed");
	    */
            /*
             */
        }

    }
}
