/******************************************************************
 *
 *	CyberUPnP for Java
 *
 *	Copyright (C) Satoshi Konno 2002
 *
 *	File : ClockDevice.java
 *
 ******************************************************************/

package device;

import service.*;
import xmltypes.*;

import java.io.*;

import org.cybergarage.upnp.*;
import org.cybergarage.upnp.device.*;
import org.cybergarage.upnp.control.*;
import org.cybergarage.http.*;
import org.cybergarage.upnp.ssdp.*;

public class ClockDevice extends Device implements ActionListener, QueryListener, NotifyListener 
{
    private final static String DESCRIPTION_FILE_NAME = "description/description.xml";
    // Change start
    private final static String PRESENTATION_URI = "/presentation";
    private final static String REFRESH_UI = "/refresh";
    // Change end

    private ControlPoint ctrlPoint = new ControlPoint();
    
    private Timer timer;
    private Service timerService;

    private Device lastDeviceSeen;

    public ClockDevice() throws InvalidDescriptionException
    {
	super(new File(DESCRIPTION_FILE_NAME));

	UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);

	org.cybergarage.util.Debug.on();

	ctrlPoint.addNotifyListener(this);
	ctrlPoint.start();
	
	Action getTimeAction = getAction("GetTime");
	getTimeAction.setActionListener(this);
	
	Action setTimeAction = getAction("SetTime");
	setTimeAction.setActionListener(this);
	
	Action timeValidAction = getAction("TimeValid");
	timeValidAction.setActionListener(this);
	
	ServiceList serviceList = getServiceList();
	// there should only be a timer service
	timerService = serviceList.getService(0);
	timerService.setQueryListener(this);
	
	setLeaseTime(60);
    }

    public void setTimer(Timer t) {
	timer = t;
	System.out.println("Our timer service is " + t);
    }
    
    ////////////////////////////////////////////////
    // ActionListener
    ////////////////////////////////////////////////
    
    public boolean actionControlReceived(Action action)
    {
	String actionName = action.getName();
	if (actionName.equals("GetTime") == true) {
	    System.out.println("GetTime action control");

	    Time now = timer.getTime();
	    Argument timeArg = action.getArgument("CurrentTime");
	    timeArg.setValue(now.toString());
	    return true;
	}
	if (actionName.equals("SetTime") == true) {
	    System.out.println("SetTime action control");
	    Argument timeArg = action.getArgument("NewTime");
	    String newTime = timeArg.getValue();
	    timer.setTime(new Time(newTime));
	    System.out.println("SetTime action to " + newTime);
	    return true;
	}
	if (actionName.equals("TimeValid") == true) {
	    System.out.println("TimeValid action control");
	    boolean timeValid = timer.isValidTime();

	    Argument timeArg = action.getArgument("Valid");
	    timeArg.setValue("" + timeValid);
	    return true;
	}
	return false;
    }
    
    ////////////////////////////////////////////////
    // QueryListener
    ////////////////////////////////////////////////
    
    public boolean queryControlReceived(StateVariable stateVar)
    {
	System.out.println("query control");
	if (stateVar.getName().equals("Time")) {
	    stateVar.setValue(getTime().toString());
	    return true;
	} else if (stateVar.getName().equals("TimeValid")) {
	    stateVar.setValue("" + isValidTime());
	    return true;
	}
	return false;
    }
    
    //
    // HttpRequestListner
    //
    // This is responsible for generating the HTML UI
    //

    public void httpRequestRecieved(HTTPRequest httpReq)
    {
	System.out.println("HTTP request: " + httpReq);
	
	String uri = httpReq.getURI();

	// Change start
	if (uri.startsWith(PRESENTATION_URI)) {
	    sendPage(httpReq);
	} else if (uri.startsWith(REFRESH_UI)) {
	    sendPage(httpReq);
	} else {
	    super.httpRequestRecieved(httpReq);
	    return;
	}
	// Change end
    }

    // Change start
    private void sendPage(HTTPRequest httpReq) {
	// deliver an HTML page
	String time = getTime().toString();
	String refreshUrl = "http://" +
	    httpReq.getLocalAddress() + 
	    ":" +
	    httpReq.getLocalPort() +
	    REFRESH_UI;
	System.out.println("referesh url " + refreshUrl);

	// the page attempts to turn caching off at the client side
	// but a bug in JEditorPane ignores the Cache-Control value
	String page = "<html>\n" + 
	    "<head>\n" +
	    "</head>\n" +
	    "<body>\n" +
	    "<form method=\"post\" action=\"" + 
	    refreshUrl + "\">\n" +
	    "New time" +
	    "<input type=\"text\" name=\"time\">\n" +
	    "<input type=\"submit\">\n" +
	    "<p>\n" +  
	    time + 
	    "\n<br>\n" +
	    "<a href=\"" +
	    refreshUrl +
	    "\"> Refresh </a>\n" +
	    "</p>\n" +
	    "</body>\n" +
	    "</html>\n";
	
        System.out.println("Sending page " + page);
	HTTPResponse response = new HTTPResponse();
	response.setStatusCode(HTTPStatus.OK);
	response.setContent(page);
	response.setCacheControl(0); // no caching - ignored by JEditorPane
	httpReq.post(response);
    }
    // Change end

    public void setTime(Time time) {
	System.out.println("setTime in clock device to " + time);
	timer.setTime(time);
    }

    public Time getTime() {
	return timer.getTime();
    }

    public boolean isValidTime() {
	return timer.isValidTime();
    }

    public void deviceNotifyReceived(SSDPPacket ssdpPacket) { 

	System.out.println("New SSDPPacket, all " + ssdpPacket);
	/* There doesn;t seem to be a simple way of getting the new
	 * device from the packet, even though CyberLink creates a
	 * new device from it - and adds it to a list. Looks like we
	 * have to get the whole list and search through it for the
	 * device created by this packet :-(
	 */
	DeviceList devList = ctrlPoint.getDeviceList();
	Device dev = null;
	System.out.println("Devices: " + devList.size());
	for (int n = 0; n < devList.size(); n++) {
	    if (devList.getDevice(n).getSSDPPacket() == ssdpPacket) {
		dev = devList.getDevice(n);
		break;
	    }
	}
	if (dev == null) {
	    // couldn't find it? That shouldn't happen...
	    return;
	}

	/* this doesn't work :-( device changes each time
	
	if (dev == lastDeviceSeen) {
	    // UPnP sends repeat messages to try to make sure things
	    // get through. But we don't want to repeat work
	    System.out.println("Seen this device before");
	    lastDeviceSeen = dev;
	    return;
	}
	*/

	ServiceList timerServices = new ServiceList();

	if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:clock:1")) {
	    ServiceList services = dev.getServiceList();
	    for (int m = 0; m < services.size(); m++) {
		Service svc = services.getService(m);
		if (svc.getServiceType().equals("urn:schemas-upnp-org:service:timer:1")) {
		    timerServices.add(svc);
		}
	    }
	}

	tryClockValidation(timerServices);
    } 

    private ServiceList getTimerServices() {
	ServiceList timerServices = new ServiceList();

	DeviceList devList = ctrlPoint.getDeviceList();
	System.out.println("Devices: " + devList.size());
	for (int n = 0; n < devList.size(); n++) {
	    Device dev = devList.getDevice(n);
	    if (dev.getDeviceType().equals("urn:schemas-upnp-org:device:clock:1")) {
		ServiceList services = dev.getServiceList();
		for (int m = 0; m < services.size(); m++) {
		    Service svc = services.getService(m);
		    if (svc.getServiceType().equals("urn:schemas-upnp-org:service:timer:1")) {
			timerServices.add(svc);
		    }
		}
	    }
	}
	return timerServices;
    }

    private void tryClockValidation(ServiceList services) {
	org.cybergarage.util.Debug.on();
	for (int n = 0; n < services.size(); n++) {
	    Service svc = (Service) services.elementAt(n);

	    System.out.println("Checking service " + svc);
	    Action validAction = svc.getAction("TimeValid");
	    if (validAction.postControlAction()) {
		System.out.println("Checking TimeValid succeeded");
		Argument arg = validAction.getArgument("Valid");
		String value = arg.getValue();
		System.out.println("valid time? " + value);
		if (isValidTime() && value.equals("false")) {
		    // other clock needs to be set
		    Action setTimeAction = svc.getAction("SetTime");
		    setTimeAction.setArgumentValue("NewTime",
						    getTime().toString());
		    setTimeAction.postControlAction();
		} else if (! isValidTime() && value.equals("true")) {

		    Action getTimeAction = svc.getAction("GetTime");
		    if ( ! getTimeAction.postControlAction()) {
			System.out.println("GetTime post action failed");
		    }
		
		    Argument arg2 = getTimeAction.getArgument("CurrentTime");
		    Object val = arg2.getValue();
		    System.out.println("Arg type " +  val.getClass());
		    System.out.println("setTime in clock validation to " + arg2.getValue());
		    setTime(new Time(arg2.getValue()));
		}
	    }
	}
    }
}

