import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class GuiInterface extends JComponent implements ActionListener {
    // Note: I don't recommend that you use this as a starting example for other
    // graphics applications. I'm sacrificing a fair amount on programming style
    // for the sake of getting it all to fit into a single class.
    private static final Color EMPTY_COLOR = new Color(0xA0, 0xF0, 0x00);
    private static final Color HAPPY_COLOR = new Color(0x00, 0x80, 0x00);
    private static final Color BURNING_COLOR = new Color(0xD0, 0x50, 0x00);

    private JButton step;
    private JButton reset;

    private double[] prevParms;
    private int rows;
    private int cols;
    private FireSim sim;
    private int[][] prev;

    private GuiInterface(JButton step, JButton reset) {
        this.step = step;
        this.reset = reset;
        this.prevParms = null;
        this.sim = null;
        this.prev = null;

        setPreferredSize(new Dimension(300, 300));
        setBorder(BorderFactory.createEtchedBorder());
        step.addActionListener(this);
        reset.addActionListener(this);
    }

    protected void paintComponent(Graphics g) {
        int width = getWidth();
        int height = getHeight();

        if(sim == null) {
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, width, height);
        } else {
            for(int i = 0; i < rows; i++) {
                for(int j = 0; j < cols; j++) {
                    int x0 = j * width / cols;
                    int x1 = (j + 1) * width / cols;
                    int y0 = i * height / rows;
                    int y1 = (i + 1) * height / rows;
                    int w = x1 - x0;
                    int h = y1 - y0;

                    g.setColor(getColor(sim.getCell(i, j)));
                    g.fillRect(x0, y0, w, h);
                    if(prev != null) {
                        g.setColor(getColor(prev[i][j]));
                        g.fillRect(x0 + w / 4, y0 + h / 4, w / 2, h / 2);
                    }
                }
            }
        }
    }

    private Color getColor(int state) {
        switch(state) {
        case FireSim.EMPTY: return EMPTY_COLOR;
        case FireSim.HAPPY: return HAPPY_COLOR;
        case FireSim.BURNING: return BURNING_COLOR;
        default: return Color.RED;
        }
    }

    public void actionPerformed(ActionEvent event) {
        if(event.getSource() == reset) {
            doReset();
        } else if(event.getSource() == step) {
            saveState();
            sim.step();
            repaint();
        }
    }

    private void doReset() {
        double[] parms = retrieveParameters(this, prevParms);
        prevParms = parms;
        rows = (int) parms[0];
        cols = (int) parms[1];
        sim = new FireSim(rows, cols, parms[2], parms[3], (int) parms[4]);
        prev = null;
        repaint();
    }


    private void saveState() {
        int[][] ret = new int[rows][cols];
        for(int i = 0; i < rows; i++) {
            for(int j = 0; j < cols; j++) ret[i][j] = sim.getCell(i, j);
        }
        prev = ret;
    }

    public static void main(String[] args) {
        JFrame gui = new JFrame("Fire!");
        gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton step = new JButton("Step");
        JButton reset = new JButton("Reset");
        GuiInterface main = new GuiInterface(step, reset);

        JPanel buttons = new JPanel();
        buttons.add(step);
        buttons.add(reset);

        Container contents = gui.getContentPane();
        contents.add(main, BorderLayout.CENTER);
        contents.add(buttons, BorderLayout.SOUTH);

        gui.pack();
        gui.setVisible(true);
        main.doReset();
    }

    private static double[] retrieveParameters(Component parent,
            double[] defaults) {
        GridBagLayout gb = new GridBagLayout();
        JPanel panel = new JPanel(gb);
        GridBagConstraints gbc = new GridBagConstraints();

        gbc.gridwidth = 2;
        gbc.anchor = GridBagConstraints.LINE_START;
        JLabel prompt = new JLabel("Enter the simulation parameters.");
        gb.setConstraints(prompt, gbc);
        panel.add(prompt);

        String[] labels = { "Rows", "Columns", "Empty Probability",
                "Catch Probability", "Burn Life" };
        boolean[] isInt = { true, true, false, false, true };
        JTextField[] fields = new JTextField[labels.length];
        for(int i = 0; i < fields.length; i++) {
            gbc.gridwidth = 1;
            gbc.gridx = 0;
            gbc.gridy = i + 1;
            JLabel label = new JLabel(labels[i] + ": ");
            gb.setConstraints(label, gbc);
            panel.add(label);

            gbc.gridx = 1;
            fields[i] = new JTextField(6);
            if(defaults != null) {
                fields[i].setText(isInt[i] ? "" + (int) defaults[i]
                    : "" + defaults[i]);
            }
            gb.setConstraints(fields[i], gbc);
            panel.add(fields[i]);
        }

        while(true) {
            int action = JOptionPane.showConfirmDialog(parent, panel,
                    "Configure Parameters", JOptionPane.OK_CANCEL_OPTION,
                    JOptionPane.QUESTION_MESSAGE);
            if(action != JOptionPane.OK_OPTION) {
                System.exit(0);
            }

            double[] ret = new double[fields.length];
            boolean accepted = true;
            for(int i = 0; i < ret.length; i++) {
                try {
                    if(isInt[i]) {
                        ret[i] = Integer.parseInt(fields[i].getText());
                    } else {
                        ret[i] = Double.parseDouble(fields[i].getText());
                        if(ret[i] < 0 || ret[i] > 1) {
                            prompt.setText(labels[i] + " must be between 0 "
                                    + "and 1");
                            accepted = false;
                            break;
                        }
                    }
                } catch(NumberFormatException e) {
                    prompt.setText(labels[i] + " must be a valid "
                            + (isInt[i] ? "integer" : "number"));
                    accepted = false;
                    break;
                }
            }
            if(accepted) return ret;
        }
    }
}
