/*
 * KohonenVisualizerFrame.java
 *
 * Created on December 27, 2008, 3:01 AM
 */

package org.neuroph.easyneurons.samples;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.swing.JPanel;

import org.jdesktop.application.Action;
import org.neuroph.core.Connection;
import org.neuroph.core.Layer;
import org.neuroph.core.Neuron;
import org.neuroph.easyneurons.TrainingController;
import org.neuroph.nnet.Kohonen;
import org.neuroph.nnet.learning.KohonenLearning;

/**
 * TODO: refresh iscrtavanje, kada se uradi resize, minimize restore framea
 * @author Zoran Sevarac <sevarac@gmail.com>
 */
public class KohonenSample extends javax.swing.JInternalFrame
		implements Observer, Runnable {

	Image imageBuffer;
	Graphics2D drawingBuffer;
	Kohonen neuralNet;
	TrainingController controller;
	double pixVal = 0.004;
	int X0 = 260;
	int Y0 = 260;
	
	private ConcurrentLinkedQueue<Vector> displayDataBuffer;
	private Thread drawingThread = null;
	private boolean isDrawing = false;
		
	
	/** Creates new form KohonenVisualizerFrame */
	public KohonenSample(TrainingController controller) {
		initComponents();
		this.controller = controller;
		this.neuralNet = (Kohonen) controller.getNetwork();	
		this.displayDataBuffer = new ConcurrentLinkedQueue<Vector>(); 
	}

	/**
	 * This method is called from within the constructor to initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is always
	 * regenerated by the Form Editor.
	 */
	@SuppressWarnings("unchecked")
	// <editor-fold defaultstate="collapsed"
        // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
        private void initComponents() {
                java.awt.GridBagConstraints gridBagConstraints;

                controlPanel = new javax.swing.JPanel();
                jLabel1 = new javax.swing.JLabel();
                jLabel2 = new javax.swing.JLabel();
                jLabel3 = new javax.swing.JLabel();
                learningRateField = new javax.swing.JTextField();
                IphaseField = new javax.swing.JTextField();
                IIphaseField = new javax.swing.JTextField();
                jLabel4 = new javax.swing.JLabel();
                currentIterationField = new javax.swing.JTextField();
                randomizeButton = new javax.swing.JButton();
                startButton = new javax.swing.JButton();
                stopButton = new javax.swing.JButton();
                repaintButton = new javax.swing.JButton();
                closeButton = new javax.swing.JButton();
                drawingPanel = drawingPanel = new JPanel () {
                        protected void paintComponent(Graphics g) {
                                super.paintComponent(g);
                                repaintDrawingPanel();
                                g.drawImage(imageBuffer, 0, 0, this);
                        }

                };

                setClosable(true);
                setIconifiable(true);
                org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(org.neuroph.easyneurons.EasyNeuronsApplication.class).getContext().getResourceMap(KohonenSample.class);
                setTitle(resourceMap.getString("Form.title")); // NOI18N
                setDoubleBuffered(true);
                setName("Form"); // NOI18N
                setNormalBounds(null);

                controlPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
                controlPanel.setName("controlPanel"); // NOI18N
                controlPanel.setLayout(new java.awt.GridBagLayout());

                jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N
                jLabel1.setName("jLabel1"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(jLabel1, gridBagConstraints);

                jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N
                jLabel2.setName("jLabel2"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 0;
                gridBagConstraints.gridy = 1;
                gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(jLabel2, gridBagConstraints);

                jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N
                jLabel3.setName("jLabel3"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 0;
                gridBagConstraints.gridy = 2;
                gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(jLabel3, gridBagConstraints);

                learningRateField.setColumns(7);
                learningRateField.setText(resourceMap.getString("learningRateField.text")); // NOI18N
                learningRateField.setName("learningRateField"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
                gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(learningRateField, gridBagConstraints);

                IphaseField.setText(resourceMap.getString("IphaseField.text")); // NOI18N
                IphaseField.setName("IphaseField"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 1;
                gridBagConstraints.gridy = 1;
                gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
                gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(IphaseField, gridBagConstraints);

                IIphaseField.setText(resourceMap.getString("IIphaseField.text")); // NOI18N
                IIphaseField.setName("IIphaseField"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 1;
                gridBagConstraints.gridy = 2;
                gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
                gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(IIphaseField, gridBagConstraints);

                jLabel4.setText(resourceMap.getString("jLabel4.text")); // NOI18N
                jLabel4.setName("jLabel4"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(jLabel4, gridBagConstraints);

                currentIterationField.setColumns(10);
                currentIterationField.setText(resourceMap.getString("currentIterationField.text")); // NOI18N
                currentIterationField.setName("currentIterationField"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(currentIterationField, gridBagConstraints);

                javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(org.neuroph.easyneurons.EasyNeuronsApplication.class).getContext().getActionMap(KohonenSample.class, this);
                randomizeButton.setAction(actionMap.get("randomize")); // NOI18N
                randomizeButton.setText(resourceMap.getString("randomizeButton.text")); // NOI18N
                randomizeButton.setName("randomizeButton"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 2;
                gridBagConstraints.gridy = 1;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(randomizeButton, gridBagConstraints);

                startButton.setAction(actionMap.get("start")); // NOI18N
                startButton.setText(resourceMap.getString("startButton.text")); // NOI18N
                startButton.setName("startButton"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 3;
                gridBagConstraints.gridy = 1;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(startButton, gridBagConstraints);

                stopButton.setAction(actionMap.get("stopTraining")); // NOI18N
                stopButton.setText(resourceMap.getString("stopButton.text")); // NOI18N
                stopButton.setName("stopButton"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 4;
                gridBagConstraints.gridy = 1;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(stopButton, gridBagConstraints);

                repaintButton.setAction(actionMap.get("repaintKohonen")); // NOI18N
                repaintButton.setText(resourceMap.getString("repaintButton.text")); // NOI18N
                repaintButton.setName("repaintButton"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 3;
                gridBagConstraints.gridy = 2;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(repaintButton, gridBagConstraints);

                closeButton.setAction(actionMap.get("close")); // NOI18N
                closeButton.setText(resourceMap.getString("closeButton.text")); // NOI18N
                closeButton.setName("closeButton"); // NOI18N
                gridBagConstraints = new java.awt.GridBagConstraints();
                gridBagConstraints.gridx = 4;
                gridBagConstraints.gridy = 2;
                gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
                controlPanel.add(closeButton, gridBagConstraints);

                getContentPane().add(controlPanel, java.awt.BorderLayout.PAGE_END);

                drawingPanel.setBackground(resourceMap.getColor("drawingPanel.background")); // NOI18N
                drawingPanel.setName("drawingPanel"); // NOI18N
                drawingPanel.setPreferredSize(new java.awt.Dimension(520, 460));
                drawingPanel.setLayout(new java.awt.BorderLayout());
                getContentPane().add(drawingPanel, java.awt.BorderLayout.CENTER);

                pack();
        }// </editor-fold>//GEN-END:initComponents


	public void update(Observable o, Object arg) {
		KohonenLearning kl = (KohonenLearning) (controller.getNetwork()
				.getLearningRule());
		this.currentIterationField.setText(kl.getIteration().toString()); // ovo isto prebaciti u negede thread
	
		Vector<Point> weights = getWeightPoints();
		this.displayDataBuffer.add(weights);
		
		if (!isDrawing) {
			drawingThread = new Thread(this);
			drawingThread.start();		
		}
	}

	public void run() {
	
		isDrawing = true;
		
		while(!controller.isStoppedTraining()) {
			drawFromDataBuffer();
		}
		
		isDrawing = false;
	}
	
	// initializes drawing buffer, draws axis and weights
	private void drawFromDataBuffer() {

		if (displayDataBuffer.size()==0) {
			try {
				Thread.sleep(100); // if there is no data to display wait 100 milisec
			} catch(InterruptedException ie) {

			}
			return;
		}

		if (drawingBuffer == null) {
			imageBuffer = drawingPanel.createImage(520, 520);
			drawingBuffer = (Graphics2D) imageBuffer.getGraphics();	 
			
		}

		drawingBuffer.clearRect(0, 0, getWidth(), getHeight());
		drawAxis();
		
		Vector<Point> weights = displayDataBuffer.poll();
		if (weights!=null) drawWeights(weights);
		
		// ako je minimizovan a radi ovde baca NullPointerException
		this.drawingPanel.getGraphics().drawImage(imageBuffer, 0, 0, this.drawingPanel);		
	
	}

	// draws x and y  axis
	private void drawAxis() {
//		int width = drawingPanel.getWidth();
//		int height = drawingPanel.getHeight();
		
		this.drawingBuffer.drawLine(260, 0, 260, 460);
		this.drawingBuffer.drawLine(0, 230, 520, 230);
	}	

	private void drawWeights(Vector<Point> weights) {
		Point prevPoint = null;
		
		int i = 0;
		int mapSize = ((KohonenLearning) neuralNet.getLearningRule()).getMapSize(); // duzina

		for(Point p : weights) {
			drawingBuffer.fillOval((int) p.getX(), (int) p.getY(), 8, 8);

			if (prevPoint != null)
				if (i % mapSize > ((i - 1) % mapSize))
					drawingBuffer.drawLine((int) prevPoint.getX(), (int) prevPoint
							.getY(), (int) p.getX(), (int) p.getY());

			if (i >= mapSize) {
				Point upPoint = weights.elementAt(i - mapSize);
				drawingBuffer.drawLine((int) upPoint.getX(), (int) upPoint.getY(),
						(int) p.getX(), (int) p.getY());
			}

			prevPoint = p;
			i++;
		}	
	}

	private Vector<Point> getWeightPoints() {
		Vector<Point> weightPoints = new Vector<Point>();
		Layer mapLayer = (Layer) neuralNet.getLayerAt(1);
		Iterator<Neuron> eN = mapLayer.getNeuronsIterator();
		while (eN.hasNext()) {
			Neuron cell = eN.next();
			Vector inputs = cell.getInputConnections();
			Connection c1 = (Connection) inputs.elementAt(0);
			Connection c2 = (Connection) inputs.elementAt(1);
			double wX = c1.getWeight().getValue();
			double wY = c2.getWeight().getValue();
			Point point = createPoint(wX, wY);
			weightPoints.add(point);
		} 
		return weightPoints;
	}

	private Point createPoint(double xVal, double yVal) {
		int xPix = (int) (xVal / pixVal);
		int yPix = (int) (yVal / pixVal);
		int xCoord = X0 + xPix;
		int yCoord = Y0 - yPix;
		return new Point(xCoord, yCoord);
	}

	@Action
	public void randomize() {
		this.displayDataBuffer.clear();
		controller.randomize();
		
		if (!isDrawing) {
			drawFromDataBuffer();
		}
	}

	@Action
	public void stopTraining() {
		controller.stopTraining();
		this.displayDataBuffer.clear();
	}

	@Action
	public void close() {
		stopTraining();
		this.dispose();
	}

	@Action
	public void start() {
		String learningRateStr = learningRateField.getText().trim().toString();
		String IphaseStr = IphaseField.getText().trim().toString();
		String IIphaseStr = IIphaseField.getText().trim().toString();
		Double learningRate = new Double(learningRateStr);
		Integer Iphase = new Integer(IphaseStr);
		Integer IIphase = new Integer(IIphaseStr);
				
		controller.setKohonenParams(learningRate, Iphase, IIphase);
		controller.train();
					
	}

	@Action
	public void repaintKohonen() {
		Vector<Point> weights = getWeightPoints();
		this.displayDataBuffer.add(weights);		
		this.drawFromDataBuffer();	
	}
	
	public void repaintDrawingPanel() {
		if (drawingBuffer == null) {
			imageBuffer = drawingPanel.createImage(520, 460);
			drawingBuffer = (Graphics2D) imageBuffer.getGraphics();	 
		}
		drawAxis();
		Vector<Point> weights = getWeightPoints();
		drawWeights(weights);		
		
	}
		
	

        // Variables declaration - do not modify//GEN-BEGIN:variables
        private javax.swing.JTextField IIphaseField;
        private javax.swing.JTextField IphaseField;
        private javax.swing.JButton closeButton;
        private javax.swing.JPanel controlPanel;
        private javax.swing.JTextField currentIterationField;
        private javax.swing.JPanel drawingPanel;
        private javax.swing.JLabel jLabel1;
        private javax.swing.JLabel jLabel2;
        private javax.swing.JLabel jLabel3;
        private javax.swing.JLabel jLabel4;
        private javax.swing.JTextField learningRateField;
        private javax.swing.JButton randomizeButton;
        private javax.swing.JButton repaintButton;
        private javax.swing.JButton startButton;
        private javax.swing.JButton stopButton;
        // End of variables declaration//GEN-END:variables

}
