import java.io.*;
import java.net.*;

public class FlipServer{

  static final int size = 6; // how many keys wide and high
  static final int port = 8199; // agreed upon w/ NetInterface, unassigned
  private MulticastOutputStream out; // communicates to the clients
  private boolean pushedOddTimes[][]; // keeps track of history

  /** The addOut method is used to add another output stream that should
   * receive the pushes (row and column numbers); this is used when
   * a new client connects. Buttons that were pushed earlier are
   * also sent to the new stream, provided they were pushed an odd number
   * of times.  */ 

  public void addOut(OutputStream newOut){
    try{
      for(int row = 0; row < size; row++){
        for(int col = 0; col < size; col++){
          if(pushedOddTimes[row][col]){
            newOut.write(row);
            newOut.write(col);
          }
        }
      }
    } catch(Exception e){
      System.err.println(e);
    }
    out.add(newOut);
  }

  /** The removeOut method is used to remove one of the output streams
   * to which pushes (row and column numbers) are being sent; this is
   * used when the thread tending a particular client connection notices
   * that the client has closed the connection */

  public void removeOut(OutputStream oldOut){
    out.remove(oldOut);
  }

  /** The push method is used to record and transmit a push; it is used
   * by a client connection thread that has received a row and column */

  public void push(int row, int col){
    pushedOddTimes[row][col] = !pushedOddTimes[row][col];
    try{
      out.write(row);
      out.write(col);
    } catch(Exception e){
      System.err.println(e);
    }
  }

  public static void main(String[] args){
    FlipServer server = new FlipServer();
    try{
      ServerSocket ss = new ServerSocket(port);
      while(true){
        Socket s = ss.accept();
        OutputStream newOut = s.getOutputStream();
        server.addOut(newOut);
        new ClientConnectionThread(server, s, newOut);
      }
    } catch(Exception e){
      System.err.println(e);
      System.exit(1);
    }
  }

  private FlipServer(){
    pushedOddTimes = new boolean[size][size];
    out = new MulticastOutputStream();
  }
}

class ClientConnectionThread extends Thread{

  private FlipServer server;
  private Socket sock;
  private OutputStream out;
  private InputStream in;

  ClientConnectionThread(FlipServer srvr, Socket s, OutputStream o)
       throws IOException
  {
    server = srvr;
    sock = s;
    out = o;
    in = s.getInputStream();
    start(); // start thread listening for incoming pushes
  }

  public void run(){
    try{
      while(true){
        int row = in.read();
        int col = in.read();
        if(col == -1){ // end of data indication
          break;       // so break out of loop (to the removeOut)
        }
        server.push(row, col);
      }
      server.removeOut(out); // the client died, so don't send it any more
    } catch(Exception e){
      System.err.println(e);
    } finally{
      try{
        sock.close();
      } catch(Exception e){
        System.err.println(e);
      }
    }
  }
}