/*-*-c++-*-*/

/*
  UltraMaster Dethtris    --    UltraMaster Group 
  ====================
  karl@claremont.com
  julian@claremont.com
  david@claremot.com
  http://ariel.claremont.com/ultram/tetris/index.html
  
  This source originally by Karl, but Julian changed to emacs tabbing and 
  added the ability to re-start the game 2/3/96

  */

import java.awt.*;
import java.lang.*;
import java.applet.*;
import network;
import box;
import piece;
import board;
import scoreboard;
import terminal;
import constants;

public class tetris extends java.applet.Applet implements Runnable {
  private Image offscreenimage;
  private Graphics offscreengraphics;
  
  boolean dolife = false;
  boolean mustdrawboard = false;
  int ok2drop;
  
  int lifespeed = 200;
  int ledstate = 0;
  
  network thenet;
  int frequency[];
  int totalpieces;
  
  scoreboard myscoreboard;
  
  boolean deathmatch = false;
  
  Image buffer;
  
  int oldscore;
  int oldlevel;
  int oldlines;
  
  board thisgrid;
  board nextgrid;
  board lifegrid;
  
  piece thispiece;
  piece nextpiece;
  
  Graphics g;
  Graphics gbuffer;
  
  int gamespeed = 1000;
  double powerloss = .85;
  
  Thread t1;
  boolean oktothrow = true;
  Event reenter[];
  Color ledcolor;
  boolean dontdoit;
  boolean reset=false;  //Reset button -start over.
  
  public void init() {	
    // make stuff
    dolife = false;
    mustdrawboard = false;
    
    ok2drop = 0;
    thisgrid = new board(21,12,2,2,true,20);
    nextgrid = new board(5,5,215,2,false,20);
    lifegrid = new board(81,42,2,2,true,5);
    myscoreboard = new scoreboard(0,0);
    frequency = new int[7];
    
    //0..7 holds instances of a particular piece
    for (int i = 0; i < 7; i++){
      frequency[i] = 0;
    }
    
    //set up graphics
    g = getGraphics();
    if (offscreenimage == null){
      offscreenimage = createImage(204,404);
      offscreengraphics = offscreenimage.getGraphics();
    }
    
    //init the grids
    for (int i=0; i < 21; i++){
      thisgrid.grid[i][0].setBox(0);
      thisgrid.grid[i][11].setBox(0);
      thisgrid.grid[i][0].solid = true;
      thisgrid.grid[i][11].solid = true;
    }
    
    for (int i = 0; i < 12; i++){
      thisgrid.grid[20][i].setBox(0);
      thisgrid.grid[20][i].solid = true;
    }
    
    reenter = new Event[10];
    for (int i = 0; i < 10; i ++){
      reenter[i] = null;
    }
    /* we can't repaint until the pieces are ready */
    dontdoit = true;
  }
  
  public void run() {
    
    //init network and get two pieces
    thenet = new network(this);
    
    thispiece = new piece(thenet.getPiece(),thisgrid,g);
    nextpiece = new piece(thenet.getPiece(),nextgrid,g);
    /* from now on, it's ok to paint the screen */
    dontdoit = false;
    
    nextpiece.xpos = 2;
    nextpiece.ypos = 2;
    
    totalpieces += 2;
    
    frequency[nextpiece.type] += 1;
    frequency[thispiece.type] += 1;
    
    thispiece.drawPiece();
    
    setLayout(null);
    resize(324,404);
    show();
    lifegrid.drawgrid = false;
    mustdrawboard = true;
    paint(g);
    
    requestFocus();
    
    do {
      try {
	Thread.sleep(getSpeed());           
      } 
      catch (InterruptedException e) {
      }
      
      if (dolife) {
	lifegrid.drawBoard(offscreengraphics);
	g.drawImage(offscreenimage,0,0,null);
	lifegrid.doLife();
      } else {
	deliverEvent(new Event(this,0 ,java.awt.Event.KEY_PRESS,0,0,Event.F12,0));
      }
      if (reset) {
	resetme();
	reset=false;
      }
    } while (!deathmatch);
  }
  
  public void resetme() {
    String tempname;

    init();
    //init network and get two pieces
    
    //REMED FOR NETWORK STUB
    //thenet.stop();

    //tempname=thenet.username.getText();
    //thenet = new network(this);
    //thenet.username.setText(tempname);

    thispiece = new piece(thenet.getPiece(),thisgrid,g);
    nextpiece = new piece(thenet.getPiece(),nextgrid,g);
    /* from now on, it's ok to paint the screen */
    dontdoit = false;
    
    nextpiece.xpos = 2;
    nextpiece.ypos = 2;
    
    totalpieces += 2;
    
    frequency[nextpiece.type] += 1;
    frequency[thispiece.type] += 1;
    
    thispiece.drawPiece();
    
    setLayout(null);
    resize(324,404);
    show();
    lifegrid.drawgrid = false;
    mustdrawboard = true;
    paint(g);
    
    requestFocus();
  }

  public boolean handleEvent(Event e){
    int tmpflower = 0;
    int tmpscore = 0;
    
    //Re-start the game
    if (dolife && (e.id==Event.MOUSE_DOWN)) {
      reset=true;
      return true;
    }

    if (dolife) return true;
    switch (e.id){
    case Event.KEY_ACTION:
    case Event.KEY_PRESS:
      
      if (!serialize(e)){ 
	return true;
      }
      
      while (e != null) {
	
	//Figure out what key has been pressed, and act accordingly
	switch (e.key){
	case Event.F12:
	  //The event we throw in the main loop
	  tmpflower = thispiece.move(constants.MOVE_DOWN);
	  testDone(tmpflower);
	  break;
	  
	case Event.F11:
	  myUpdate(g);
	  break;
	  
	case Event.UP:
	case 'i':
	  thisgrid.level += 1;
	  drawData(g);
	  break;
	  
	case Event.LEFT:
	case '4':
	case 'j':
	case 'J':
	  tmpflower = thispiece.move(constants.MOVE_LEFT);
	  break;
	  
	case Event.RIGHT:
	case '6':
	case 'l':
	case 'L':
	  tmpflower = thispiece.move(constants.MOVE_RIGHT);
	  break;
	  
	case Event.DOWN:
	case '5':
	case 'k':
	case 'K':
	  tmpflower = thispiece.move(constants.ROTATE);
	  break;
	  
	case ' ':
	  tmpflower = thispiece.drop();
	  testDone(tmpflower);
	  break;
	  
	case 'g':
	  thisgrid.drawgrid = !thisgrid.drawgrid;
	  mustdrawboard = true;
	  myUpdate(g);
	  break;
	  
	case 'e':
	  thisgrid.emboss = !thisgrid.emboss;
	  mustdrawboard = true;
	  myUpdate(g);
	  break;
	}
	e = getNextevent();
      }
      
    }	
    return true;
  }
  
  public void start() {
    /* don't start new threads if app is "over" */
    if (dolife) return;
    deathmatch=false;
    t1 = new Thread(this);
    t1.start();  
  }

  public void stop() {          
    deathmatch=true;
    destroy();
    //REMED FOR NETWORK STUB
    //thenet.stop();
  }
  
  
  //Draw Background
  public void paint(Graphics g) {    
    if (dontdoit) {
      return;
    }
    mustdrawboard = true;  
    Dimension d=size();
    g.setColor(constants.NULL_COLOR);
    g.fillRect(0,0,324,404);
    
    //Draw the next shape emboss area
    g.setColor(constants.EMBOSS_SHADOW);
    g.drawLine(209,0,209,90);
    g.drawLine(210,0,210,89);
    g.drawLine(209,0,319,0);
    g.drawLine(209,1,318,1);
    g.setColor(constants.EMBOSS_HILIGHT);
    g.drawLine(319,1,319,90);
    g.drawLine(318,2,318,90);
    g.drawLine(211,89,319,89);
    g.drawLine(210,90,319,90);
    
    //draw embossing for the button area
    
    g.setColor(constants.EMBOSS_SHADOW);
    g.drawLine(209,190,209,404);
    g.drawLine(210,190,210,403);
    g.drawLine(209,190,319,190);
    g.drawLine(209,191,318,191);
    
    g.setColor(constants.EMBOSS_HILIGHT);
    g.drawLine(319,191,319,404);
    g.drawLine(318,192,318,404);
    g.drawLine(211,403,319,403);
    g.drawLine(210,404,319,404);
    
    myUpdate(g);
  }
  
  public void update(Graphics g){
    myUpdate(g);
  }
  
  //Update Graphics
  public void myUpdate(Graphics g) {
    int heightofbar = 0;
    int biggestbar = 0;
    float multip;
    
    Color freqhilight = constants.NULL_COLOR;
    Color freqnormal = constants.NULL_COLOR;
    Color freqshadow = constants.NULL_COLOR;
    
    drawData(g);
    
    nextpiece.drawPiece();
    
    //Draw the buffered board
    if (mustdrawboard){
      mustdrawboard = false;
      //draw the embossing of the playing area
      offscreengraphics.setColor(constants.NULL_COLOR);
      offscreengraphics.fillRect(0,0,324,404);
      
      offscreengraphics.setColor(constants.EMBOSS_SHADOW);
      offscreengraphics.drawLine(0,0,0,404);
      offscreengraphics.drawLine(1,0,1,403);
      offscreengraphics.drawLine(0,0,204,0);
      offscreengraphics.drawLine(0,1,203,1);
      
      offscreengraphics.setColor(constants.EMBOSS_HILIGHT);
      offscreengraphics.drawLine(1,404,204,404);
      offscreengraphics.drawLine(2,403,204,403);
      offscreengraphics.drawLine(203,2,203,404);
      offscreengraphics.drawLine(204,1,204,404);
      
      if (!dolife) thisgrid.drawBoard(offscreengraphics);	
      else lifegrid.drawBoard(offscreengraphics);
      g.drawImage(offscreenimage,0,0,null);
    }
    else {
      thispiece.drawPiece();
    }
    
    //Draw the frequencies
    multip = 0;
    biggestbar = 0;		
    for (int i = 0; i < 7; i++){
      
      if (freq(i) > multip){
	multip = freq(i);
      }		
    }
    
    if (multip == 0) {
      biggestbar = 200;
    }
    
    else{
      biggestbar = (int)(200 / multip);
    }
    
    g.setColor(constants.NULL_COLOR);
    g.fillRect(211,192,106,209);
    
    for (int i = 0; i < 7; i++){
      heightofbar = (int)(freq(i) * biggestbar);		
      switch(i){
      case constants.LINE:
	freqhilight = constants.RED_HILIGHT;
	freqnormal = constants.RED_NORMAL;
	freqshadow = constants.RED_SHADOW;
	break;
      case constants.T:
	freqhilight = constants.YELLOW_HILIGHT;
	freqnormal = constants.YELLOW_NORMAL;
	freqshadow = constants.YELLOW_SHADOW;
	break;
      case constants.LEFT_S:
	freqhilight = constants.BLUE_HILIGHT;
	freqnormal = constants.BLUE_NORMAL;
	freqshadow = constants.BLUE_SHADOW;
	break;
      case constants.RIGHT_S:
	freqhilight = constants.GREEN_HILIGHT;
	freqnormal = constants.GREEN_NORMAL;
	freqshadow = constants.GREEN_SHADOW;
	break;
      case constants.LEFT_L:
	freqhilight = constants.LTBLUE_HILIGHT;
	freqnormal = constants.LTBLUE_NORMAL;
	freqshadow = constants.LTBLUE_SHADOW;
	break;
      case constants.RIGHT_L:
	freqhilight = constants.PURPLE_HILIGHT;
	freqnormal = constants.PURPLE_NORMAL;
	freqshadow = constants.PURPLE_SHADOW;
	break;
      case constants.BOX:
	freqhilight = constants.ORANGE_HILIGHT;
	freqnormal = constants.ORANGE_NORMAL;
	freqshadow = constants.ORANGE_SHADOW;
	break;
      }
      
      if (heightofbar > 0){
	g.setColor(freqnormal);
	g.fillRect(217 + i * 14, 402 - heightofbar, 10, heightofbar);
	
	g.setColor(freqhilight);
	g.drawLine(217 + i * 14, 402 - heightofbar, 227 + i * 14, 402 - heightofbar);
	g.drawLine(217 + i * 14, 403 - heightofbar, 226 + i * 14, 403 - heightofbar);
	g.drawLine(217 + i * 14, 402 - heightofbar, 217 + i * 14, 402);
	g.drawLine(218 + i * 14, 402 - heightofbar, 218 + i * 14, 401);
	
	g.setColor(freqshadow);
	g.drawLine(227 + i * 14, 403 - heightofbar, 227 + i * 14,402);
	g.drawLine(226 + i * 14, 404 - heightofbar, 226 + i * 14, 402);
	g.drawLine(219 + i * 14, 401, 227 + i * 14, 401);
	g.drawLine(218 + i * 14, 402, 226 + i * 14, 402);
      }
      
    }
    Toolkit t=java.awt.Toolkit.getDefaultToolkit();
    t.sync();
  }
  
  public void drawData(Graphics g) {
    //Draw the score, level, and rows fields
    g.setColor(constants.NULL_COLOR);
    g.drawString("Score: " + oldscore,214,115);
    g.drawString("Level: " + oldlevel,214,135);
    g.drawString("Lines: " + oldlines,214,155);
    
    oldscore = thisgrid.getScore();
    oldlevel = thisgrid.getLevel();
    oldlines = thisgrid.getLines();
    
    g.setColor(new Color(0,0,0));
    
    g.drawString("Score: " + thisgrid.getScore(),214,115);
    g.drawString("Level: " + thisgrid.getLevel(),214,135);
    g.drawString("Lines: " + thisgrid.getLines(),214,155);
  }
  
  public void testDone(int tmpscore){
    Font f;
    if (thispiece.timetodie){
      thisgrid.score += tmpscore;
      myUpdate(g);
      thenet.done(thisgrid.score, thisgrid.lines, getScoreboard());
      setlife();
      //Draw message about re-starting.
      g.setColor(constants.NULL_COLOR);
      g.fillRect(210,2,107,85);

      g.setColor(Color.black);
      f=getFont();
      g.setFont(new Font(f.getName(),Font.BOLD,20));
      g.drawString("GameOver",211,20);
      g.drawString("Click to",225,50);
      g.drawString("play again",211,70);
      g.setFont(f);
      dolife = true;
    }
    
    else if (tmpscore != 0){
      mustdrawboard = true;
      thisgrid.score += tmpscore;
      thispiece.copyPiece(nextpiece);
      thispiece.drawPiece();
      nextpiece.erasePiece();
      nextpiece.newPiece(thenet.getPiece(),2,2);
      frequency[nextpiece.type] += 1;
      totalpieces += 1;
      deliverEvent(new Event(this,0 ,java.awt.Event.KEY_PRESS,0,0,Event.F11,0));
    }
    
  }
  
  public int getSpeed(){
    int tmp;
    if (!dolife)
      return (tmp = (int)(gamespeed * (Math.pow(powerloss,thisgrid.level)))) < 5 ? 5 : tmp;
    else return lifespeed;
  }
  
  public float freq(int i){
    return (float)(((float)frequency[i] / (float)totalpieces));
  }
  
  public scoreboard getScoreboard(){
    myscoreboard.setScore(thisgrid.score);
    myscoreboard.setLines(thisgrid.lines);
    
    for (int i = 1; i < 11; i ++){
      for (int j = 0; j < 20; j ++){
	myscoreboard.grid[i-1][j] = thisgrid.grid[j][i].boxcolor;
      }
    }
    
    return myscoreboard;
  }
  
  boolean serialize(Event e){
    for (int i = 9; i > 0; i --){
      if (reenter[i-1] != null) {
	reenter[i] = e;
	return false;
      }
    }
    reenter[0] = e;
    return true;
  }
  
  Event getNextevent() {
    for (int i=0; i< 9; i++) {
      reenter[i] = reenter[i+1];
    }
    reenter[9] = null;
    return reenter[0];
  }
  
  void setlife(){
    for (int i = 0; i < 20; i ++){
      for (int j = 1; j < 11; j ++){
	for (int k = i * 4; k < i * 4 + 4; k ++){
	  for (int l = (j - 1) * 4 + 1; l < (j - 1) * 4 + 5; l ++){
	    lifegrid.grid[k][l].solid = true;
	    lifegrid.grid[k][l].setBox(thisgrid.grid[i][j].boxcolor);
	  }
	}
      }
    }
  }
}










