package jmeetup;

import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;

import gui.*;
import internet.*;


/**
 * The JMeetUp server
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public class Server implements Runnable
{
    private volatile boolean              keepRunning;
    private Hashtable<ConnectionHandler,
                      ConnectionHandler>  tcpHandlers; // Thread safe
    private Hashtable<ConnectionHandler,
                      UDPMessageSender>   udpHandlers; // Thread safe
    private ServerSocket                  ss;
    private Thread                        controlThread;
    private Vector<Proposal>              proposals;   // Thread safe

    /**
     * Default Constructor
     */
    public Server()
    {
       this(9000);
    }


    /**
     * Explicit Value Constructor
     *
     * @param port  The port the server should listen to
     */
    public Server(int port)
    {
       try 
       {
          ss = new ServerSocket(port);

          // Make sure accept() will timeout so that we can stop
          ss.setSoTimeout(5000);

          tcpHandlers = new Hashtable<ConnectionHandler,ConnectionHandler>();
          udpHandlers = new Hashtable<ConnectionHandler,UDPMessageSender>();

          proposals = new Vector<Proposal>();
       }
       catch (Exception e) 
       {
          System.err.println(e);
          System.exit(1);
       }
    }


    /**
     * Add a proponent to a Proposal
     *
     * @param  id           The ID of the proposal
     * @param  username     The proponent
     */
    public synchronized void addProponent(int id,
					  String username)
					 
    {
       Enumeration<Proposal>    e;
       Proposal                 p;


       e = proposals.elements();
       while (e.hasMoreElements()) 
       {
          p = e.nextElement();
          if (p.getID() != id) p.removeProponent(username);
          else                 p.addProponent(username);
       }
    }


    /**
     * Add a Proposal
     *
     * @param  username     The user making the proposal
     * @param  description  Decscription of the proposal
     */
    public synchronized void addProposal(String username, 
					 String description)
    {
       int        id;
       Proposal   p;


       id = proposals.size() + 1;
       p = new Proposal(id, description);
       proposals.add(p);
       addProponent(id, username);
    }


    /**
     * Broadcast a message
     *
     * @param message   The message
     */
    public synchronized void broadcast(String message)
    {
       Enumeration<ConnectionHandler>   e;
       ConnectionHandler                tcph;
       UDPMessageSender                 udph;

       e = tcpHandlers.elements();
       while (e.hasMoreElements())
       {
          tcph = e.nextElement();
          udph = udpHandlers.get(tcph);
          
          if (udph != null) udph.send(message);
       }
    }


    /**
     * Get all of the proposals
     *
     * @return  The proposals
     */
    public synchronized Enumeration<Proposal> getProposals()
    {
       return proposals.elements();
    }


    /**
     * Remove a connection handler
     *
     * @param name    The name
     */
    public void remove(ConnectionHandler handler)
    {
       ConnectionHandler   tcph;
       UDPMessageSender    udph;


       tcph = tcpHandlers.remove(handler);
       udph = udpHandlers.remove(handler);

       if (udph != null) udph.stop();
       if (tcph != null) tcph.stop();
    }


    /**
     * Remove all connection handlers
     *
     */
    public void removeAll()
    {
       Enumeration<ConnectionHandler>   e;
       ConnectionHandler                handler;

       e = tcpHandlers.elements();
       while (e.hasMoreElements())
       {
          handler = e.nextElement();
          remove(handler);
       }
    }


    /**
     * The "entry point" for the new thread of execution
     */
    public void run()
    {
       ConnectionHandler   tcph;
       UDPMessageSender    udph;
       Socket              s;

       while (keepRunning) 
       {
          try 
          {
             s   = ss.accept();
             udph = new UDPMessageSender(s.getInetAddress());
             udph.start();

             try 
             {
                tcph =new ConnectionHandler(s,this);
		    
                // Use the ConnectionHandler 
                // as the key in both cases since 
                // it will initiate the remove later
                tcpHandlers.put(tcph, tcph);
                udpHandlers.put(tcph, udph);

                tcph.start();
		    
             }
             catch (IOException ioet) 
             {
                ioet.printStackTrace();
             }
          }
          catch (SocketException se) 
          {
             // Unable to create UDP socket or accept() timed-out
          }
          catch (IOException ioes)
          {
             // Unable to accept
          }
       }
       
       // The thread is about to terminate so remove all of the
       // handlers
       removeAll();

       controlThread = null;
    }


    /**
     * Set the port for the UDPMessageSender
     *
     * @param handler The ConnectionHandler
     * @param port    The port to use
     */
    public void setSenderPort(ConnectionHandler handler,
		              int port)
    {
       ConnectionHandler      tcph;
       UDPMessageSender       udph;


       tcph = tcpHandlers.get(handler);
       udph = udpHandlers.get(handler);

       if (udph != null) udph.setPort(port);
    }



    /**
     * Start the new thread of execution
     */
    public void start()
    {
       if (controlThread == null)
       {
          keepRunning = true;
          controlThread = new Thread(this);
          controlThread.start();
       }
    }



    /**
     * Stop the thread of execution
     */
    public void stop()
    {
       keepRunning = false;
    }

}
