package ac.essex.gp.interfaces.graphical;

import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.plot.PlotOrientation;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.util.Vector;
import java.util.Date;
import java.text.DecimalFormat;
import java.io.File;

import ac.essex.ooechs.treeanimator.TreeFrame;
import ac.essex.gp.tree.TreeUtils;
import ac.essex.gp.problems.ImagingProblem;
import ac.essex.gp.problems.Problem;
import ac.essex.gp.training.TrainingImage;
import ac.essex.gp.individuals.Individual;

/**
 * Main window - displays graphs and stats and allows the user to interact.
 */
class MainWindow extends JFrame {

    JLabel generationNumber;
    JLabel fitness;
    JLabel hits, misses, size;
    JLabel time;
    JLabel individuals;

    JMenuItem mnuExit, mnuSave, mnuViewTree;
    JCheckBoxMenuItem mnuDisplayImage, mnuGpSummary;

    JLabel chart;
    JTextArea displayIndividual;
    DefaultCategoryDataset series;

    JButton smack;
    private GraphicalListener graphicalListener;

    private ResultsSummary resultsSummary;

    public MainWindow(GraphicalListener gl) {
        this.graphicalListener = gl;

        series = new DefaultCategoryDataset();

        Container c = getContentPane();
        generationNumber = new JLabel("Initialising...");
        fitness = new JLabel();
        fitness.setToolTipText("The fitness of the current best individual. The lower the fitness the better the individual.");
        hits = new JLabel();
        hits.setToolTipText("The number of correct classifications that the current best individual made");
        misses = new JLabel();
        misses.setToolTipText("The number of mistakes that the current best individual made");
        time = new JLabel();
        time.setToolTipText("The amount of time that the evolution process has taken so far");
        size = new JLabel();
        size.setToolTipText("The size of the individual displayed above in nodes");
        individuals = new JLabel();
        individuals.setToolTipText("The number of individuals that have been evaluated");

        chart = new JLabel();
        c.add(chart, BorderLayout.CENTER);

        displayIndividual = new JTextArea("Evaluating: Generation 0");
        JScrollPane pane = new JScrollPane(displayIndividual);
        pane.setPreferredSize(new Dimension(350, -1));
        c.add(pane, BorderLayout.EAST);

        // MENUS

        mnuDisplayImage = new JCheckBoxMenuItem("Display describe() Output");
        mnuDisplayImage.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.CTRL_MASK));
        mnuDisplayImage.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (!mnuDisplayImage.isSelected()) {
                    if (graphicalListener.imageWindow != null) graphicalListener.imageWindow.onClose();
                } else {
                    graphicalListener.previousFitness = -1;
                    graphicalListener.imageWindow = new ImageWindow(graphicalListener);
                }
                graphicalListener.displayOutput = mnuDisplayImage.isSelected();
            }
        });
        mnuDisplayImage.setSelected(graphicalListener.displayOutput);

        mnuGpSummary = new JCheckBoxMenuItem("GP Summary");
        mnuGpSummary.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, ActionEvent.CTRL_MASK));
        mnuGpSummary.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (resultsSummary == null || !mnuGpSummary.isSelected()) {
                    resultsSummary = new  ResultsSummary(MainWindow.this, graphicalListener.results);
                    mnuGpSummary.setSelected(true);
                } else {
                    if (resultsSummary != null)  resultsSummary.dispose();
                    mnuGpSummary.setSelected(false);
                }
            }
        });

        mnuViewTree = new JMenuItem("Tree Structure");
        mnuViewTree.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, ActionEvent.CTRL_MASK));
        mnuViewTree.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (graphicalListener.getBestIndividual() != null) {
                    //new TreeFrame(TreeUtils.getAnimatedTree(graphicalListener.getBestIndividual()));
                }
            }
        });

        mnuSave = new JMenuItem("Save Solution");
        mnuSave.setEnabled(false);
        mnuSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
        mnuSave.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                graphicalListener.saveIndividual();
            }
        });

        JMenuItem mnuStop = new JMenuItem("Stop Evolution");
        mnuStop.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                graphicalListener.e.stopFlag = true;
                ((JMenuItem) e.getSource()).setEnabled(false);
            }
        });

        mnuExit = new JMenuItem("Close");
        mnuExit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onExit();
            }
        });

        JMenuItem mnuCopyToClipboard = new JMenuItem("Copy code to clipboard");
        mnuCopyToClipboard.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                graphicalListener.writeToClipboard(displayIndividual.getText());
            }
        });

        JMenu edit = new JMenu("Edit");
        edit.add(mnuCopyToClipboard);


        JMenu file = new JMenu("File");
        file.add(mnuSave);
        file.addSeparator();
        file.add(mnuStop);
        file.add(mnuExit);

        JMenu view = new JMenu("View");
        view.add(mnuGpSummary);
        view.add(mnuViewTree);
        view.add(mnuDisplayImage);


        JMenuBar bar = new JMenuBar();
        bar.add(file);
        bar.add(edit);
        bar.add(view);

        if (graphicalListener.p instanceof ImagingProblem) {
            ImagingProblem ip = ((ImagingProblem) graphicalListener.p);
            int trainingCount = ip.getTrainingCount();
            JMenu preview = new JMenu("Preview");
            for (int i = 0; i < trainingCount; i++) {
                preview.add(new ImageMenuItem(graphicalListener, ip.getTrainingImageName(i), i));
            }
            bar.add(preview);
        }

        setJMenuBar(bar);

        // END MENUS

        JPanel p = new JPanel(new GridLayout(1,7));

        p.add(generationNumber);
        p.add(individuals);
        p.add(fitness);
        p.add(hits);
        p.add(misses);
        p.add(size);
        p.add(time);

        c.add(p, BorderLayout.SOUTH);
        setTitle("Genetic Programming");

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                onExit();
            }
        });

        try {
            setIconImage(new ImageIcon(getClass().getResource("/gp-icon.gif")).getImage());
        } catch (Exception e) {
        }            

        setSize(700, 300);
        setLocation((1280 - 700) / 2, (1024 - 300) / 2);
        setVisible(true);

    }

    JFileChooser chooser = null;


    public void onExit() {
        if (graphicalListener.e != null) graphicalListener.e.stopFlag = true;
        if (graphicalListener.imageWindow != null) graphicalListener.imageWindow.dispose();
        dispose();
    }

    public void updateGenerationNumber(int num) {
        generationNumber.setText("Gen: " + num);
    }

    public void updateTime() {
        long elapsed = (System.currentTimeMillis() - graphicalListener.startTime) / 1000;
        time.setText(elapsed + " secs.");
    }

    DecimalFormat f = new DecimalFormat("0.000");

    public void displayIndividual(Individual ind) {
        if (ind != null) {
            mnuSave.setEnabled(true);
            //if (graphicalListener.p.getGpVariant() == Problem.TYPE_STANDARD_GP) {
                String java = ind.toJava(graphicalListener.p.getMethodSignature(graphicalListener.e.getBestIndividual()));
                if (!java.equals(displayIndividual.getText())) {
                    String info = "// Fitness: " + ind.getKozaFitness() + "\n";
                    info += "// Hits: "  + ind.getHits() + "\n// Mistakes: " + ind.getMistakes() + "\n// Evolved at: " + new Date().toString();

                    displayIndividual.setText(info + "\n\n" + java);
                }
/*            } else {
                SubGenerationalProblem cpp = (SubGenerationalProblem) graphicalListener.p;
                displayIndividual.setText(cpp.getStrongClassifier().toString());
            }*/
            fitness.setText("F: " + f.format(ind.getKozaFitness()));
            hits.setText("TP: " + ind.getHits());
            misses.setText("FP: " + ind.getMistakes());
            size.setText("Size: " + ind.getTreeSize());
            updateChart(ind, graphicalListener.currentGeneration);
            if (resultsSummary != null) {
                resultsSummary.displayResults(graphicalListener.results);
            }
        }
    }

    public void updateChart(Individual ind, int generation) {
        //if (graphicalListener.p.getGpVariant() == Problem.TYPE_STANDARD_GP) {
            series.addValue(ind.getKozaFitness(), "training", String.valueOf(generation));
/*        } else {
            // Basic shape problem allows both unseen and training data to be presented together
            if (graphicalListener.p instanceof JasmineBasicShapeProblem) {
                // display the training and unseen data together, but convert them both
                // to percentages for direct comparison.
                JasmineBasicShapeProblem shapeProblem = (JasmineBasicShapeProblem) graphicalListener.p;
                JasmineProject unseen = shapeProblem.getUnseenProject();
                if (unseen != null) {
                    double hitsUnseen = ((SubGenerationalIndividual) ind).test(unseen);
                    // TODO: Hideously inefficient. What the hell was I thinking?????????
                    double hitsPercentage = hitsUnseen / JasmineUtils.getTrainingData(unseen).size();
                    series.addValue(hitsPercentage, "unseen", String.valueOf(graphicalListener.subgeneration));
                }
                double hitsPercentage = 1 - (ind.getHits() / (double) shapeProblem.getTrainingCount());
                series.addValue(hitsPercentage, "training", String.valueOf(graphicalListener.subgeneration));
            } else {
                // Not basic shape problem - just display training data
                series.addValue(ind.getHits(), "training", String.valueOf(graphicalListener.subgeneration));
            }
        }*/
        JFreeChart myChart = ChartFactory.createLineChart(null, "Generations", "Error", series, PlotOrientation.VERTICAL, false, false, false);
        BufferedImage image = myChart.createBufferedImage(chart.getWidth(), chart.getHeight());
        chart.setIcon(new ImageIcon(image));
    }

}
