/*
 * DNDLogic.java
 *
 * Created on March 28, 2001, 10:07 AM
 */

/**
 *
 * @author  Al Williams
 * @version 
 */
import java.net.*;
import java.util.*;
import java.io.IOException;

public class DNDLogic extends Observable implements Runnable {

    final private static int PACKSIZ=64;
    final private String encoding="ISO8859_1";
    private int port = 1069;
    private MulticastSocket sock;
    private DatagramPacket pack;
    private byte[] data = new byte[PACKSIZ];
    private InetAddress ia;
    private boolean shutdown=false;
    private byte state=0;
    final public static byte AVAILABLE=1;
    final public static byte DND=2;
    private Hashtable ulist;
    private String myname;
    /** Creates new DNDLogic */
    public DNDLogic() {
        pack=new DatagramPacket(data,PACKSIZ);
        ulist=new Hashtable(64);
    }

    public void run() {  // listener thread
        try {
            sock.setSoTimeout(5000);
        }
        catch (SocketException e) { }
        do {
            try {
                pack.setLength(PACKSIZ);
                sock.receive(pack);
            }
            catch (IOException e) { 
              continue; 
            }
            String incoming=new String(pack.getData(),0,pack.getLength());
            String name="",status;
            status=incoming.substring(0,1);
            if (pack.getLength()>1)
                name=incoming.substring(1,pack.getLength());
            if (name.equals(myname)) continue; // ignore myself
            switch (status.charAt(0)) {
                case '+':
                case '-':
                    add(name,status);
                    break;
                case '@':
                    remove(name);
                    break;
                case '/':
                    sendStatus();
                default:   // fall through (no need to update / command)
                    continue;
            }
         // if +,-,@ then update observers
            notifyObservers();
        } while (!shutdown);
    }

// announce a login
    public boolean login(String name,String ip,int portn, byte status) 
                         throws SocketException {
        state=status;
        port=portn;
        try {
           sock = new MulticastSocket(port);
          // before JDK1.2, use setTTL
           sock.setTimeToLive(16);
        }
        catch (IOException e) {
            return false; 
        }
        try {
            ia=InetAddress.getByName(ip);
        }
        catch (UnknownHostException e) {
            sock.close();
            return false;
        }
        myname=name;
        try {
            sock.joinGroup(ia);  // listen for broadcasts
        }
        catch (IOException e) {
            sock.close();
            return false;
        }
        new Thread(this).start();  // start server thread
        sendStatus();  // make announcement
        query();        // ask everyone to announce
        return true;
    }
    
// send an announcement
    private void sendStatus() {
        String datastr;
        switch (state) {
            case AVAILABLE: 
                datastr="+";
                break;
            case DND:
                datastr="-";
                break;
            default:
                datastr="?";
        }
        datastr+=myname;
        try {
            byte[] dbuf;
            dbuf=datastr.getBytes(encoding);
            DatagramPacket pack1=new DatagramPacket
                           (dbuf,dbuf.length,ia,port);
            sock.send(pack1);  // announce
        }  
        catch (Exception e) { } 
    }

// clear list of users (initialize list to contain me)
    public void clear() {
          ulist.clear();
        add(myname,"*");  // asterisk means its me!
        setChanged();
          notifyObservers();
    }

// ask everyone on the network to announce themselves
    public void query()  {
        try {
          String qstring="/" + myname;
          byte[] dbuf=qstring.getBytes(encoding);
          DatagramPacket pack1=new DatagramPacket
                         (dbuf,dbuf.length,ia,port);
          sock.send(pack1);        
        }
        catch (Exception e) { }
    }
    
// Shutdown server thread and announce leaving
    public void logout() {
        try {
            String datastr = "@" + myname;
            byte [] dbuf=datastr.getBytes(encoding);
            DatagramPacket pack1=new DatagramPacket
                           (dbuf,dbuf.length,ia,port);
            sock.send(pack1);
            shutdown=true;
            sock.close();
        } 
        catch (Exception e) { }
    }

    
// Get a copy of the current user list
    public synchronized Hashtable getList() {
        return (Hashtable) ulist.clone();
    }
    
    
// Set my current state and make announcement
    public void setStatus(byte status) {
        state=status;
        sendStatus();
    }
    
// Add a user to the list
    private synchronized void add(String name,String status) {
        if (ulist.containsKey(name)) {
              if (status.equals((String)ulist.get(name))) return; 
                                      // ignore dup
            ulist.remove(name);
        }
        ulist.put(name,status);
        setChanged();
    }
    
// Remove user from list
    private synchronized void remove(String name) {
        ulist.remove(name);
        setChanged();
    }
    
}