/***
 * Neuroph  http://neuroph.sourceforge.net
 * Copyright by Neuroph Project (C) 2008
 *
 * This file is part of Neuroph framework.
 *
 * Neuroph is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Neuroph is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Neuroph. If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * BackpropagationSample.java
 *
 * This sample is based on demos at Java Neural Index  http://lcn.epfl.ch/tutorial/english/index.html
 * and it is implemented on top of Neuroph neural networks
 */

package org.neuroph.easyneurons.samples.mlperceptron;

import org.neuroph.core.Layer;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.util.Iterator;
import org.neuroph.easyneurons.samples.perceptron.PerceptronSampleTrainingSet;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.JOptionPane;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.learning.SupervisedTrainingElement;
import org.neuroph.core.learning.TrainingSet;
import org.neuroph.easyneurons.EasyNeuronsApplicationView;
import org.neuroph.easyneurons.NeuralNetworkTraining;
import org.neuroph.easyneurons.dialog.SupervisedTrainingMonitor;
import org.neuroph.easyneurons.errorgraph.GraphFrame;
//import org.neuroph.nnet.MultiLayerPerceptron;
import org.neuroph.nnet.MultiLayerPerceptron;
import org.neuroph.nnet.learning.LMS;
import org.neuroph.nnet.learning.MomentumBackpropagation;
//import org.neuroph.util.TransferFunctionType;

/**
 *
 * @author Marko Koprivica
 * @author Zoran Sevarac
 */
public class MultiLayerPerceptronSample extends javax.swing.JInternalFrame implements Observer{
    EasyNeuronsApplicationView mainFrame;

    InputSpacePanel inputSpacePanel;
    InformationPanel informationPanel;
    ControllsPanel sourcePanel;

    PerceptronSampleTrainingSet pst;
    TrainingSet trainingSet;
    NeuralNetwork neuralNetwork;
    NeuralNetworkTraining controller;

    Thread firstCalculation = null;
    int iterationCounter = 0;


    /** Creates new form BackpropagationSample */
    public MultiLayerPerceptronSample(PerceptronSampleTrainingSet ps) {
        initComponents();
        setSize(770, 600);
        setIconifiable(true);
        setClosable(true);

        trainingSet = new TrainingSet();
        this.pst = ps;
                

         this.mainFrame = EasyNeuronsApplicationView.getInstance();
       // this.displayDataBuffer = new ConcurrentLinkedQueue<NeuralNetwork>();   // red koji se ne koristi
       // link();    // povezivanje panela sa frameom

        inputSpacePanel = new InputSpacePanel();
        informationPanel = new InformationPanel();
        sourcePanel = new ControllsPanel(this);


        inputSpacePanel.setSize(570, 570);
        informationPanel.setSize(200, 50);
        sourcePanel.setSize(182, 520);

        add(inputSpacePanel);
        add(informationPanel);
        add(sourcePanel);

        inputSpacePanel.setLocation(0, 0);
        informationPanel.setLocation(570, 0);
        sourcePanel.setLocation(570, 50);

// DragNDrop - start
        this.dtListener = new DTListener();

        this.dropTarget = new DropTarget(
                  this,
                  this.acceptableActions,
                  this.dtListener,
                  true);

        this.dropTarget2 = new DropTarget(
                  inputSpacePanel,
                  this.acceptableActions,
                  this.dtListener,
                  true);

        this.dropTarget3 = new DropTarget(
                  sourcePanel,
                  this.acceptableActions,
                  this.dtListener,
                  true);
// DragNDrop - end
    }

 
    /** 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" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(org.neuroph.easyneurons.EasyNeuronsApplication.class).getContext().getResourceMap(MultiLayerPerceptronSample.class);
        setTitle(resourceMap.getString("Form.title")); // NOI18N
        setName("Form"); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 654, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 623, Short.MAX_VALUE)
        );

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

    boolean f = false;


    @Override
    public void update(Observable o, Object arg) {           // pri promeni neuronske mreze, pokrece se update - observer patern

        iterationCounter++;
        if (iterationCounter % 10 == 0) {
            NeuralNetwork nnet = controller.getNetwork();

            nnet.pauseLearning();                             // pauza
            imagePlaying(nnet);                                  //  racunanje odgovora mreze i pozivanje crtanja
            nnet.resumeLearning();                            //  nastavak ucenja
        }

     }

    public void train(NeuralNetwork nn)               // kada se pritisne train dugme na source panelu, logika dolazi do ovde
    {
        //if (!trainingSet.isEmpty()) trainingSet.clear();
        trainingSet = inputSpacePanel.getTrain();            //  izvlaci se training set sa tackama ulaza i izlaza iz inputSpacePanela, nije moglo drugacije
        neuralNetwork = nn;  //sp.getNet();                  // mreza je dosla iz sourcePanela koji i poziva ovu metodu
        
        controller = new NeuralNetworkTraining(neuralNetwork, trainingSet);

        pst.setTrainingSet(trainingSet,neuralNetwork,controller);

        neuralNetwork.getLearningRule().addObserver(this);              // dodaje se observer na mrezu i pri njenoj promeni poziva se metoda update u ovoj klasi
        neuralNetwork.addObserver(this);

        controller.setLmsParams(sourcePanel.getLearningRate(),sourcePanel.getMaxError(), sourcePanel.getMaxIteration());

        LMS learningRule = (LMS) this.controller.getNetwork().getLearningRule();

		if (learningRule instanceof MomentumBackpropagation) {
			((MomentumBackpropagation)learningRule).setMomentum(sourcePanel.getMomentum());
		}

        SupervisedTrainingMonitor trainingMonitor = new SupervisedTrainingMonitor(null,
				false, this.controller);
		trainingMonitor.setLocationRelativeTo(this);
		learningRule.addObserver(trainingMonitor);

            GraphFrame graphFrame = mainFrame.openErrorGraphFrame();
            graphFrame.observe(learningRule);

        controller.train();                // pocetak treniranja mreze
    }

    public void stop()             // zaustavljanje treniranja pozvano iz source panela
    {
        controller.stopTraining();
    }

    public void clear()            //  restartovanje inputSpacePanela pozvano iz source panela
    {
        inputSpacePanel.clearPoints();
        pst.setTrainingSet(trainingSet,null,null);
    }

    // poziva se iz update metode za ulaze od (0,0) pa sve do (1,1) povecavajuci se za 0.02 racuna se izlaz mreze, ovo je vid testiranja na uvek istim podacima
    public void imagePlaying(NeuralNetwork nn) {

        if (nn!=null){
        for(int i=0; i<50;i++)                  // racunanje izlaza mreze za 2500 ulaza
            for (int j=0;j<50; j++)
            {
                double x = 0.0 + i*0.02;        // vrednosti ulaza x1 i x2
                double y = 1.0 - j*0.02;
                //neuralNetwork
                nn.setInput(new double[]{x, y});
                nn.calculate();
                double v = nn.getLayers().lastElement().getNeurons().firstElement().getOutput();    // izlaz iz mreze za ulaze x1 i x2
           //     System.out.println("ULAZ ["+i+"]["+j+"] JE:"+"["+x+"]"+"["+y+"] A IZLAZ JE "+v);
                inputSpacePanel.setGridPoints(i, j, v); // pozivanje metode koja treba da pripremi crtanje u inputSpacePanelu
                
            }
        }
      }

// DragNDrop - start
    // DnD liseneri
        class DTListener implements DropTargetListener
            {
                private DataFlavor chooseDropFlavor(DropTargetDropEvent e) {

                      DataFlavor chosen = null;

                      if (e.isDataFlavorSupported(TransferableObject.flavorN)) {
                        chosen = TransferableObject.flavorN;
                      } else if (e.isDataFlavorSupported(TransferableObject.flavorT)) {
                        chosen = TransferableObject.flavorT;
                      }else { }

                      return chosen;
                }


                public void dragEnter(DropTargetDragEvent dtde) {
                    dtde.acceptDrag(dtde.getDropAction());
                }

                public void dragExit(DropTargetEvent dte) {

                }

                public void dragOver(DropTargetDragEvent dtde) {
                    dtde.acceptDrag(dtde.getDropAction());
                }

                public void dropActionChanged(DropTargetDragEvent dtde) {
                     dtde.acceptDrag(dtde.getDropAction());
                }
                 public void drop(DropTargetDropEvent e) {

                      DataFlavor chosen = chooseDropFlavor(e);
                      if (chosen == null) {
                        System.err.println( "No flavor match found" );
                        e.rejectDrop();
                        return;
                      }
                      // the actual operation
                      int da = e.getDropAction();
                      // the actions that the source has specified with DragGestureRecognizer
                      int sa = e.getSourceActions();
//                      System.out.println( "drop: sourceActions: " + sa);
//                      System.out.println( "drop: dropAction: " + da);

                      if ( ( sa & MultiLayerPerceptronSample.this.acceptableActions ) == 0 ) {
                            System.err.println( "No action match found" );
                            e.rejectDrop();
                            // ovde ispisati sta se radi u slucaju greske
                            //sourcePanel.setJtfMultylayer("ERROR");
                            JOptionPane.showMessageDialog(MultiLayerPerceptronSample.this, "GRESKA! NEDOZVOLJENA AKCIJA");
                            sourcePanel.clear();
                            return;
                      }
                    System.out.println("1");

                    Object data = null;

                        try {    // ovde ispisati sta se radi kod dropa

                            e.acceptDrop(MultiLayerPerceptronSample.this.acceptableActions);

                            data = e.getTransferable().getTransferData(chosen);
                            //System.out.println(chosen.toString());
                            //System.out.println("2 i data: " + data.toString());
                            if (data == null)   throw new NullPointerException();

                        } catch ( Throwable t ) {
                            System.err.println( "Couldn't get transfer data: " + t.getMessage());
                            t.printStackTrace();
                            e.dropComplete(false);
                            return;
                          }
                    
                     //if (data instanceof NeuralNetwork ) {
                         if(data instanceof MultiLayerPerceptron){
                             NeuralNetwork dropNetwork = (MultiLayerPerceptron) data;
                             int inputNeurons = dropNetwork.getInputNeurons().size();
                             int outputNeurons = dropNetwork.getOutputNeurons().size();
                             //System.out.println("3.1 uspeli smo i ulaznih je: "+ inputNeurons +" a izlaznih je: "+outputNeurons);

                             if(inputNeurons == 2 && outputNeurons == 1)
                             {
                                 int i = 0;
                                 String midleLayers = "";
                                 //posto su sve provere prosle krecemo sa crtanjem i ubacivanjem
                                 for (Iterator<Layer> it = dropNetwork.getLayersIterator(); it.hasNext();) {
                                     Layer l = it.next();

                                     if(i != 0 && i != dropNetwork.getLayersCount()-1){
                                        if(i>1) midleLayers = midleLayers + " ";
                                        midleLayers = midleLayers+(l.getNeurons().size()-1);}
                                    i++;

                                 }
                                 sourcePanel.setJtfMultylayer(midleLayers);

                                 //crtanje
                                 imagePlaying(dropNetwork);

                                 //ovde treba ostale podatke izvuci
                                 sourcePanel.setJTFs("", "", "");
                             }
                             else{
                                 //sourcePanel.setJtfMultylayer("ERROR");
                                 JOptionPane.showMessageDialog(MultiLayerPerceptronSample.this, "Error! MREZA MORA BITI 2 X 1");
                                 sourcePanel.clear();}


                     } else if (data instanceof TrainingSet) {
                         TrainingSet dropTrainingSet = (TrainingSet) data;
                         SupervisedTrainingElement element = (SupervisedTrainingElement) dropTrainingSet.trainingElements().firstElement();
                         if(element.getDesiredOutput().size()==1 && element.getInput().size()==2){
                            // System.out.println("Training ulazi");
                         }
                           //dropTrainingSet.trainingElements().get(0).
                           //if(dropTrainingSet.trainingElements().firstElement().getInput().size()==2 && dropTrainingSet.trainingElements().firstElement().get.size()==2)
                         inputSpacePanel.drawPointsFromTrainingSet(dropTrainingSet);
                     }

                     else JOptionPane.showMessageDialog(MultiLayerPerceptronSample.this, "Error");

                   e.dropComplete(true);
                }

            }

    private DropTarget dropTarget;
    private DropTarget dropTarget2;
    private DropTarget dropTarget3;
    private DropTargetListener dtListener;
    private int acceptableActions = DnDConstants.ACTION_COPY;
// DragNDrop - end
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables

}