package be.ac.ulb.code.wit;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import be.ac.ulb.code.wit.rangeparser.MediaRange;
import be.ac.ulb.code.wit.rangeparser.RangeMatcher;
import be.ac.ulb.code.wit.rangeparser.RangeParser.MediaRangeParser;

public class SimpleResourceServer extends AbstractHandler {
	String helloWorld = "Hello, World!";
	RangeMatcher<MediaRange> acceptMatcher = 
		new RangeMatcher<MediaRange>(new MediaRangeParser());
	
	private static final String MEDIATYPE_TEXT = "text/plain";
	private static final String MEDIATYPE_XML = "application/xml";
	
	public SimpleResourceServer() throws ParseException {
		// Register the format that we can output
		acceptMatcher.registerKnownType(MEDIATYPE_TEXT);
		acceptMatcher.registerKnownType(MEDIATYPE_XML);
	}
	
	/**
	 * Retrieves the resource data in a given format
	 * @param media
	 * @return
	 */
	public String getHello(String media) {
		if (media.equals(MEDIATYPE_TEXT)) {
			return helloWorld;
		}
		else if (media.equals(MEDIATYPE_XML)) {
			Document doc = XMLHelper.xml();
			Element message = XMLHelper.makeElement(doc, "message");
			message.setTextContent(helloWorld);
			return XMLHelper.stringify(doc);
		}
		return null;
	}
	
	/**
	 * Change the service data according to the text in the given stream.
	 * The data on the stream are expected to be plain text.
	 * 
	 * @param data The data to be used
	 * @throws IOException if the stream could not be read
	 */
	public void changeHello(InputStream data) throws IOException {
		helloWorld = 
			IOUtils.toString(data, "UTF-8");
	}
	
	private void writeHello(String requestMedia, HttpServletResponse response) throws IOException {
		String hello = getHello(requestMedia);
		response.setContentType(requestMedia);
		response.getWriter().println(hello);
		response.getWriter().flush();
	}
	
	public boolean handleHelloResource(
			HttpServletRequest request, 
			HttpServletResponse response,
			String requestMedia) throws IOException {
		
		if (request.getMethod().equals("GET")) {
			writeHello(requestMedia, response);
			return true;
		}
		if (request.getMethod().equals("PUT")) {
			String contentType = request.getHeader("Content-Type");
			if (contentType.equals("text/plain")) {
				changeHello(request.getInputStream());
				writeHello(requestMedia, response);
				return true;
			}
			else {
				response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
			}
			return true;
		}
		
		return false;
	}
	
	public void handle(String target,
			Request baseRequest,
			HttpServletRequest request,
			HttpServletResponse response) 
	throws IOException, ServletException
	{
		String accept = request.getHeader("Accept"); 
		MediaRange media = null;
		boolean badRequest = false;
		
		try {
			media = acceptMatcher.getBestMatch(accept);
		} 
		catch (ParseException e) {
			badRequest = true;
		}
		catch (NumberFormatException e) {
			badRequest = true;
		}
		
		if (badRequest) {
			response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
		}
		else if (media == null) {
			response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
		}
		else if ("/hello".equals(request.getRequestURI())) {
			if (!handleHelloResource(request, response, media.toString())) {
				response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
			}
		}
		else {
			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
		}
		baseRequest.setHandled(true);
	}
	
	public static void main(String[] args) throws Exception
	{
		// Setup the server 
		Server server = new Server(8080);
		server.setHandler(new SimpleResourceServer());
		server.start();
		server.join();
	}
}
