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

import java.awt.*;
import java.net.*;
import java.io.*;
import java.lang.*;
import scoreboard;
import terminal;
import constants;
import java.util.Random;   //For Random
import tcp_conn;
import SECRET;
/*
SECRET.java contains something like this:

***********************************

public class SECRET {
 final static int SECRET=.5;
}

***********************************

You may use any function you like, so long as it matches the function
in SECRET.h
*/

public class network extends Frame implements Runnable { 

  //Our new server, in our own domain, on a T1!
  //String server="ariel.cobite.com";
  //Our test server
  String server="localhost";
  int portnumber=5633;
  int ourport=portnumber-1;

  // "REAL" variables
  boolean ok2go=false;
  boolean singleplay=false;
  boolean time_to_die=false;
  boolean gameover=false;
  boolean startsig=false;
  int upevery=10;               //Update boards every upevery seconds
  int loopnum;                  //Loop counter
  int piecenum=0;               //What piece number is it?  0 means start-up
  int finalscore=-1;            //Flags the run-loop to finish game
  int finallines=-1;
  int lastupdate=-1; 
  int players;

  // Main thread object
  Thread mainloop;

  //New, Fast network class
  tcp_conn client;

  // Visual components
  mybutton new1, new2, new3, new4, new5, new6, new7, new8; 
  mybutton join;
  Label namelabel, instr1, instr2, instr3, instr4;
  Label gamelabel;
  TextArea message;            //Area for displaying messages
  TextField username;
  List games;                //Listbox showing running games
  Panel lpanel;                //Panel for games List-box
  terminal boards;             //The other window which shows opponents
  tetris grandparent;          //The main applet window

  scoreboard myscoreboard;     //Data structures used to pass opponent info
  scoreboard otherboard;
  char grid[];

  //mybutton join;
  //mybutton newb;
  //Label slotlabel;
  //TextField slots;

  Random rndpiece=new Random();  //Random number generator

  public network(tetris gp) {

    //Search ports near our server in case it's changed
    do {
      ourport++;
      System.out.println("Trying port number:"+ourport);
      client=new tcp_conn(server, ourport);
    } while ((client.server==null) && (ourport < portnumber+20));

    grandparent=gp;
    int WIDTH=500;
    int HEIGHT=420;
    setLayout(null);

    int i;
    pack();
    reshape(100,100,WIDTH,HEIGHT);
    
    //Inanimate components - labels and instructions
    gamelabel=new Label("WINDOWS RULES!", Label.CENTER);
    gamelabel.reshape(5, 25, (WIDTH-10), 15);
    add(gamelabel);

    instr2=new Label("LINUX ROCKS!", Label.CENTER);
    instr2.reshape(5, 400, (WIDTH-10), 15);
    add(instr2);

    namelabel=new Label("FIRST, please enter your name:", Label.RIGHT);
    namelabel.reshape(5, 45, (WIDTH-10)/2, 30);
    add(namelabel);
    
    instr1=new Label("< ----- NEXT, CHOOSE ONE ------ >", Label.CENTER);
    instr1.reshape(5, 80, (WIDTH-10), 20);
    add(instr1);
    instr3=new Label("Select a game to JOIN.", Label.CENTER);
    instr3.reshape(5, 105, (WIDTH-10)/2, 15);
    add(instr3);

    instr4=new Label("Click here to  START a new game.", Label.CENTER);
    instr4.reshape(WIDTH/2, 105, (WIDTH-10)/2, 15);
    add(instr4);
    
    //User name field and choice box for picking existing game
    username=new TextField();
    username.reshape(WIDTH/2, 45, (WIDTH-10)/2, 30);
    add(username);

    games=new List();
    games.setFont(new Font("Courier", Font.BOLD, 12));
    getgames(games);
    //
    //This listbox inside of a panel is necessary in windows, not linux !?
    lpanel=new Panel();
    lpanel.reshape(5, 120, (WIDTH-10)/2-5, 110);
    lpanel.setLayout(new BorderLayout());
    lpanel.add("Center", games);
    add(lpanel);
    
    //The eight buttons to start new games...
    new1=new mybutton("1 player game", 1);
    new2=new mybutton("2 player game", 2);
    new3=new mybutton("3 player game", 3);
    new4=new mybutton("4 player game", 4);
    new5=new mybutton("5 player game", 5);
    new6=new mybutton("6 player game", 6);
    new7=new mybutton("7 player game", 7);
    new8=new mybutton("8 player game", 8);
    join=new mybutton("Start Multi-player", 9);
    new1.reshape( ((WIDTH-10)/2) + ((       1  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       1  //ROW
         -1)), (WIDTH-10)/4, 35);
    new2.reshape( ((WIDTH-10)/2) + ((       2  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       1  //ROW
         -1)), (WIDTH-10)/4, 35);
    new3.reshape( ((WIDTH-10)/2) + ((       1  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       2  //ROW
         -1)), (WIDTH-10)/4, 35);
    new4.reshape( ((WIDTH-10)/2) + ((       2  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       2  //ROW
         -1)), (WIDTH-10)/4, 35);
    new5.reshape( ((WIDTH-10)/2) + ((       1  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       3  //ROW
         -1)), (WIDTH-10)/4, 35);
    new6.reshape( ((WIDTH-10)/2) + ((       2  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       3  //ROW
         -1)), (WIDTH-10)/4, 35);
    new7.reshape( ((WIDTH-10)/2) + ((       1  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       4  //ROW
         -1)), (WIDTH-10)/4, 35);
    new8.reshape( ((WIDTH-10)/2) + ((       2  //COLUMN
         -1)*(WIDTH-10))/4, 120+(36*(       4  //ROW
         -1)), (WIDTH-10)/4, 35);
    join.reshape(5, 230, (WIDTH-10)/2-5, 30);
    add(new1);
    add(new2);
    add(new3);
    add(new4);
    add(new5);
    add(new6);
    add(new7);
    add(new8);
    add(join);
    
    //The big window to display messages and entertaining info.
    message=new TextArea();
    message.setFont(new Font("Courier", Font.PLAIN, 12));
    message.reshape(5,265,WIDTH-10,HEIGHT-265-30);
    message.setEditable(false);
    add(message);

    pack();
    reshape(0,0,WIDTH,HEIGHT);
    show();
    setResizable(false);

    username.requestFocus();      //First thing to get
    start();
  }
  
  //THIS IS THE LOOP WHICH PERFORMS BACKROUND TASKS.
  public void run() {

    do {
      //Sleep for a second.
      try {
	Thread.sleep(1000);
      } catch (InterruptedException e) {
	return;
      }      

      //Finish game - tell server
      if ((finalscore > -1) && (!gameover)) { 
	donework(finalscore, finallines);
	gameover=true;
      }

      //Update scoreboards from server after death
      if ((gameover)
	  && (loopnum>=upevery)
	  && (!singleplay)
	  ) {
	loopnum=0;
	deadboards();
      }

      //Update scoreboards during game play.
      if ((loopnum>=upevery)
	  && (piecenum !=0)
	  && (ok2go)
	  && (!singleplay)
	  && (!gameover)
	  ) {
	loopnum=0;
	updateboards();
      }

      //Increment number to count when to update boards
      loopnum++;
    } while (!time_to_die);
  }
  
  public void start() {   //Start the thread
    time_to_die=false;
    mainloop=new Thread(this);
    mainloop.start();
  }

  public void stop() {    //Stop the thread
    dispose();
    startsig=false;
    client.close();
    if ((!singleplay) && (boards != null)) {
      boards.dispose();
    }
  }

  //THIS FUNCTION GETS OTHER PLAYERS BOARDS DURING GAME PLAY
  public void updateboards() {
    packet pack=new packet();
    String result;
    byte bytebuf;
    int i, j;
    int boardnum;
    //***** Send out board as the request.
    grid=new char[201];
    //  if (!gameover) {  //myscoreboard stores our board after death.
    myscoreboard=grandparent.getScoreboard();
    otherboard=grandparent.getScoreboard(); //Kludge so I don't have
    // to construct a scoreboard.
    //  }
    for (i=0; i<10; i++) {
      for (j=0; j<20; j++) {
	try {
	  grid[ ((i*20)+j) ]=
	    new String().valueOf(myscoreboard.grid[i][j]).charAt(0);
	} catch (StringIndexOutOfBoundsException evt) {
	  neterr("StringIndexOutOfBoundsException\n");
	  neterr("in String().charAt(0)\n");
	  return;
	}
      }
    }
    if (!singleplay) {          //Don't show boards in single-play
      neton();
      pack.data=new String( myscoreboard.score+","+
                            myscoreboard.lines+","+
                            new String(grid));
      pack.length=pack.data.length();
      pack.type=7;
      pack.index=0;
      client.writepack(pack);

      //*******  Receive back all the other boards
      pack=client.readpack();   //Priming read
      while (pack.type != -1){
	otherboard.myname=new String(pack.data);  //Name

	pack=client.readpack();
        boardnum=pack.index;
        boardnum--;

        otherboard.score=0;
	pack=client.readpack();
        otherboard.score=pack.index;

        otherboard.lines=0;
	pack=client.readpack();
        otherboard.lines=pack.index;

	pack=client.readpack();
	result=pack.data;

        for (i=0; i<10; i++) {
          for (j=0; j<20; j++) { 
            try {
	      otherboard.grid[i][j]=result.charAt((i*20)+j);
            } catch (StringIndexOutOfBoundsException evt) {
	      System.out.print("Error: i="+i+" j="+j);
              neterr("StringIndexOutOfBoundsException\n");
              neterr("on istream.readLine()\n");
	      
              //return;
            }
	    //((int)bytebuf)-48;
          }
        }
	pack=client.readpack();
        boards.setBoard(boardnum, otherboard);
      } // End of While
      netoff();
    } // End of if singleplay
    return;
  }

  //THIS IS THE FUNCTION TO GET OTHER PLAYERS BOARDS AFTER DEATH
  public void deadboards() {
    packet pack=new packet();
    String result;
    byte bytebuf;
    int i, j;
    int boardnum;
    neton();
    
    //***** Send out game number only.
    pack.type=8;
    pack.index=0;
    client.writepack(pack);

    //*******  Receive back all the other boards
    pack=client.readpack();   //Priming read
    while (pack.type != -1){
      otherboard.myname=new String(pack.data);  //Name

      pack=client.readpack();
      boardnum=pack.index;
      boardnum--;

      otherboard.score=0;
      pack=client.readpack();
      otherboard.score=pack.index;

      otherboard.lines=0;
      pack=client.readpack();
      otherboard.lines=pack.index;

      pack=client.readpack();
      result=pack.data;

      for (i=0; i<10; i++) {
	for (j=0; j<20; j++) { 
	  try {
	    otherboard.grid[i][j]=result.charAt((i*20)+j);
	  } catch (StringIndexOutOfBoundsException evt) {
	    neterr("StringIndexOutOfBoundsException\n");
	    neterr("on istream.readLine()\n");
	    //return;
	  }
	  //((int)bytebuf)-48;
	}
      }
      pack=client.readpack();
      boards.setBoard(boardnum, otherboard);
    } // End of While
    netoff();
    return;
  }
  
  //THIS FUCTION SENDS A PIECE BACK TO THE TETRIS
  public synchronized int getPiece() {
    int count=0;

    if (piecenum==0) {
      if (client.server != null) {
	while (!ok2go) {                 //Wait for user
	  try {
	    Thread.sleep(100);
	  } catch (InterruptedException e) {
	    return -1;
	  }
	  while (ok2go && !enough()) {  //Wait for other users
	    try {
	      wait(10000);
	    } catch (InterruptedException e) {
	      return -1;
	    }
	  } // End while not enough
	}   // End while not ok2go
      } else {
	System.out.println("SCORE SERVER IS DOWN - CONTINUING STAND-ALONE");
	hide();
      }
    }
    piecenum++;
    return (int)(rndpiece.nextDouble()*7);
  }
  
  //THIS FUNCTION GETS THE LIST OF GAMES
  public void getgames(List games){
    //this function can't use neton() and netoff() because it happens
    //before the tetris is alive.
    packet pack=new packet();
    String gameline;
    gameline=new String();
    games.clear();
    pack.data=new String("");
    pack.type=5;
    pack.index=0;
    client.writepack(pack);

    pack=client.readpack();  //Priming read
    while (pack.type != -1) {
      gameline=pack.data;
      games.addItem(gameline);
      pack=client.readpack();
    } 
  }

  public boolean donework(int score, int lines) {
    packet pack=new packet();
    int authkey;
    String result;
    neton();

    //Tell server we're done
    pack.data=new String(score+","+lines);
    pack.length=pack.data.length();
    pack.type=2;
    pack.index=0;
    client.writepack(pack);       //Send game-end info

    pack=client.readpack();       //Get authorization key
    authkey=pack.index; 

    pack.data=new String();       //compose auth pack
    pack.length=0;
    pack.type=4;
    pack.index=SECRET.SECRET*authkey;
    client.writepack(pack);       //Send authorization key ASSuME

    netoff();
    return true;
  }

  public void done(int score, int lines, scoreboard s){
    int i, j;
    grid=new char[200];
    myscoreboard=s;
    for (i=0; i<10; i++) {
      for (j=0; j<20; j++) {
	try {
	  grid[ ((i*20)+j) ]=
	    new String().valueOf(myscoreboard.grid[i][j]).charAt(0);
	} catch (StringIndexOutOfBoundsException evt) {
	  neterr("StringIndexOutOfBoundsException\n");
	  neterr("in String().charAt(0)\n");
	  return;
	}
      }
    }
    updateboards();
    finallines=lines;
    finalscore=score;
  }

  public boolean enough() {
    packet askpack;
    packet pack;
    //Ask if we are alowed to start
    
    askpack=new packet();
    askpack.data=new String();
    askpack.length=0;
    askpack.type=6;
    
    neton();
    client.writepack(askpack);  //Ask if we can start?  Please?
    pack=client.readpack();     //Get it backa
    netoff();
    if (pack.type==7) {
      join.setLabel("ABORT WAITING for multi-player");
      join.id=10;
      message.appendText(pack.data);  //show the info
      return false;
    } else {
      if (!singleplay) {
	boards=new terminal(players);
	boards.setTitle("Updated every "+upevery+" seconds.");
      }
      hide();
      return true;
    }
  }

  public void startnew(int numPlayers) {
    packet pack=new packet();

    String result;
    result=new String();
    String temp;
    int seed;

    players=numPlayers;

    if (username.getText().length()<1) {
      message.setText("Please enter your name first.\n");
    }
    startsig=true;
    neton();
    if (players==1) {
      singleplay=true;
    } 
    pack.data = new String(players+","+username.getText());
    pack.length=pack.data.length();
    pack.type=0;
    pack.index=0;
    client.writepack(pack);      //Send request
    pack=client.readpack();    //Get back seed
    netoff();
    seed=pack.index;
    rndpiece.setSeed(seed);
    ok2go=true;
  }

  public void startselected() {
    packet pack=new packet();
    String result;
    result=new String();
    String temp;
    int seed;
    
    if (username.getText().length()==0) {
      message.setText("Please enter your name first.\n");
      return;
    }  

    temp=new String();
    if (games.getSelectedItem() != null) {
      temp=games.getSelectedItem();
    } else {
      message.setText("Please select a game to join first.\n");
      return;
    }

    startsig=true;
    try {
      temp=temp.substring(0, temp.indexOf(" "));  //Get game number
    } catch (StringIndexOutOfBoundsException evt) {
      neterr("StringIndexOutOfBoundsException\n");
      neterr("While parsing ListBox selection.\n");
    }
    pack.type=1;
    pack.data=new String(username.getText());
    pack.length=pack.data.length();
    pack.index=(new Integer(temp.trim())).intValue();
    client.writepack(pack);
    
    pack=client.readpack();
    players=pack.index;                      //Get number of players or -1

    if (players != -1) {
      pack=client.readpack();
      netoff();
      seed=pack.index;                       //get random seed
      rndpiece.setSeed(seed);
      ok2go=true;
    } else {
      message.appendText("Cannot Add game.\n");
      message.appendText(pack.data);         //Print server error
    }
  }  // END OF STARTSELECTED()
  
  public void neton() {
    grandparent.netled(1);
  }

  public void netoff() {
    grandparent.netled(0);
  }

  public void neterr(String s) {
    System.out.print("Net Error:");
    System.out.print(s);
    message.appendText("Network Error.  Socket server may be down, or\n");
    message.appendText("URL may be wrong (security exception).\n");
    message.appendText("email julian@cobite.com for help.\n");
    message.appendText(s);
    grandparent.netled(2);
  }

  public void abort() {      //Reset connection with server
    client.close();
    client=new tcp_conn(server, ourport);
    startsig=false;
    ok2go=false;
    join.setLabel("Join multi-player");
    join.id=9;

  }

}//  ************************  END OF CLASS NETWORK
  
class mybutton extends Button { //mybutton calls functions in the parent.
  int id;           

  mybutton(String name, int myid) {
    super(name);
    id=myid;
  }

  public boolean handleEvent(Event e) {
    switch (id) {
    case 1:     // 1-8 are start new buttons
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
      if ( ((network)getParent()).startsig==false) {
	((network)getParent()).startnew(id);
      }
      break;
    case 9:
      ((network)(getParent())).startselected();
      break;
    case 10:
      ((network)getParent()).abort();
      break;
    }
    return false;
  }
}
//  ************************  END OF CLASS MYBUTTON







