/*
 * Copyright 2014 Loic Merckel
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package org.swiftexplorer.auth.server;


import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class AuthHttpServer implements SynchronousDataProvider<Map<String, String> > {

	final private Logger logger = LoggerFactory.getLogger(AuthHttpServer.class);
	
	final private int port ;
	private HttpServer server = null ;
	private ExecutorService httpThreadPool = null ;
	
	private volatile BlockingQueue<Map<String, String> > sharedQueue = null;
	private final Object lock = new Object () ;
	
	private static final Map<String, String> emptyMap = new HashMap <String, String> () ;
	
	public AuthHttpServer (int port)
	{
		super () ;
		this.port = port ;
	}
	
	
	@Override
	public void stopWaiting () throws InterruptedException
	{
		synchronized (lock)
		{
			if (sharedQueue == null)
				return ;
			sharedQueue.put(emptyMap);
		}
	}
	
	
	@Override
	public Map<String, String> startAndWaitForData () throws IOException, InterruptedException
	{
		InetSocketAddress addr = new InetSocketAddress(port);	
		BlockingQueue<Map<String, String> > blockingQueue = new LinkedBlockingQueue<Map<String, String> >();
		synchronized (lock)
		{
			sharedQueue = blockingQueue ;
			server = HttpServer.create(addr, 0);
			server.createContext("/", new HandlerMapParameter(blockingQueue));
			httpThreadPool = Executors.newCachedThreadPool() ;
			server.setExecutor(httpThreadPool);
			server.start();
		}
		return blockingQueue.poll(10 * 60, TimeUnit.SECONDS);
	}
	
	
	public void stopServer ()
	{
		synchronized (lock)
		{
			if (server == null)
				return ;
			server.stop(0);
			
			if (httpThreadPool == null) // should never happen...
				return ;
			
			httpThreadPool.shutdown(); 
			try 
			{ 
				httpThreadPool.awaitTermination(60, TimeUnit.SECONDS); 
			} 
			catch (InterruptedException e) 
			{			
				logger.error("Error occurred while stopping the server", e);
			}
		}
	}
	
	
	static private Map<String, String> queryToParameterMap (String query)
	{
	    if (query == null || query.isEmpty())
	    	return emptyMap ;
	    Map<String, String> ret = new HashMap<String, String>();
	    for (String str : query.split("&")) 
	    {
	        String paramNameVal[] = str.split("=");
	        if (paramNameVal.length > 1) 
	            ret.put(paramNameVal[0], paramNameVal[1]);
	        else
	            ret.put(paramNameVal[0], "");
	    }
	    return ret;
	}
	
	
	static private class HandlerMapParameter implements HttpHandler 
	{
		final private Logger logger = LoggerFactory.getLogger(HandlerMapParameter.class);
		
		final BlockingQueue<Map<String, String> > sharedQueue ;
		public HandlerMapParameter (BlockingQueue<Map<String, String> > sharedQueue)
		{
			super () ;
			this.sharedQueue = sharedQueue ;
		}
		
		@Override
		public void handle(HttpExchange exchange) throws IOException 
		{
			String requestMethod = exchange.getRequestMethod();
			if (requestMethod.equalsIgnoreCase("GET")) 
			{
				Headers responseHeaders = exchange.getResponseHeaders();
				responseHeaders.set("Content-Type", "text/plain");
				exchange.sendResponseHeaders(200, 0);
				
				OutputStream responseBody = exchange.getResponseBody();
				Headers requestHeaders = exchange.getRequestHeaders();
				Set<String> keySet = requestHeaders.keySet();
				Iterator<String> iter = keySet.iterator();
				while (iter.hasNext()) {
					String key = iter.next();
					List<String> values = requestHeaders.get(key);
					String s = key + " = " + values.toString() + "\n";
					responseBody.write(s.getBytes());		
				}
				responseBody.close();
					
				if (sharedQueue != null)
				{
					String query = exchange.getRequestURI().getQuery() ;
					try 
					{
						sharedQueue.put(queryToParameterMap(query));
					} 
					catch (InterruptedException e) 
					{
						logger.error("Error occurred in the server handler", e);
					}
				}
			}
		}
	}
}