
/* Josh Holtrop
 * 2008-12-04
 * CS656
 * Lab 4
 */

import java.io.IOException;
import java.util.*;

import org.restlet.Client;
import org.restlet.data.Form;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.*;
import org.w3c.dom.*;

import edu.gvsu.cis.cs656.lab4.server.PresenceService;
import edu.gvsu.cis.cs656.lab4.server.RegistrationInfo;

public class PresenceServiceImpl implements PresenceService
{
    private int myPort;
    private String myHost;
    private String myAppURI;

    public PresenceServiceImpl(String host, int port)
    {
        myHost = host;
        myPort = port;
        myAppURI = "http://" + host + ":" + port + "/v1";
    }

    /**
     * Register a client with the presence service.
     * @param reg The information that is to be registered about a client.
     */
    public void register(RegistrationInfo reg) throws Exception
    {
        Form form = new Form();
        form.add("user[name]", reg.getUserName());
        form.add("user[host]", reg.getHost());
        form.add("user[port]", (new Integer(reg.getPort())).toString());
        form.add("user[status]", reg.getStatus() ? "available" : "away");

        // construct request to create a new user resource
        String usersResouceUri = myAppURI + "/users";
        Request request = new Request(Method.POST, usersResouceUri);

        request.setEntity(form.getWebRepresentation());

        // Invoke the client HTTP connector
        Response resp = new Client(Protocol.HTTP).handle(request);
        if ( ! resp.getStatus().isSuccess() )
            System.out.println(resp.getStatus());
    }

    /**
     * Unregister a client from the presence service.  Client must call this
     * method when it terminates execution.
     * @param userName The name of the user to be unregistered.
     */
    public void unregister(String userName) throws Exception
    {
    	String userResourceUri = myAppURI + "/users/" + userName;
    	Request request = new Request(Method.DELETE, userResourceUri);

        // Invoke the client HTTP connector
        Response resp = new Client(Protocol.HTTP).handle(request);
        if ( ! resp.getStatus().isSuccess() )
            System.err.println(resp.getStatus());
    }

    /**
     * Lookup the registration information of another client.
     * @param name The name of the client that is to be located.
     * @return The RegistrationInfo info for the client, or null if
     * no such client was found.
     */
    public RegistrationInfo lookup(String name) throws Exception
    {
    	try
    	{
	        String userResourceUri = myAppURI + "/users/" + name;
	    	Request request = new Request(Method.GET, userResourceUri);
	    	Response resp = new Client(Protocol.HTTP).handle(request);
	    	if ( ! resp.getStatus().isSuccess() )
                System.out.println(resp.getStatus());
            DomRepresentation rep = resp.getEntityAsDom();
            Document d = rep.getDocument();
            Node n = d.getFirstChild();
            if (n == null)
                return null;
            return nodeToRegInfo(n);
    	}
    	catch (Exception ioex)
    	{
    		ioex.printStackTrace();
    	}
        return null;
    }

    /**
     * Sets the user's presence status.
     * @param name The name of the user whose status is to be set.
     * @param status true if user is available, false otherwise.
     */
    public void setStatus(String userName, boolean status) throws Exception
    {
        Form form = new Form();
        form.add("user[status]", status ? "available" : "away");

        String userResourceUri = myAppURI + "/users/" + userName;
        Request request = new Request(Method.PUT, userResourceUri);
        request.setEntity(form.getWebRepresentation());

        try
        {
            // Invoke the client HTTP connector
            Response resp = new Client(Protocol.HTTP).handle(request);
            if ( ! resp.getStatus().isSuccess() )
                System.err.println(resp.getStatus());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * Determine all users who are currently registered in the system.
     * @return An array of RegistrationInfo objects - one for each client
     * present in the system.
     */
    public RegistrationInfo[] listRegisteredUsers() throws Exception
    {
        Vector<RegistrationInfo> users = new Vector<RegistrationInfo>();

        try
        {
            String usersResourceUri = myAppURI + "/users";
            Request request = new Request(Method.GET, usersResourceUri);

            // Invoke the client HTTP connector
            Response resp = new Client(Protocol.HTTP).handle(request);
            if ( ! resp.getStatus().isSuccess() )
                System.err.println(resp.getStatus());
            DomRepresentation dom = resp.getEntityAsDom();
            Document d = dom.getDocument();
            Node root = d.getFirstChild();
            if (root == null)
                System.err.println("listRegisteredUsers(): Error: root node not found");
            NodeList nl = root.getChildNodes();
            if (nl == null)
                System.err.println("listRegisteredUsers(): Error: nl is null");
            for (int i = 0; i < nl.getLength(); i++)
            {
                Node n = nl.item(i);
                if (n.getNodeName().equals("user"))
                {
                    RegistrationInfo ri = nodeToRegInfo(n);
                    users.add(ri);
                }
            }
        }
        catch (Exception ioex)
        {
            ioex.printStackTrace();
        }

        RegistrationInfo[] dum = new RegistrationInfo[1];
        return users.toArray(dum);
    }

    private RegistrationInfo nodeToRegInfo(Node n)
    {
        RegistrationInfo ri = new RegistrationInfo();
        NodeList nl = n.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++)
        {
            Node child = nl.item(i);
            String name = child.getNodeName();
            if (name.equals("name"))
                ri.setUserName(child.getTextContent());
            else if (name.equals("host"))
                ri.setHost(child.getTextContent());
            else if (name.equals("port"))
                ri.setPort(Integer.parseInt(child.getTextContent()));
            else if (name.equals("status"))
                ri.setStatus(child.getTextContent().equals("available"));
        }
        return ri;
    }
}
