package org.aktin.broker;

import java.sql.SQLException;
import java.util.Base64;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.aktin.broker.auth.AuthCache;
import org.aktin.broker.db.BrokerBackend;
import org.aktin.broker.server.DateDataSource;
import org.aktin.broker.xml.Node;
import org.aktin.broker.xml.NodeList;

@Path("/broker/node/")
public class NodeInfoEndpoint {
	private static final Logger log = Logger.getLogger(NodeInfoEndpoint.class.getName());
	@Inject
	private BrokerBackend db;
	@Inject
	private AuthCache auth; // for cached last contact timestamp

	/**
	 * Retrieve a list of registered nodes with the
	 * broker.
	 * @return JSON list of nodes
	 */
	@GET
	@Produces(MediaType.APPLICATION_XML)
	@RequireAdmin
	public Response allNodes(){
		try {
			List<Node> nodes = db.getAllNodes();
			auth.fillCachedAccessTimestamps(nodes);
			return Response.ok(new NodeList(nodes)).build();
			// look up cached last contact in AuthCache
		} catch (SQLException e) {
			log.log(Level.SEVERE, "unable to retrieve node list", e);
			return Response.serverError().build();
		}
	}
	/**
	 * Retrieve information about a single node.
	 * @param nodeId node id
	 * @return status {@code 200} with node info or status {@code 404} if not found. 
	 */
	@GET
	@Path("{id}")
	@Produces(MediaType.APPLICATION_XML)
	@RequireAdmin
	public Node getNodeInfo(@PathParam("id") int nodeId){
		Node node;
		try {
			node = db.getNode(nodeId);
			if( node == null ){
				throw new NotFoundException();
			}
			// look up cached last contact in AuthCache
			auth.fillCachedAccessTimestamps(Collections.singletonList(node));
		} catch (SQLException e) {
			log.log(Level.SEVERE, "unable to retrieve node list", e);
			throw new InternalServerErrorException(e);
		}
		return node;
	}
	/**
	 * Retrieve a named resource uploaded previously by the specified node.
	 * <p>
	 * Last modified and eTag headers will be set.
	 * The eTag is calculated {@code url-safe-base64(sha-256(data))}.
	 * </p>
	 * @param nodeId node id
	 * @param resourceId resource id
	 * @return status {@code 200} with node info or status {@code 404} if not found. 
	 * @throws SQLException sql error
	 */
	@GET
	@Path("{node}/{resource}")
	@RequireAdmin
	public Response getNodeResource(@PathParam("node") int nodeId, @PathParam("resource") String resourceId) throws SQLException{		
		DateDataSource ds = db.getNodeResource(nodeId, resourceId);
		if( ds == null ){
			throw new NotFoundException();
		}
		ResponseBuilder resp = Response.ok(ds, ds.getContentType());
		// set etag
		if( ds instanceof DigestPathDataSource ){
			resp.tag(Base64.getUrlEncoder().encodeToString(((DigestPathDataSource)ds).sha256));
			resp.header("Content-MD5", Base64.getUrlEncoder().encodeToString(((DigestPathDataSource)ds).md5));
		}
		// set last modified
		resp.lastModified(Date.from(ds.getLastModified()));
		return resp.build();
	}
	
}

@@ -464,25 +464,129 @@
 rs.core.
-MediaType
+CacheControl;%0Aimport javax.ws.rs.core.Context;%0Aimport javax.ws.rs.core.MediaType;%0Aimport javax.ws.rs.core.Request
 ;%0Aimport
@@ -1116,16 +1116,54 @@
 estamp%0A%0A
+%09@Context%0A%09private Request request;%0A%09%0A
 %09/**%0A%09 *
@@ -3052,24 +3052,202 @@
 %7D%0A%09%09
-ResponseBuilder 
+Date lastModified = Date.from(ds.getLastModified());%0A%09%09ResponseBuilder resp = request.evaluatePreconditions(lastModified);%0A%09%09if( resp == null )%7B%0A%09%09%09// timestamps different, resource modified%0A%09%09%09
 resp
@@ -3289,16 +3289,17 @@
 e());%0A%09%09
+%09
 // set e
@@ -3302,16 +3302,89 @@
 et etag%0A
+%09%09%7Delse%7B%0A%09%09%09log.info(%22Resource not changed %22+nodeId+%22/%22+resourceId);%0A%09%09%7D%0A
 %09%09if( ds
@@ -3659,42 +3659,198 @@
 ied(
-Date.from(ds.getLastModified())
+lastModified);%0A%09%09// add cache control header%0A%09%09CacheControl cc = new CacheControl();%0A%09%09cc.setMustRevalidate(true);%0A%09%09cc.setMaxAge(60*5); // may cache for 5 minutes%0A%09%09resp.cacheControl(cc
 );%0A
+%0A
 %09%09re

allow conditional http get for node resources: lastModified precondition