/*
** Authors: 	A. Carra, Kelek, J. Wojdacki
**
** Title: 	matrixDisplay
**		
** Desc:	A quick stab at the cool graphics from the movie
**		the matrix.
**		for windows, this program requires that your 'serif' 
**		font is correctly aliased to a unicode font via the 
**		<java dir>\lib\font.properties file. Try messing with
**		the velos and probs variables to achieve the desired
**		effect.
**
** Changes:     <WOJDACKI> Added RNG Thread to speed up operation
**		<CARRA> Added matrix character matrix to streamline
**			rng usage and improve appearance
** 
**
*/


import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.*;

import RCGthread.*;
import RNGthread.*;

public class matrixDisplay extends Frame
implements ImageObserver, WindowListener, Runnable {
    public int vRows[];	//unused
    public int vCols[];	//unused
    public int nactive[]; //number of active cells in the column ('stream length')
    public int offsets[]; //offsets of the columns from 0
    public int velos[];	//velocities of the columns (right now, 0-4)
    public int probs[];	//velocities of the columns (right now, 0-4)
    public int upd[];
    public int nRows = 0;	//number of rows (estimated)
    public int nCols = 0;	//number of columns (estimated)
    
    public int vI;	
    
    public char[] carr;
    public char[][] matrix;

    
    private Thread repaintThread;
    private RCGthread RCG;
    private RNGthread RNG;

    transient private Image backImage = null;	//background image for dbuffering
    transient private Graphics bg     = null;	//gfx context for dbuffering
    
    public Color flash;
    public Color tail;
    public Color ngreen;
    
    //constructor
    public matrixDisplay() {
	super();	       		
	
	if((this.getSize().width > 0) && (this.getSize().height > 0)){
		nCols = (this.getSize().width/15);
		nRows = (this.getSize().height/15);
	    }
	else
	    {
		nCols = 400/15;
		nRows = 250/15;
	    }
	
	try{
	    matrix = new char[nCols][nRows];
	    nactive = new int[nCols];
	    offsets = new int[nCols];
	    velos = new int[nCols];
	    probs = new int[nCols];
	    upd = new int[nCols];
	    
	    //setup initial r/c/v s
	    for(int i = 0; i < nCols; i++)
		{
		    nactive[i] = (int)(java.lang.Math.random()*(nRows/1.5));
		    offsets[i] = 15-(nactive[i]*15);//(int)(java.lang.Math.random()*nRows);
		    velos[i] = (int)((java.lang.Math.random()*12)+2);
		    probs[i] = (int)((java.lang.Math.random()*10));
		    upd[i] = 0;
		}
	}catch(Exception ex){
	    System.err.println("Initialization error:"+ ex);
	    System.err.println("Try resizing your window to fix it");	
	}
	//window resizing 
	resizer sizer = new resizer();
	addComponentListener(sizer);
	
	addWindowListener(this);
	
	flash = new Color(230,255,230);
	tail = Color.green.darker().darker();
	ngreen = Color.green.darker();
	
	repaintThread = new Thread(this);
	repaintThread.start();
	
	RCG = new RCGthread(10240);
	RCG.start();
	
	RNG = new RNGthread(10240);
	RNG.start();

	carr = new char[1];
	
    }
    
    public void run () {
	while (true) {
	    try { Thread.sleep(110); } catch (InterruptedException e) {}
	    repaint();
	}
    }
    
    
    public void update (Graphics g) {
	if( backImage == null || backImage.getWidth(this) != this.getSize().width ||
	    backImage.getHeight(this) != this.getSize().height ) {
	    MediaTracker mt = new MediaTracker( this );
	    backImage = createImage( this.getSize().width, this.getSize().height );
	    mt.addImage( backImage, 0 );
	    try{ mt.waitForAll(); }catch(Exception e){}
	    bg = backImage.getGraphics();
	}
	
	if( bg != null && this != null ) {
	    bg.setColor( Color.black );
	    bg.setFont(new Font("serif", Font.BOLD, 15));
	    bg.fillRect( 0, 0, this.getSize().width, this.getSize().height );
	}
	
	paint(bg);
	if (backImage != null) g.drawImage(backImage,0,0,this);
    }
    
    //double buffered painting... 
    public void paint(Graphics g)
    {
	int colctr;
	
	
	try{
	    for(int il = 0; il < nCols; il++)
		{
		    double tempR = RNG.get_RNG();

		    if(offsets[il] < nactive[il]*15)
		    	{
			    offsets[il] += 15;
		    	    upd[il] = 3;
			}
		
		    else if ((int)(tempR*96) <= ( probs[il] * (velos[il]-5))) 
		 	{
			    offsets[il] += 15 ;
		    	    upd[il] = 2;
			}

		    else if((int)(RNG.get_RNG()*100) <= ( probs[il] * (velos[il]-5)))
			{
			    offsets[il] = offsets[il] + velos[il] ;
		    	    upd[il] = 1;
			}

		    else
			{
			    upd[il] = 0;
			}
		}
	}catch(Exception ex){/*System.err.println("paint caught:"+ex);*/}		
	
	int h = this.getSize().height;	
	int jptemp;
	int iptemp;
	
	try{
	    //loop, updating each column's position
	    for (int i = 1; i < nCols; i++ )
		{
		    
		    iptemp = i * 15;
		    colctr = nactive[i];
		    
		    for (int j = 0; j <= colctr; j++ )
			{
			    jptemp = j * 15;
			    
			    if ((j == 0 ) /*&& (upd[i] == 1)*/)
				{
				    bg.setColor(tail);
				}
			    else if (j == 2)
				{
				    bg.setColor(ngreen);
				}

			    if (upd[i] > 1)			    
				{
				    if (j == colctr)
				    {
					if (upd[i] == 2)
						bg.setColor(flash);
				    	matrix[i][j] = RCG.get_RCG();
				    }
				    else
				    	matrix[i][j] = matrix[i][j+1];

				    carr[0] = matrix[i][j];
				    bg.drawChars( carr,
						  0,1, (iptemp), ( (offsets[i]) + (jptemp) ) % h );
				}

			    else if (upd[i] == 1 )
				{
				    
				    if (j == colctr)
					bg.setColor(flash);
				    
				    carr[0] = RCG.get_RCG();
				    matrix[i][j] = carr[0];
				    bg.drawChars( carr,
						  0,1, (iptemp), ( (offsets[i]) + (jptemp) ) % h );
				}
			    else
				{
				    if(matrix[i][j] == '\0')
				    {
					    carr[0] = RCG.get_RCG();
				    	    matrix[i][j] = carr[0];
				    }	
				    else
					    carr[0] = matrix[i][j];
				    bg.drawChars( carr,
						  0,1, (iptemp), 
						  ( (offsets[i] ) + (jptemp) ) % h );
				}			    
			}
		}	
	    
	}catch(Exception ex){/*System.err.println("paint error:"+ex);*/}
	
	
    }
   
    
    //resizing 
    class resizer extends java.awt.event.ComponentAdapter
    {
	public void componentResized(java.awt.event.ComponentEvent event)
	{
	    Component c = event.getComponent();
	    
	    nCols = c.getSize().width/15;
	    
	    nRows = c.getSize().height/15;

	    matrix = new char[nCols][nRows];	    
	    nactive = new int[nCols];
	    offsets = new int[nCols];
	    velos = new int[nCols];
	    probs = new int[nCols];
	    upd = new int[nCols];
	    
	    for(int il = 0; il < nCols; il++)
		{
				//System.err.println(il);
		    nactive[il] = (int)(RNG.get_RNG()* (nRows/1.5));
		    offsets[il] = 15-(nactive[il]*15);/*(int)(RNG.get_RNG()* nRows);*/
		    velos[il] = (int)((RNG.get_RNG()*12)+2);
		    probs[il] = (int)((RNG.get_RNG()*10));
		    upd[il] = 0;
		}
	    try{
		c.repaint();
		c.setVisible(true);
	    }catch(Exception ex){;}
	}
    }
    
    //mouse 
    class mad extends java.awt.event.MouseAdapter
    {
	public void mouseClicked(java.awt.event.MouseEvent event)
	{
	    repaint();
	}
    }
    
    // WindowListener
    public void windowClosing (WindowEvent event) {
	dispose();
    }
    
    public void windowClosed (WindowEvent event) {
        close();
	System.exit(0);
    }

  public void close() {
        repaintThread.stop();
 	repaintThread = null;
        RNG.stop();
 	RNG = null;
        RCG.stop();
	RCG = null;
 	System.exit(0);
  }

    
    public void windowActivated (WindowEvent event) { }
    public void windowDeactivated (WindowEvent event) { }
    public void windowDeiconified (WindowEvent event) { }
    public void windowIconified (WindowEvent event) { }
    public void windowOpened (WindowEvent event) { }
    
}

