import java.awt.*;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.util.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowAdapter;

class Cube {
  static final int wh=12;
  static Random  rand=new Random();
  Component  comp;
  Image      image[]= new Image[6];
  int        color=0;
  int        rot=0;
  int        masks[]= new int[4];
  public int mask=0;

  public Cube(Component comp, int m0, int m1, int m2, int m3){
    this.comp=comp;
    mask=m0;
    masks[0]=m0;
    masks[1]=m1;
    masks[2]=m2;
    masks[3]=m3;
    setColor();
  }

  public void setColor(){
    int index=rand.nextInt();
    if (index<0) 
     index=(-index);
    color= index%(image.length);
  }

  public void rotate(int i){
    rot+=i;
    if (rot<0) 
      rot=3;
    if (rot>3) 
      rot=0;
    mask = masks[rot];
  }

  public void draw(Graphics g, Image image, int x, int y){
     int index,offset;

     if (mask!=0) {
       index = mask;
       for (int i=0; i<3; i++) {
         offset=0;
         if ((index&0x04)!=0) 
           g.drawImage(image,x+offset,y,comp);
         offset+=wh;
         if ((index&0x02)!=0) 
           g.drawImage(image,x+offset,y,comp);
         offset+=wh;
         if ((index&0x01)!=0) 
           g.drawImage(image,x+offset,y,comp);
         index>>=4;
         y+=wh;
       }
     }else
       g.drawImage(image,x,y,comp);

  }

  private Image createCube(Color c){
   Image temp= comp.createImage(Cube.wh,Cube.wh);
   Graphics g= temp.getGraphics();
   g.setColor(Color.white);
   g.fillRect(0,0,Cube.wh,Cube.wh);
   g.setColor(c);
   g.fillRect(1,1,Cube.wh-1,Cube.wh-1);
   g.dispose();
   return temp;  
  }

  public void paint(Graphics g, int col, int row){
     int x=col*wh;
     int y=row*wh;
     if (image[0]==null) {

       MediaTracker mt= new MediaTracker(comp);

       try {
       Class c = Class.forName("ice");
       image[0]= Toolkit.getDefaultToolkit().getImage(c.getResource("diamg.gif"));
       image[1]= Toolkit.getDefaultToolkit().getImage(c.getResource("diamp.gif"));
       image[2]= Toolkit.getDefaultToolkit().getImage(c.getResource("diamo.gif"));
       image[3]= Toolkit.getDefaultToolkit().getImage(c.getResource("diamb.gif"));
       image[4]= Toolkit.getDefaultToolkit().getImage(c.getResource("diamy.gif"));
       image[5]= Toolkit.getDefaultToolkit().getImage(c.getResource("diamr.gif"));
       for (int i=0; i<image.length; i++) 
         mt.addImage(image[i], i);
       try {
         mt.waitForAll();
       }catch (Exception e){
       }
       }catch (Exception e){
       }

     }
     draw(g,image[color],x,y);
  }
}


public class ice extends Panel
       implements Runnable,
                  KeyListener{
   static int TOTALROWS=20;
   Cube cubes[]= new Cube[4];   
   Cube current=null;
   Image tile=null;
   Image background=null;
   Image tiling=null;
   Image copy=null;
   Random  rand=new Random();
   Graphics gt,gb,gc;
   int  speedSet=10;        //adjust speedSet will adjust after clearing a row the speed
   int  speed=speedSet;     //the speed that pieces fall.
   int  row=0,col=0;
   int  lines=0;
   long grid[]= new long[TOTALROWS+2];
   Label label = new Label("by r.redpath ");

   public ice(String title){
     Frame f= new Frame(" ice ");
     label.setBackground(Color.lightGray);
     f.setLayout(new BorderLayout() );
     f.add("Center", this);
     f.add("South", label);
     f.setBounds(10,10,Cube.wh*16+7,Cube.wh*TOTALROWS);
     setVisible(true);
     cubes[0] = new Cube((Component)this,0x00000074 ,0x00000446 ,0x00000170 ,0x00000622  );
     cubes[1] = new Cube((Component)this,0x00000444 ,0x00000070 ,0x00000444 ,0x00000070  );
     cubes[2] = new Cube((Component)this,0x00000066 ,0x00000066 ,0x00000066 ,0x00000066  );
     cubes[3] = new Cube((Component)this,0x00000270 ,0x00000262 ,0x00000072 ,0x00000464  );
     current= cubes[0];
     setBackground(Color.lightGray);
     addKeyListener(this);
//     addMouseListener(this);
     f.addWindowListener( new WindowAdapter() {
                             public void windowClosing(WindowEvent e) {System.exit(0);}
                             } );
     f.show();
     start();
   }

   public void start(){
     new Thread(this).start();
   }

   private void set(){
     int mask = current.mask;
     --row;
     if (row==0) {
       gb.drawImage(tiling,0,0,this);
       for (int i=0; i<grid.length; i++) 
         grid[i]=0;
       newCurrent();
       row=0;
       speed=speedSet;
       return;
     }
     grid[row]  |= (mask&0x0F)<<(16-col);
     mask>>=4;
     grid[row+1]|= (mask&0x0F)<<(16-col);
     mask>>=4;
     grid[row+2]|= (mask&0x0F)<<(16-col);
     current.paint(gb,col,row);
     row=0;
     speed=speedSet;
     newCurrent();   
     int j=grid.length-1;

     gc.drawImage(tiling,0,0,this);

     for (int i=j; i>=0; i--) 
       if ((grid[i]&0x0007FFF8)==0x0007FFF8) 
         ++lines;
       else{
         gc.drawImage(background,
                      0,j*Cube.wh, 16*Cube.wh, j*Cube.wh+Cube.wh,
                      0,i*Cube.wh, 16*Cube.wh, i*Cube.wh+Cube.wh,
                      this);
         grid[j--]= grid[i];
      }
     if (j>=0) 
       Toolkit.getDefaultToolkit().beep();

     for (int i=j; i>=0; i--) 
       grid[i]=0;
     gb.drawImage(copy,0,0,this);
     repaint();
   }

   private boolean checkGrid(){
     int  mask= current.mask;
     long index;
     int  gridindex=row;

     if (col>12){
       int right=0;
       index = (mask&0x0F) | ((mask>>4)&0x0F) |  ((mask>>8)&0x0F);
       if ((index&0x004)!=0)
          right=1;
       if ((index&0x002)!=0)
          right=2;
       if ((index&0x001)!=0)
          right=3;
       if (col>(16-right))
          col=16-right;
     }

     for (int i=0; i<3; i++) {
        index= mask&0x0F;
        index<<= (16-col);
        if ((grid[gridindex]&index)!=0){
           return false;
        }
        if ( (gridindex>(TOTALROWS-1))&&          //at bottom for this cube
             ((mask&0x0F)!=0) ){
           return false;
        }
        ++gridindex;
        mask>>=4;
     }
     return true;
   }


   public void newCurrent(){
     int index=rand.nextInt();
     if (index<0) 
       index=(-index);
     current=cubes[index%cubes.length];
     current.setColor();
     index=rand.nextInt();
     if (index<0) 
       index=(-index);
     col= index%16;
     checkGrid();
   }

   public void run(){
      Thread.currentThread().setName("ice_thread");
      int count=0;
      try {
        while (true) {
           Thread.sleep(25);
           if (count++<speed) 
             continue;
           count=0;
           ++row;
           if (checkGrid())
               paint(getGraphics());
           else
               set();
        }
      }catch (Exception e){
        e.printStackTrace();
      }
   }


   public void keyTyped(KeyEvent e){
   }

   public void keyPressed(KeyEvent e){
     switch (e.getKeyCode()){
      case KeyEvent.VK_UP:
         current.rotate(1);
         break;
      case KeyEvent.VK_DOWN:
         current.rotate(-1);
         break;
      case KeyEvent.VK_LEFT:
         if (col!=0) 
          --col;
         if (checkGrid())
           paint(getGraphics());
         else
           --col;
         break;         
      case KeyEvent.VK_RIGHT:
         if (col!=15) 
          ++col;
         if (checkGrid())
           paint(getGraphics());
         else
           --col;
         break;                 
      default:
         speed=0;
     }

   }

   public void keyReleased(KeyEvent e){
   }

   public void update(Graphics g){
     paint(g);
   }

   public void paint(Graphics g){
     g.drawImage(background,0,0,this);
     current.paint(g,col,row);
     label.setText("by r.redpath        lines "+lines);
   }

   private void clearBackground(){

   }

   public void doLayout(){
     super.doLayout();
     Rectangle rect = getBounds();
     TOTALROWS=rect.height/Cube.wh;
     if (TOTALROWS<2) 
        TOTALROWS=2;
     grid= new long[TOTALROWS+2];
     int h=TOTALROWS*Cube.wh;
     int w=16*Cube.wh;

     if (background==null) {
       MediaTracker mt= new MediaTracker(this);
       try {
         Class c = Class.forName("ice");
         tile =Toolkit.getDefaultToolkit().getImage(c.getResource("tback.gif"));
       }catch (Exception e){
       }

       mt.addImage(tile, 1);
       try {
         mt.waitForID(1);
       }catch (Exception e){
       }
     }

       copy       = createImage(w,h);
       background = createImage(w,h);
       gb         = background.getGraphics();
       gc         = copy.getGraphics();
       tiling     = createImage(w,h);
       gt         = tiling.getGraphics();

       if (tile==null) {
          gb.setColor(Color.white);
          gb.fillRect(0,0,w,h);
          return;
       }

       int ni = w/tile.getWidth(this)+1;
       int nj = h/tile.getHeight(this)+1;
       for (int i=0; i<ni; i++)
          for (int j=0; j<nj; j++)
             gt.drawImage(tile,i*tile.getWidth(this),
                               j*tile.getHeight(this),this);

       gb.drawImage(tiling,0,0,this);
   }

   public static void main(String args[]){
     ice f= new ice(" ice ");
   }

}