package net.yak.stalk;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;

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

import net.yak.stalk.Pro.Assoc;
import net.yak.stalk.Pro.FalseObj;
import net.yak.stalk.Pro.Map;
import net.yak.stalk.Pro.NilObj;
import net.yak.stalk.Pro.Num;
import net.yak.stalk.Pro.Str;
import net.yak.stalk.Pro.TrueObj;
import net.yak.stalk.Pro.Vec;
import net.yak.stalk.Talk.Terp;

public class WebServer {
	public static class Ht extends Talk { // For XSS Safety.
		private StringBuffer sb;

		public Ht() {
			this.sb = new StringBuffer();
		}
		public Ht(Ht that) {
			this.sb = new StringBuffer(that.sb.toString());
		}
		public Ht(String s) {
			this.sb = new StringBuffer(Talk.htmlEscape(s));
		}
		public String toString() {
			return sb.toString();
		}
		public Ht append(String s) {
			sb.append(htmlEscape(s));
			return this;
		}
		public Ht append(Ht that) {
			sb.append(that.toString());
			return this;
		}
		public Ht append(Pro that) {
			sb.append(htmlEscape(that.toString()));
			return this;
		}
		static public Ht tag(Ht appendMe, String type, String[] args,
				String body) {
			return tag(appendMe, type, args, new Ht(body));
		}
		static public Ht tag(Ht appendMe, String type, String[] args, Ht body) {
			Ht z = appendMe == null ? new Ht() : appendMe;
			assert htmlTagP.matcher(type).matches();
			z.sb.append(fmt("<%s ", type));
			if (args != null) {
				for (int i = 0; i < args.length; i += 2) {
					assert htmlTagP.matcher(args[i]).matches();
					z.sb.append(fmt("%s=\"%s\" ", args[i],
							htmlEscape(args[i + 1])));
				}
			}
			z.sb.append(fmt(">%s</%s>", body, type));
			return z;
		}
	}

	public static class InspectorVisitor extends Terp.Visitor {
		Ht ht;

		InspectorVisitor() {
			ht = new Ht();
		}

		void visitPro(Pro a) {
			ht.append("<Pro>:" + a);
		}
		void visitCls(Cls a) {
		}
		void visitNum(Num a) {
			Ht.tag(ht, "span", new String[] { "style", "color=#555555" }, ""
					+ a.num);
		}
		void visitStr(Str a) {
			Ht.tag(ht, "span", new String[] { "style",
					"color: #338033; font-family: courier" }, a.str);
		}
		void visitFalseObj(FalseObj a) {
		}
		void visitTrueObj(TrueObj a) {
		}
		void visitNilObj(NilObj a) {
		}
		void visitVec(Vec a) {
			Ht defs = new Ht();
			for (int i = 0; i < a.vec.size(); i++) {
				InspectorVisitor valueVisitor = new InspectorVisitor();
				a.vec.get(i).visit(valueVisitor);
				Ht.tag(defs, "li", null, valueVisitor.ht);
			}
			Ht.tag(ht, "ul", null, defs);
		}
		void visitMap(Map a) {
			Ht defs = new Ht();
			Assoc[] assocs = a.sortedAssocs();
			for (int i = 0; i < assocs.length; i++) {
				InspectorVisitor keyVisitor = new InspectorVisitor();
				assocs[i].key.visit(keyVisitor);
				InspectorVisitor valueVisitor = new InspectorVisitor();
				assocs[i].value.visit(valueVisitor);
				Ht.tag(defs, "dt", null, keyVisitor.ht.append(" :"));
				Ht.tag(defs, "dd", null, valueVisitor.ht);
			}
			Ht.tag(ht, "dl", null, defs);
		}
		void visitAssoc(Assoc a) {
		}
		void visitUsr(Usr a) {
		}
	}

	public static class SaidMapRenderer {
		Map map;

		public SaidMapRenderer(Map map) {
			this.map = map;
		}
		public Ht toHt() {
			InspectorVisitor vis = new InspectorVisitor();
			vis.visitMap(map);
			return vis.ht;
		}
	}

	public static class FavIconHandler extends Talk implements HttpHandler {

		public void handle(HttpExchange t) throws IOException {
			Headers respHeads = t.getResponseHeaders();
			respHeads.set("Content-Type", "text/plain");
			t.sendResponseHeaders(400, 0);
			OutputStream os = t.getResponseBody();
			os.close();
		}
	}

	public static class TerpHandler extends Talk implements HttpHandler {
		Terp terp;

		public TerpHandler() {
			super();
			this.terp = new Terp();
			System.err.println("Initialized Terp Hander.");
		}
		public void handle(HttpExchange t) throws IOException {
			System.err.println("handle().");
			String response = "?";
			String responseType = "text/plain";
			try {
				InputStream is = t.getRequestBody();
				String submissions = readAll(is);

				URI uri = t.getRequestURI();
				String path = uri.getPath();
				String query = uri.getQuery();
				try {
					Map map = terp.handleUrl(path, query, submissions);
					response = new SaidMapRenderer(map).toHt().toString();
					// response = htmlEscape(map.toString());
					responseType = "text/html";
				} catch (Exception ex) {
					ex.printStackTrace();
					response = "*** ERROR *** " + ex;
					System.err.println("[1] " + response);
				}
			} catch (Exception ex) {
				response = "*** (Outer handle) ERROR ***" + ex;
				System.err.println("[2] " + response);
			}

			Headers respHeads = t.getResponseHeaders();
			respHeads.set("Content-Type", responseType);
			t.sendResponseHeaders(200, response.length());
			OutputStream os = t.getResponseBody();
			os.write(response.getBytes());
			os.close();
		}
		public String readAll(InputStream is) throws IOException {
			StringBuffer b = new StringBuffer();
			while (true) {
				int c = is.read();
				if (c < 0)
					break;
				b.append((char) c);
			}
			return b.toString();
		}

	}

	public static void main(String[] args) throws IOException {
		HttpServer server = HttpServer.create(new InetSocketAddress(8000), 5);
		server.createContext("/favicon.ico", new FavIconHandler());
		server.createContext("/", new TerpHandler());
		server.setExecutor(null); // creates a default executor
		server.start();
	}

}
