package org.artisanlogiciel.games.maze.gui;

import org.artisanlogiciel.games.maze.*;
import org.artisanlogiciel.games.maze.model.WidthHeightProvider;
import org.artisanlogiciel.games.maze.persist.MazePersistRaw;
import org.artisanlogiciel.games.maze.solve.SolvingModel;
import org.artisanlogiciel.games.minetest.net.MiM;
import org.artisanlogiciel.graphics.Drawing;
import org.artisanlogiciel.xpm.Xpm;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Locale;

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

    MazeComponent maze;
    MazeControler controler;

    boolean autoSize;

    public boolean isStatusEnable() {
        return statusEnable;
    }

    public void setStatusEnable(boolean statusEnable) {
        this.statusEnable = statusEnable;
    }

    public void addDrawing(Drawing drawing, boolean add)
    {
        if (maze != null)
        {
            maze.addDrawing(drawing,add);
        }
    }

    boolean statusEnable = true;
    boolean mGrow = false;


    JTextField statusField = null;

    Maze3dSettings stlsettings;

    MazeFrame mazeFrame;

    WidthHeightProvider frameSize = null;

    public void setHexagon(boolean hexagon) {
        addStatus(hexagon ? "hexagon" : "square");
        maze.resetCellRenderer(hexagon, frameSize);
    }

    public void setBackground(boolean background) {
        addStatus(background ? "view background" : "hide background");
        maze.setViewBackground(background);
    }

    private class MazeFrame extends JFrame
    {
        MazeFrame(LabyModel model, WidthHeightProvider frame, MazeParams params) {
            super(MazeDefault.labels.getString("title"));
            frameSize = frame;
            maze = createMazeComponent(model, frame);

            Container con = this.getContentPane();
            JScrollPane scrollableMaze = new JScrollPane(maze);
            con.add(scrollableMaze, BorderLayout.CENTER);
            controler = new MazeControler(Display.this, params);
            con.add(controler.getMoveControl(), BorderLayout.NORTH);
        /*
        scrollableMaze.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                if (autoSize) {
                    maze.getAutoSize();
                }
            }
        });
        */


            model.setMazeListener(maze);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setBounds(frame.getWidth(), frame.getHeight(), frame.getWidth(), frame.getHeight());
            setVisible(true);
        }

    }

    public Display(LabyModel model, WidthHeightProvider frame, MazeParams params)
    {
        super(model);
        mazeFrame = new MazeFrame(model, frame,params);
        if (params != null) {
            // fixedParams = new MazeParamsFixed(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth());
            this.params = params;
        }
    }

    private MazeComponent createMazeComponent(LabyModel model, WidthHeightProvider frame) {

        MazeComponent comp = new MazeComponent(model, frame , 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 resetModel() {
        // recreate labyrinth
        if (params != null) {
            params = controler.getSettings().resetParams();
            setModel(new LabyModel(params));
            refresh();
        }
    }

    @Override
    protected 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.goTo(Brick.UP);
    }

    void goSouth() {
        maze.goTo(Brick.DOWN);
    }

    void goEast() {
        maze.goTo(Brick.RIGHT);
    }

    void goWest() {
        maze.goTo(Brick.LEFT);
    }
    void setWallSize(int size) {
        maze.setWallSize(size);
    }

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

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

    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, WidthHeightProvider frame, MazeParams params) {
        Display display = new Display(model, frame, params);
    }


    /**
     * Not part of generic Maze since using ImageIo and Graphics
     */
    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();
        }
    }

    public static void main(String pArgs[]) {
        LabyModel model = null;
        WidthHeightProvider frame = new WidthHeightProvider() {
            @Override
            public int getWidth() {
                return 600;
            }

            @Override
            public int getHeight() {
                return 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, frame, null);
        } else {
            MazeParamsFixed params = new MazeParamsFixed(MazeDefault.getInstance().getParams());
            model = new LabyModel(params);

            setupDisplay(model, frame, params);

	    /*
            model.generateWithEntry(0, 0);

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

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

    }
}
