/* 
 java.net example (1.2): 
    Client-server connection-oriented communication.
    Multithreaded server implementation.
 
   (written by Petr Grygarek, petr.grygarek@vsb.cz)
   (modified by Michal Krumnikl, michal.krumnikl@vsb.cz)
   (modified again by Pavel Moravec, pavel.moravec@vsb.cz)
*/
 
import java.net.*;
import java.io.*;
import java.util.Date;
import java.util.Vector;
 
public class AcceptCovidEdition implements Runnable {
 
  static ServerSocket s;
  static DatagramSocket rs;
  static int clients=0;
  static Vector<InetAddress> addresses = new Vector<>();
 
  protected Socket cs;
  int client;
 
  AcceptCovidEdition(Socket cs) { 
    this.cs=cs; 
    this.client = ++clients;
    (new Thread(this)).start();  
  }
 
  public static void main(String[] args) {
 
    try {
    s=new ServerSocket(8000);
    System.out.println("Server listening at " + s.getLocalSocketAddress());
    rs = new DatagramSocket();
    Socket cso;
 
      do {
        cso=s.accept();
        addresses.add(cso.getInetAddress());
        AcceptCovidEdition ms=new AcceptCovidEdition(cso);
      }  while (true);
    } catch (IOException e) {
     if (e instanceof SocketException) System.out.println("Shutting down after last connection terminates!");// "==Conections won't be accepted anymore=="); 
      else System.out.println(e); 
    }
    //rs.close();
  }
 
  public void run() {
    String msg;
    byte [] data = new byte[512];
    InetAddress adr = cs.getInetAddress();
    try {
     BufferedReader is = new BufferedReader(new InputStreamReader(cs.getInputStream()));
     System.out.println("New connection #" +  client + " from " + adr.getHostAddress() + " : " + cs.getPort());
     do {
      msg = is.readLine();
      System.out.println("#" + client + ": " + msg + " (" + msg.length() + ')');
      sendReply(msg, cs.getInetAddress());
     }  while (!msg.equalsIgnoreCase("exit") && !msg.equalsIgnoreCase("server_down"));     
     if (addresses.contains(adr)) {
        addresses.remove(adr);
     }
     cs.close();
     System.out.println("Closing connection #" + client + " from "+ cs.getInetAddress()+" : "+cs.getPort());
     if (msg.equalsIgnoreCase("server_down")) {
        s.close();
     }
   } catch (Exception e) { 
     if (e instanceof NullPointerException) System.out.println("Client #" + client  
             + " with address " + adr.getHostAddress() + " died prematurely!"); 
     if (addresses.contains(adr)) {
        addresses.remove(adr);
     }
     else System.out.println(e); 
   }
  }
 
  protected void sendReply(String msg, InetAddress ia) throws IOException
  {
     ByteArrayOutputStream ba = new ByteArrayOutputStream();
     DataOutputStream data = new DataOutputStream(ba);
     if (msg.length() >= 255)
     {
        msg = msg.substring(0,254);
     }
     data.write(ia.getAddress(),0,4);
     data.writeInt((int)((new Date()).getTime() / 1000));
     data.writeByte((char)msg.length());
     data.writeBytes(msg);
     data.flush();
     byte [] pdata = ba.toByteArray(); 
     
     DatagramPacket p=new DatagramPacket(pdata, pdata.length, InetAddress.getByName("10.0.0.255"),8010);

     for (int i=0; i < addresses.size(); i++) {
         p.setAddress(addresses.get(i));
         //System.err.println("Sending back to " + p.getAddress().getHostAddress());
         rs.send(p);         
     }
  }
}
