import java.net.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;

public class ajamServer {
  static protected Set activeClients = new HashSet();

  public static void main(String[] args){

   int i=0;
   ServerSocket server_socket;

    try {

        server_socket = new ServerSocket(8888);
        System.out.println("Server waiting for client on port " + server_socket.getLocalPort());

        while(true) {
                Socket s = server_socket.accept();
                System.out.println("New connection accepted " + s.getInetAddress() + ":" + s.getPort());
                clientHandler newClient = new clientHandler(s,i++);
                activeClients.add(newClient);
                newClient.start();
        }
      } catch (IOException e) {
		  e.printStackTrace();
      }

  }
}

class clientHandler extends Thread{
 BufferedOutputStream output;
 DataInputStream input;
 boolean good = true;
 int id;
 Socket sock;
   public clientHandler(Socket sock,int id){
	  this.id = id;
      this.sock = sock;
   }
  
   public synchronized void sendMessage(Object m){
     if(output != null) {
      try{
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
		((D)m).writeExternal(out);
        //out.writeObject(m);
        out.close();
        output.write(bos.toByteArray());
        //output.write(0);
        output.flush();
	    //System.out.println("sent m");
       //  out.print(msg);
       //  out.write(0);
       //  out.flush();
      }catch(Exception e){
			System.out.println("closing");
			e.printStackTrace(); 
			good = false;
	 }
     }else{
			good = false;
		 System.out.println("out is null");
	 }
   }

   public void broadcast(Object m, boolean self){
	  synchronized(ajamServer.activeClients){
       Iterator iter = ajamServer.activeClients.iterator();
       while(iter.hasNext()){
         clientHandler t = (clientHandler)iter.next();
         if(t!=this||self){
        //if(!ht.containsKey(String.valueOf(t.id)))
              t.sendMessage(m);
        //else System.out.println("blocking: "+t.id);
         }
       }
      }
   }

   public void addNotify(boolean meToo){
	  synchronized(ajamServer.activeClients){
       int users = ajamServer.activeClients.size();
	   //byte msg[] = { 'A','r','r','i','v','e' };
	   String msg = "Arrive";
	   //ajData m = new ajData(env.ARRIVAL,id,"Arrive",users);
	   D m = new D(env.ARRIVAL,id,msg,users);
       broadcast(m,meToo);
      }
   }

   public void dropNotify(boolean meToo){
	  synchronized(ajamServer.activeClients){
       int users = ajamServer.activeClients.size();
	   //ajData m = new ajData(env.DEPARTURE,id,"Depart",users);
	   String msg = "Depart";
	   //byte msg[] = { 'D','e','p','a','r','t' };
	   D m = new D(env.DEPARTURE,id,msg,users);
       //System.out.println("dropping ");
       broadcast(m,meToo);
      }
   }

   public void run(){
     try{
	    byte[] b = {(byte)' '};
        D m = new D(env.SETCHAR,0," ",0);

		sock.setSoTimeout(3000000);
		BufferedInputStream gis = new BufferedInputStream(sock.getInputStream());
        output = new BufferedOutputStream(sock.getOutputStream());
		
        /* This section reads the name of the most recently saved drawing */
        File file = new File("/web/act/01/asciiJam/docs/lastdrawing.txt");
		DataInputStream dis = new DataInputStream(new FileInputStream(file));
		String name = dis.readLine();
        /* This section reads most recently saved drawing */
        File file2 = new File("/web/act/01/asciiJam/logs/"+name);
		dis = new DataInputStream(new GZIPInputStream(new FileInputStream(file2)));
		String line;
		int y = 0;
		/* The first half of the file contains the letters */
		char[][] letters = new char[64][128];
		while((line = dis.readLine())!=null){
		   if(y<64){
			   for(int x = 0; x<128;x++) letters[y][x] = line.charAt(x);
		   }else break;
		   y++;
		}
		y = 0;
		/* The second half of the file contains the colors */
		int[][] colors = new int[64][128];
		do{
		   if(y<64){
			   for(int x = 0; x<255;x+=2){
				 char c = line.charAt(x);
				 char c2 = line.charAt(x+1);
				 if(c>='0' && c<='9'){ 
				    if(c2>='0' && c2<='9'){ 
				          colors[y][x>>1] = (c-'0')*16+(c2-'0');
					}else colors[y][x>>1] = (c-'0')*16+(c2-'A'+10);

			     }else{
				    if(c2>='0' && c2<='9'){ 
				          colors[y][x>>1] = (c-'A')*16+(c2-'0');
					}else colors[y][x>>1] = (c-'A')*16+(c2-'A'+10);
				 }
			   }
		   }
		   y++;
		}while((line = dis.readLine())!=null);
        /* This section sends the most recently saved drawing */
		for(int x=0;x<128;x++){
		   for(y=0;y<64;y++){
			   b[0] = (byte)letters[y][x];
		       if(b[0]!=32){
			      m.col = (short)colors[y][x];
			      m.a = String.valueOf(letters[y][x]);
			      m.idx = (short)  (   ( (64-y) *128 )+x);
			      m.typ = env.SETCHAR;
			      sendMessage(m);
			   }
		   }
		}
 		/* Enter the listening loop */
		m = new D(0,0,"",0);
        while(good){
            ObjectInputStream in= new ObjectInputStream(gis);
			((D)m).readExternal(in);
			//m=(D)in.readObject();
			if(m==null){
				good = false;
				 continue;
            }
            else
            {
				if(m.typ==env.ARRIVAL){
					m.idx = (short)ajamServer.activeClients.size();
					 broadcast(m,true);
				}
				else if(m.typ==env.DEPARTURE){
					 good = false;
                }
			    else broadcast(m,false);
            }
       }
       gis.close();
    } catch (EOFException eof) {
                eof.printStackTrace();
    } catch (IOException e) {
                e.printStackTrace();
    }
    try{
	   synchronized(ajamServer.activeClients){
         ajamServer.activeClients.remove(this);
	   }
       dropNotify(false);
       if(input!=null)  input.close();
       if(output!=null) output.close();
    } catch(IOException e){
       System.out.println(e);
    }

   }
}

