package handwriting.editor;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

import handwriting.kohonen.*;

@SuppressWarnings("serial")
public class DrawingEditor extends JFrame {
	private MousePencil c;
	private DrawingPanel view;
	private SampleData data;
	private SOMClassifier som;
	
	private JMenuItem open, save, clear, recordDrawing, drawErase, dilate;
	private JFileChooser chooser;
	private JComboBox labeler, indexer;
	private JButton swapImage;
	private JTextField netLabel;
	private JMenuItem createSom, applySom, viewSomMotion, viewSomOutputs;
	
	private JFrame SOMDialog;
	private SOMInversionViewer somInverter;

    private JFrame SOMOutputDialog;
    private SOMOutputViewer somOutputs;
	
	private Drawing d() {return view.getDrawing();}
	
	private Drawing makeNewDrawing() {return new Drawing(20, 20);}
	
	public DrawingEditor() {
		setSize(550, 550);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		getContentPane().setLayout(new BorderLayout());
		
		view = new DrawingPanel(makeNewDrawing());
		c = new MousePencil(view);
		getContentPane().add(view, BorderLayout.CENTER);
		
		JMenuBar bar = new JMenuBar();
		setJMenuBar(bar);
		
		addFileMenu(bar);
		addViewMenu(bar);
		addNetworkControls();
		addViewByLabel();
		addSomMenu(bar);
	}
	
	private void addFileMenu(JMenuBar bar) {
		JMenu fileMenu = new JMenu("File");
		bar.add(fileMenu);
		
		open = new JMenuItem("Open");
		open.addActionListener(new Opener());
		fileMenu.add(open);
		
		save = new JMenuItem("Save");
		save.addActionListener(new Saver());
		fileMenu.add(save);
		
		chooser = new JFileChooser();
	}
	
	private void addViewMenu(JMenuBar bar) {
		JMenu viewMenu = new JMenu("View");
		bar.add(viewMenu);
		
		clear = new JMenuItem("Clear");
		clear.addActionListener(new Clearer());
		viewMenu.add(clear);
		
		recordDrawing = new JMenuItem("Record drawing");
		recordDrawing.addActionListener(new Recorder());
		viewMenu.add(recordDrawing);
		
		drawErase = new JMenuItem("Erase");
		drawErase.addActionListener(new DrawEraser());
		viewMenu.add(drawErase);

        dilate = new JMenuItem("Dilate");
        dilate.addActionListener(new Dilator());
        viewMenu.add(dilate);
	}
	
	private void addNetworkControls() {
		JPanel netPanel = new JPanel();
		
		netPanel.add(new JLabel("Drawing label:"));
		netLabel = new JTextField(10);
		netLabel.setEditable(false);
		netPanel.add(netLabel);
		
		getContentPane().add(netPanel, BorderLayout.SOUTH);
	}
	
	private void addViewByLabel() {
		data = new SampleData();
		
		JPanel dataPanel = new JPanel();
		labeler = new JComboBox();
		labeler.addActionListener(new Labeler());
		dataPanel.add(labeler);
		
		indexer = new JComboBox();
		dataPanel.add(indexer);
		
		swapImage = new JButton("Look up image");
		swapImage.addActionListener(new Swapper());
		dataPanel.add(swapImage);
		
		getContentPane().add(dataPanel, BorderLayout.NORTH);
	}
	
	private void addSomMenu(JMenuBar bar) {
		JMenu somMenu = new JMenu("Self-Organizing Map");
		bar.add(somMenu);
		
		createSom = new JMenuItem("Create");
		createSom.addActionListener(new SomCreator());
		somMenu.add(createSom);
		
		applySom = new JMenuItem("Apply");
		applySom.addActionListener(new SomApplier());
		somMenu.add(applySom);
		
		viewSomMotion = new JMenuItem("View inverter");
		viewSomMotion.addActionListener(new SomViewMotionEnabler());
		somMenu.add(viewSomMotion);
		
		somInverter = new SOMInversionViewer();
		somInverter.addMapInversionListener(new SomViewInverter());
		SOMDialog = new JFrame("Self-Organizing Map Explorer");
		SOMDialog.getContentPane().add(somInverter);
		SOMDialog.setVisible(false);
		SOMDialog.setSize(200,200);

        viewSomOutputs = new JMenuItem("View outputs");
        viewSomOutputs.addActionListener(new SomOutputViewer());
        somMenu.add(viewSomOutputs);
        somOutputs = new SOMOutputViewer();
        SOMOutputDialog = new JFrame("Self-Organizing Output Map");
        SOMOutputDialog.getContentPane().add(somOutputs);
        SOMOutputDialog.setVisible(false);
        SOMOutputDialog.setSize(200, 200);
	}
	
	public static void main(String[] args) {
		DrawingEditor gui = new DrawingEditor();
		gui.setVisible(true);
	}
	
	private class Opener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int choice = chooser.showOpenDialog(null);
			if (choice == JFileChooser.APPROVE_OPTION) {
				try {
					data = SampleData.parseDataFrom(new Scanner(chooser.getSelectedFile()));
					loadLabels();
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
	
	private void loadLabels() {
		labeler.removeAllItems();
		for (String label: data.allLabels()) {
			labeler.addItem(label);
		}
	}
	
	private class Saver implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int choice = chooser.showSaveDialog(null);
			if (choice == JFileChooser.APPROVE_OPTION) {
				try {
					PrintStream ps = new PrintStream(chooser.getSelectedFile());
					ps.println(data.toString());
					ps.close();
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				}
			}
		}
	}
	
	private class Labeler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (hasCurrentLabel()) {
				String label = getCurrentLabel();
				if (data.hasLabel(label)) {
					indexer.removeAllItems();
					for (int i = 0; i < data.numDrawingsFor(label); ++i) {
						indexer.addItem(i);
					}
					changeIndexedDrawing();
				} else {
					data.addLabel(label);
				}
			}
		}
	}
	
	private boolean hasCurrentLabel() {
		return labeler.getItemCount() > 0;
	}
	
	private String getCurrentLabel() {
		return labeler.getSelectedItem().toString();
	}

	private void changeIndexedDrawing() {
		if (indexer.getItemCount() > 0) {
			String label = getCurrentLabel();
			int index = Integer.parseInt(indexer.getSelectedItem().toString());
			if (data.numDrawingsFor(label) > index) {
				view.resetDrawing(data.getDrawing(label, index));
			}
		}
	}
	
	private class Recorder implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			String label = JOptionPane.showInputDialog("Enter drawing label");
			if (label != null) {
				indexer.addItem(data.numDrawingsFor(label));
				data.addDrawing(label, view.getDrawing());
				loadLabels();
			}
		}
	}
	
	private class Clearer implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			view.resetDrawing(makeNewDrawing());
		}
	}
	
	private class Swapper implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			changeIndexedDrawing();
		}
	}
	
	private class DrawEraser implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (c.isDrawing()) {
				drawErase.setText("Draw");
				c.erase();
			} else {
				drawErase.setText("Erase");
				c.draw();
			}
		}
	}
	
	private class SomCreator implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int choice = chooser.showOpenDialog(null);
			if (choice == JFileChooser.APPROVE_OPTION) {
				try {
					SampleData training = SampleData.parseDataFrom(new Scanner(chooser.getSelectedFile()));
					int numOutputs = Integer.parseInt(JOptionPane.showInputDialog("Enter number of output nodes"));
					int iterations = Integer.parseInt(JOptionPane.showInputDialog("Enter training iterations"));
					
                    // Substitute your constructor calls here
					//som = new SOMClassifier(numOutputs, training, iterations);
					JOptionPane.showMessageDialog(null, "Self-organizing map created");
					
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				} catch (NumberFormatException e2) {
					// Do nothing; just let the operation fail.
				}
			}
		}
	}
	
	private class SomApplier implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (som != null) {
				netLabel.setText(som.classify(d()));
			}
		}
	}
	
	private class SomViewMotionEnabler implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (som != null) {
				SOMDialog.setVisible(true);
				somInverter.setSOM(som.getSelfOrgMap());
				somInverter.addMouseMotionListener(new SOMScanner(somInverter));
			}
		}
	}
	
	private class SomViewInverter implements MapInversionListener {
		public void inversion(double[] inputs) {
			Drawing inv = new Drawing(inputs, som.getDrawingWidth(), som.getDrawingHeight());
			view.resetDrawing(inv);
		}
	}

    private class SomOutputViewer implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            SOMOutputDialog.setVisible(true);
            somOutputs.setSOM(som.getSelfOrgMap());
            somOutputs.showOutputsFor(SOMClassifier.inputsFor(d()));
        }
    }

    private class Dilator implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            view.resetDrawing(d().makeDilated());
        }
    }
}
