//import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
import java.io.*;
import javax.swing.*;

public class MixtureEM extends JApplet {
  ControlPanel cPanel;
  PlotCanvas pCanvas;
  CurvedGaussMixture CGMix;
  GaussLineMixture GLMix;
  Database DB;
  int xSize = 700;
  int ySize = 400;

  public void init() {
	Dimension dim = this.getSize();
	xSize = (int) dim.getWidth();
	ySize = (int) dim.getHeight();

    getContentPane().setLayout(new BorderLayout());
    DB = new Database(xSize, ySize, Color.black);
    CGMix = new CurvedGaussMixture(xSize, ySize, DB);
    GLMix = new GaussLineMixture(xSize, ySize, DB);
    pCanvas = new PlotCanvas(CGMix,
                             GLMix,
                             DB, xSize, ySize);
    cPanel = new ControlPanel(pCanvas,
                              CGMix,
                              GLMix,
                              DB);
    getContentPane().add(pCanvas, BorderLayout.CENTER);
    getContentPane().add(cPanel, BorderLayout.SOUTH);
  }
}

class ControlPanel extends JPanel implements Runnable, ActionListener {
  JComboBox nKernels;
  JComboBox selectmix;
  JComboBox EMswitch;
  JComboBox selectSpeed;
  PlotCanvas pcanvas;
  CurvedGaussMixture cgmix;
  GaussLineMixture glmix;
  int select;
  Database db;
  double[] ws, ws2;
  Thread th;
  private volatile Thread EM_Thread = null;

  boolean runMode = false;

  int currentSpeed =200;	// EM algorithm speed (slow, normal, fast)

  public ControlPanel (PlotCanvas pC,
                       CurvedGaussMixture CGMix, 
                       GaussLineMixture GLMix, 
                       Database DB) {
    cgmix = CGMix;
    glmix = GLMix;
    db = DB;
    pcanvas = pC;
    pcanvas.connectControlPanel(this);
    ws = new double[Mixture.maxkp];
    ws2 = new double[Mixture.maxkp];
    ws[0] = 0.1;
    ws2[0] = 10;
    for(int i = 1; i < Mixture.maxkp; i++) ws[i] = ws2[i] = 1;
	
    setLayout(new FlowLayout());
    setSelect(3);
    selectmix = new JComboBox();
    selectmix.addItem("GaussMix");
    selectmix.addItem("LineMix");
    selectmix.setSelectedItem(Integer.toString(0));
    selectmix.addActionListener(this);
    add(selectmix);

    JButton RandomPoints = new JButton("RandomPts");
    JButton ClearPoints = new JButton("ClearPts");
    JButton InitializaKernel = new JButton("InitKernels");

    selectSpeed = new JComboBox();
    selectSpeed.addItem("Normal");
    selectSpeed.addItem("Fast");
    selectSpeed.addItem("Slow");
    selectSpeed.setSelectedItem(Integer.toString(0));
    selectSpeed.addActionListener(this);
    add(selectSpeed);

    RandomPoints.addActionListener(this);
    ClearPoints.addActionListener(this);
    InitializaKernel.addActionListener(this);

    add(RandomPoints);
    add(ClearPoints);
    add(InitializaKernel);
    
	// startButton.addActionListener(this);

    nKernels = new JComboBox();
    for(int i=1;i<10;i++)
      nKernels.addItem(Integer.toString(i));
    nKernels.setSelectedItem(Integer.toString(1));
    nKernels.addActionListener(this);

    add(nKernels);

    EMswitch = new JComboBox();
    EMswitch.addItem("EM Stop");
    EMswitch.addItem("EM Run");
    EMswitch.addItem("EM 1 Step");
    EMswitch.addItem("Segment");
    EMswitch.setSelectedItem(Integer.toString(0));
    EMswitch.addActionListener(this);

    add(EMswitch);
  }

  public void start() {
    if(EM_Thread == null) {
      EM_Thread = new Thread(this);
      EM_Thread.start();
    }
  }

  public void stop() {
     EM_Thread = null;
  }

  public void run() {
    Thread thisThread = Thread.currentThread();
    while (EM_Thread == thisThread) {
      try {
        for(int i = 0; i < 5; i++) {
            EM();
        }
        pcanvas.repaint();
        Thread.sleep(currentSpeed);
      } catch(InterruptedException e) {
      }
    }
  }

  private void EM() {
    if(select == 3) {
      cgmix.EM(ws);
    } else if(select == 4) {
      glmix.EM(ws);
    }
  }

  public void dbpush(int x, int y) {
    if(th != null) {
      stop();
      db.push(x, y);
      start();
    } else {
      db.push(x, y);
    }
  }

  public void actionPerformed(ActionEvent e) {
 
    String ch;
    int nK;
    boolean thflag;
    
    thflag = (EM_Thread != null);

    if(e.getSource() instanceof JComboBox )
    {   JComboBox JCB = (JComboBox)e.getSource();
	  String JCB_Value = (String) JCB.getSelectedItem();
	  System.out.println("JCB_Value = "+JCB_Value );

	  if (JCB_Value.equals("EM 1 Step")) 
        {	System.out.println("EM 1 Step");
            stop();
            EM();
	   }
	  else if (JCB_Value.equals("EM Run")) 
        {	System.out.println("EM Run");
        	stop();
        	start();
	   }
	  else if (JCB_Value.equals("EM Stop")) 
        {	System.out.println("EM Stop");
        	stop();
    	   }
        else if (JCB_Value.equals("Segment")) 
        {	System.out.println("Point Segmenting");
        	stop();
		pointSegmenting();
    	   }
	  else if (JCB_Value.equals("GaussMix")) 
        {	System.out.println("GaussMix");
        	setSelect(3);
        	cgmix.randomKernels(ws2);
    	   }
	  else if (JCB_Value.equals("LineMix")) 
        {	System.out.println("LinearMix");
        	setSelect(4);
        	glmix.randomKernels(ws2);
    	   }
	  else if (JCB_Value.equals("Fast")) 
        {	System.out.println("Speed selected: Fast");
        	setSpeed("Fast");
     	   }
        else if (JCB_Value.equals("Normal")) 
        {	System.out.println("Speed selected: Normal");
        	setSpeed("Normal");
     	   }
        else if (JCB_Value.equals("Slow")) 
        {	System.out.println("Speed selected: Slow");
        	setSpeed("Slow");
     	   }
	  else // Number of Kernels
        {	stop();
        	nK = Integer.parseInt(JCB_Value);
        	cgmix.setnk(nK + 1, ws);
        	glmix.setnk(nK + 1, ws);
		if(thflag) start();
     	   }
     } 
    else if(e.getActionCommand().equals("ClearPts")) 
    {
        db.clearPoints();
     } 
    else if(e.getActionCommand().equals("RandomPts")) 
    {
        db.randomPoints(10);
     } 
    else if(e.getActionCommand().equals("InitKernels")) 
    {
        cgmix.randomKernels(ws);
        glmix.randomKernels(ws);
     }

      pcanvas.repaint();
      return;
  } 

  public void pointSegmenting() {
      for (int i = 0; i < db.nPoints(); i++)		// For each point find the 2-nd level Gaussian 
	{  for (int k = 0; k < cgmix.getnk(); k++)	// for each kernel
	   {	   if (cgmix.getKernel(k) instanceof CurvedGaussian &&
			 ((CurvedGaussian)(cgmix.getKernel(k))).getPolygon().contains(db.xVal(i), db.yVal(i))) 
									// point inside the 2nd polygon Ellipse "cgmix")
	   	   {  //System.out.println("Inside::pointSegmenting() Poly.N_Points=" + 
				//((CurvedGaussian)(cgmix.getKernel(k))).getPolygon().npoints);
			db.setPointColor(i, cgmix.getKernelColor(k));
			k = cgmix.getnk(); 			// exit inner loop
	    	   }
	    }
	}
  }

  public void setSelect(int sel) { 
	select = sel;
	pcanvas.setSelect(sel);
   }

   public void setSpeed(String newSpeed)
   {     if (newSpeed.toLowerCase().startsWith("fa"))	// FAST Speed requested
	   	currentSpeed = 0;
	   if (newSpeed.toLowerCase().startsWith("sl"))	// Slow Speed requested
	   	currentSpeed = 1000;
         else  // Normal Speed requested
	   	currentSpeed = 200;
    }

}

class PlotCanvas extends JPanel implements MouseListener {
  ControlPanel cp;
  Database db;
  CurvedGaussMixture cgmix;
  GaussLineMixture glmix;
  int select;
  int xsiz, ysiz;

  public PlotCanvas(CurvedGaussMixture CGMix,
                    GaussLineMixture GLMix,
                    Database DB, int xSize, int ySize) {
    cgmix = CGMix;
    glmix = GLMix;
    db = DB;
    setBackground(Color.green);
    xsiz = xSize;
    ysiz = ySize;
    //setSize(new Dimension(xSize, ySize));
    this.addMouseListener(this);
  }

  public void connectControlPanel(ControlPanel cPanel) {
    cp = cPanel;
  }

  public void paintComponent(Graphics g) {
    super.paintComponents(g);

    g.clearRect(0, 0, xsiz, ysiz);
    db.paint(g);
    if(select == 3) {
      cgmix.paint(g);
    } else if(select == 4) {
      glmix.paint(g);
    }
  }
  

  public void setSelect(int sel) {
    select = sel;
  }

  public void mousePressed(MouseEvent e) {
        cp.dbpush(e.getX(), e.getY()); 
	  repaint();
	//System.out.println("e.getX()="+e.getX()+ "\te.getY())="+e.getY()); 
  }

  public void mouseDragged(MouseEvent e) {}    
  public void mouseReleased(MouseEvent e) {}
  public void mouseMoved   (MouseEvent e) {}
  public void mouseEntered (MouseEvent e) {}
  public void mouseExited  (MouseEvent e) {}
  public void mouseClicked (MouseEvent e) {}

/**  public boolean handleEvent(Event e) {
    switch(e.id) {
      case Event.MOUSE_DOWN:
        cp.dbpush(e.x, e.y);
        repaint();
    }
    return true;
  }
**/

}

class CurvedGaussMixture extends Mixture {
  final int kmax = 10;
  CurvedGaussian [] curvedGaussian = new CurvedGaussian[kmax];

  public CurvedGaussMixture(int xSize, int ySize, Database DB) {
    super(xSize, ySize, DB);
    
    initKernel(new Uniform(xsiz, ysiz, 0.0), typeuniform, 0);
    for(int i = 1; i < kmax; i++) {
	curvedGaussian[i] = new CurvedGaussian(xsiz, ysiz, 0.0);
      initKernel(curvedGaussian[i], typecurvedgauss, i);
    }
    setnk(2);
  }
  public CurvedGaussian getCurvedGaussian(int kernel_index) {
	return curvedGaussian[kernel_index];
   }
}

class GaussLineMixture extends Mixture {
  final int kmax = 10;

  public GaussLineMixture(int xSize, int ySize, Database DB) {
    super(xSize, ySize, DB);
    CurvedGaussian cgmix;
    initKernel(new Uniform(xsiz, ysiz, 0.0), typeuniform, 0);
    for(int i = 1; i < kmax; i++) {
      cgmix = new CurvedGaussian(xsiz, ysiz, 0.0);
      cgmix.setplotline();
      initKernel(cgmix, typecurvedgauss, i);
    }
    setnk(2);
  }
}



