<commit_before>package org.myrobotlab.service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResponse;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.BroadcasterFactory;
import org.atmosphere.nettosphere.Config;
import org.atmosphere.nettosphere.Handler;
import org.atmosphere.nettosphere.Nettosphere;
import org.myrobotlab.codec.Codec;
import org.myrobotlab.codec.CodecFactory;
import org.myrobotlab.codec.Encoder;
import org.myrobotlab.codec.MethodCache;
import org.myrobotlab.fileLib.Zip;
import org.myrobotlab.framework.Message;
import org.myrobotlab.framework.MethodEntry;
import org.myrobotlab.framework.Service;
import org.myrobotlab.framework.ServiceEnvironment;
import org.myrobotlab.framework.Status;
import org.myrobotlab.framework.StatusLevel;
import org.myrobotlab.logging.Level;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.Logging;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.net.BareBonesBrowserLaunch;
import org.myrobotlab.net.Connection;
//import org.myrobotlab.service.WebGUI3.Error;
import org.myrobotlab.service.interfaces.AuthorizationProvider;
import org.myrobotlab.service.interfaces.Gateway;
import org.myrobotlab.service.interfaces.ServiceInterface;
//import org.myrobotlab.webgui.WebGUIServlet;
import org.slf4j.Logger;

//@ManagedService(path = "/api")
//@ManagedService(path = "/snake")


public class WebGUI extends Service implements AuthorizationProvider, Gateway, Handler {

	private static final long serialVersionUID = 1L;

	public final static Logger log = LoggerFactory.getLogger(WebGUI.class);

	Integer port = 7777;
	transient Nettosphere nettosphere;
	transient Broadcaster broadcaster;
	transient BroadcasterFactory broadcastFactory;

	public String root = "root";
	boolean useLocalResources = false;
	boolean autoStartBrowser = true;
	
	public String startURL = "http://127.0.0.1:%d/index.html";

	
	// FIXME - shim for Shoutbox
	// deprecate ???
	public static class WebMsg {
		String clientid;
		// socket
		Message msg;
	}
	
	// SHOW INTERFACE
	// FIXME - allowAPI1(true|false)
	// FIXME - allowAPI2(true|false)
	// FIXME - allow Protobuf/Thrift/Avro 
	// FIXME - NO JSON ENCODING SHOULD BE IN THIS FILE !!!

	public WebGUI(String n) {
		super(n);
	}

	// ================ Gateway begin ===========================

	@Override
	public void addConnectionListener(String name) {
		// TODO Auto-generated method stub

	}

	@Override
	public void connect(String uri) throws URISyntaxException {
		// TODO Auto-generated method stub

	}

	@Override
	public HashMap<URI, Connection> getClients() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Connection> getConnections(URI clientKey) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getPrefix(URI protocolKey) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Connection publishNewConnection(Connection keys) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void sendRemote(String key, Message msg) throws URISyntaxException {
		// TODO Auto-generated method stub

	}

	@Override
	public void sendRemote(URI key, Message msg) {
		// TODO Auto-generated method stub

	}

	// ================ Gateway end ===========================

	// ================ AuthorizationProvider begin ===========================

	@Override
	public boolean allowExport(String serviceName) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isAuthorized(HashMap<String, String> security, String serviceName, String method) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isAuthorized(Message msg) {
		// TODO Auto-generated method stub
		return false;
	}

	// ================ AuthorizationProvider end ===========================

	// ================ Broadcaster begin ===========================
	public void broadcast(Message msg) {
		broadcaster.broadcast(msg); // wtf
	}

	// ================ Broadcaster end ===========================

	public void startService() {

		//Broadcaster b = broadcasterFactory.get();
		
		// a session "might" be nice - but for now we are stateless
		// SessionSupport ss = new SessionSupport();

		Config.Builder configBuilder = new Config.Builder();
		configBuilder
		//.resource("C:\\tools\\myrobotlab-WebGUI\\src\\resource\\WebGUI")
		
				//.resource("./root")
				.resource("./src/resource/WebGUI")
                .resource("./src/resource")
				// .resource("./rest")  SHOULD I DO THIS ?
				// .resource(this)
				// Support 2 APIs
				// REST - http://host/object/method/param0/param1/...  synchronous DO NOT SUSPEND
				.resource("/api", this)// TODO - go beyond Servlets
				//.resource("/api", WebGUIServlet.class)

				// For mvn exec:java
				// .resource("./src/main/resources")

				// For running inside an IDE
				// .resource("./nettosphere-samples/games/src/main/resources")
		
				// if Jetty is in the classpath it will use it by default - we want to use Netty
				.initParam("org.atmosphere.cpr.asyncSupport", "org.atmosphere.container.NettyCometSupport")
				.initParam(ApplicationConfig.SCAN_CLASSPATH, "false")
				.initParam(ApplicationConfig.PROPERTY_SESSION_SUPPORT, "true")
				.port(port).host("0.0.0.0").build();
		//.host("127.0.0.1").build();
		Nettosphere s = new Nettosphere.Builder().config(configBuilder.build()).build();
		
		s.start();
		
		broadcastFactory = s.framework().getBroadcasterFactory();
		// get default boadcaster
		broadcaster = broadcastFactory.get("/*");

		log.info("WebGUI2 {} started on port {}", getName(), port);

		// BufferedReader br = new BufferedReader(new
		// InputStreamReader(System.in));
		if (autoStartBrowser) {
			log.info("auto starting default browser");
			BareBonesBrowserLaunch.openURL(String.format(startURL, port));
		}
		
	}

	@Override
	public String[] getCategories() {
		return new String[] { "display" };
	}

	@Override
	public String getDescription() {
		return "web enabled gui";
	}
	
	
	public Map<String, String> getHeadersInfo(HttpServletRequest request) {

		Map<String, String> map = new HashMap<String, String>();

		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String key = (String) headerNames.nextElement();
			String value = request.getHeader(key);
			map.put(key.toLowerCase(), value);
		}

		return map;
	}

	/**
	 * With a single method Atmosphere does so much !!!
	 * It sets up the connection, possibly gets a session, turns the
	 * request into something like a HTTPServletRequest, provides us with 
	 * input & output streams - and manages all the "long polling" or websocket
	 * upgrades on its own !
	 * 
	 * Atmosphere Rocks !
	 */
	@Override
	public void handle(AtmosphereResource r) {
		
		Codec codec = null;
		OutputStream out = null;

		try {
			
			AtmosphereRequest request = r.getRequest();
			AtmosphereResponse response = r.getResponse();
			InputStream in = r.getRequest().getInputStream();
			out = r.getResponse().getOutputStream();

			Map<String, String> headers = getHeadersInfo(request);

			if (headers.containsKey("content-type")) {
				log.info(String.format(String.format("in encoding : content-type %s", headers.get("content-type"))));
			}
			if (headers.containsKey("accept")) {
				log.info(String.format(String.format("out encoding : accept %s", headers.get("accept"))));
			}
			
			// FIXME reconstruct REST request & log
			String pathInfo = request.getPathInfo();
			String[] parts = null;

			// get default encoder
			codec = CodecFactory.getCodec(Encoder.MIME_TYPE_MESSAGES);

			if (pathInfo != null) {
				parts = pathInfo.split("/");
			}

			if (parts == null || parts.length < 3) {
				response.addHeader("Content-Type", codec.getMimeType());
				handleError(out, codec, "API", "http(s)://{host}:{port}/api/{api-type}/{Object}/{Method}");
				return;
			}
			
			// set specified encoder
			
			String apiTypeKey = parts[2];
			String codecMimeType = Encoder.getKeyToMimeType(apiTypeKey);
			if (!codecMimeType.equals(codec.getMimeType())){
				// request to switch codec types on 
				codec = CodecFactory.getCodec(codecMimeType);
			}
			
			response.addHeader("Content-Type", codec.getMimeType());

			ArrayList<MethodEntry> info = null;
			if (parts.length == 3) {
				// *** /api/messages  ***
				ServiceEnvironment si = Runtime.getLocalServices();
				
				/* TODO - relfect with javdoc info
				log.info("inspecting");
				
				Method[] methods = clazz.getDeclaredMethods();
				info = new ArrayList<MethodInfo>();
				for (Method method : methods) {
					if (!filter.contains(method.getName())) {
						MethodInfo m = new MethodInfo();
						m.name = method.getName();
						Class<?>[] types = method.getParameterTypes();
						m.parameterTypes = new String[types.length];
						for (int i = 0; i < types.length; ++i) {
							m.parameterTypes[i] = types[i].getSimpleName();
						}
						m.returnType = method.getReturnType().getSimpleName(); // NULL
																				// ?
						info.add(m);
					}
				}
				*/
				
				respond(out, codec, "getLocalServices", si);
				return;
			} else if (parts.length == 4) {
				// *** /api/messages/runtime  ***<commit_msg>http://host:port/api/messages now suspends (with heartbeat)<commit_after>package org.myrobotlab.service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResponse;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.cpr.BroadcasterFactory;
import org.atmosphere.nettosphere.Config;
import org.atmosphere.nettosphere.Handler;
import org.atmosphere.nettosphere.Nettosphere;
import org.myrobotlab.codec.Codec;
import org.myrobotlab.codec.CodecFactory;
import org.myrobotlab.codec.Encoder;
import org.myrobotlab.codec.MethodCache;
import org.myrobotlab.fileLib.Zip;
import org.myrobotlab.framework.Message;
import org.myrobotlab.framework.MethodEntry;
import org.myrobotlab.framework.Service;
import org.myrobotlab.framework.ServiceEnvironment;
import org.myrobotlab.framework.Status;
import org.myrobotlab.framework.StatusLevel;
import org.myrobotlab.logging.Level;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.logging.Logging;
import org.myrobotlab.logging.LoggingFactory;
import org.myrobotlab.net.BareBonesBrowserLaunch;
import org.myrobotlab.net.Connection;
//import org.myrobotlab.service.WebGUI3.Error;
import org.myrobotlab.service.interfaces.AuthorizationProvider;
import org.myrobotlab.service.interfaces.Gateway;
import org.myrobotlab.service.interfaces.ServiceInterface;
//import org.myrobotlab.webgui.WebGUIServlet;
import org.slf4j.Logger;

//@ManagedService(path = "/api")
//@ManagedService(path = "/snake")


public class WebGUI extends Service implements AuthorizationProvider, Gateway, Handler {

	private static final long serialVersionUID = 1L;

	public final static Logger log = LoggerFactory.getLogger(WebGUI.class);

	Integer port = 7777;
	transient Nettosphere nettosphere;
	transient Broadcaster broadcaster;
	transient BroadcasterFactory broadcastFactory;

	public String root = "root";
	boolean useLocalResources = false;
	boolean autoStartBrowser = true;
	
	public String startURL = "http://127.0.0.1:%d/index.html";

	
	// FIXME - shim for Shoutbox
	// deprecate ???
	public static class WebMsg {
		String clientid;
		// socket
		Message msg;
	}
	
	// SHOW INTERFACE
	// FIXME - allowAPI1(true|false)
	// FIXME - allowAPI2(true|false)
	// FIXME - allow Protobuf/Thrift/Avro 
	// FIXME - NO JSON ENCODING SHOULD BE IN THIS FILE !!!

	public WebGUI(String n) {
		super(n);
	}

	// ================ Gateway begin ===========================

	@Override
	public void addConnectionListener(String name) {
		// TODO Auto-generated method stub

	}

	@Override
	public void connect(String uri) throws URISyntaxException {
		// TODO Auto-generated method stub

	}

	@Override
	public HashMap<URI, Connection> getClients() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<Connection> getConnections(URI clientKey) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getPrefix(URI protocolKey) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Connection publishNewConnection(Connection keys) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void sendRemote(String key, Message msg) throws URISyntaxException {
		// TODO Auto-generated method stub

	}

	@Override
	public void sendRemote(URI key, Message msg) {
		// TODO Auto-generated method stub

	}

	// ================ Gateway end ===========================

	// ================ AuthorizationProvider begin ===========================

	@Override
	public boolean allowExport(String serviceName) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isAuthorized(HashMap<String, String> security, String serviceName, String method) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isAuthorized(Message msg) {
		// TODO Auto-generated method stub
		return false;
	}

	// ================ AuthorizationProvider end ===========================

	// ================ Broadcaster begin ===========================
	public void broadcast(Message msg) {
		broadcaster.broadcast(msg); // wtf
	}

	// ================ Broadcaster end ===========================

	public void startService() {

		//Broadcaster b = broadcasterFactory.get();
		
		// a session "might" be nice - but for now we are stateless
		// SessionSupport ss = new SessionSupport();

		Config.Builder configBuilder = new Config.Builder();
		configBuilder
		//.resource("C:\\tools\\myrobotlab-WebGUI\\src\\resource\\WebGUI")
		
				//.resource("./root")
				.resource("./src/resource/WebGUI")
                .resource("./src/resource")
				// .resource("./rest")  SHOULD I DO THIS ?
				// .resource(this)
				// Support 2 APIs
				// REST - http://host/object/method/param0/param1/...  synchronous DO NOT SUSPEND
				.resource("/api", this)// TODO - go beyond Servlets
				//.resource("/api", WebGUIServlet.class)

				// For mvn exec:java
				// .resource("./src/main/resources")

				// For running inside an IDE
				// .resource("./nettosphere-samples/games/src/main/resources")
		
				// if Jetty is in the classpath it will use it by default - we want to use Netty
				.initParam("org.atmosphere.cpr.asyncSupport", "org.atmosphere.container.NettyCometSupport")
				.initParam(ApplicationConfig.SCAN_CLASSPATH, "false")
				.initParam(ApplicationConfig.PROPERTY_SESSION_SUPPORT, "true")
				.port(port).host("0.0.0.0").build();
		//.host("127.0.0.1").build();
		Nettosphere s = new Nettosphere.Builder().config(configBuilder.build()).build();
		
		s.start();
		
		broadcastFactory = s.framework().getBroadcasterFactory();
		// get default boadcaster
		broadcaster = broadcastFactory.get("/*");

		log.info("WebGUI2 {} started on port {}", getName(), port);

		// BufferedReader br = new BufferedReader(new
		// InputStreamReader(System.in));
		if (autoStartBrowser) {
			log.info("auto starting default browser");
			BareBonesBrowserLaunch.openURL(String.format(startURL, port));
		}
		
	}

	@Override
	public String[] getCategories() {
		return new String[] { "display" };
	}

	@Override
	public String getDescription() {
		return "web enabled gui";
	}
	
	
	public Map<String, String> getHeadersInfo(HttpServletRequest request) {

		Map<String, String> map = new HashMap<String, String>();

		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String key = (String) headerNames.nextElement();
			String value = request.getHeader(key);
			map.put(key.toLowerCase(), value);
		}

		return map;
	}

	/**
	 * With a single method Atmosphere does so much !!!
	 * It sets up the connection, possibly gets a session, turns the
	 * request into something like a HTTPServletRequest, provides us with 
	 * input & output streams - and manages all the "long polling" or websocket
	 * upgrades on its own !
	 * 
	 * Atmosphere Rocks !
	 */
	@Override
	public void handle(AtmosphereResource r) {
		
		Codec codec = null;
		OutputStream out = null;

		try {
			
			AtmosphereRequest request = r.getRequest();
			AtmosphereResponse response = r.getResponse();
			InputStream in = r.getRequest().getInputStream();
			out = r.getResponse().getOutputStream();

			Map<String, String> headers = getHeadersInfo(request);

			if (headers.containsKey("content-type")) {
				log.info(String.format(String.format("in encoding : content-type %s", headers.get("content-type"))));
			}
			if (headers.containsKey("accept")) {
				log.info(String.format(String.format("out encoding : accept %s", headers.get("accept"))));
			}
			
			// FIXME reconstruct REST request & log
			String pathInfo = request.getPathInfo();
			String[] parts = null;

			// get default encoder
			codec = CodecFactory.getCodec(Encoder.MIME_TYPE_MESSAGES);

			if (pathInfo != null) {
				parts = pathInfo.split("/");
			}

			if (parts == null || parts.length < 3) {
				response.addHeader("Content-Type", codec.getMimeType());
				handleError(out, codec, "API", "http(s)://{host}:{port}/api/{api-type}/{Object}/{Method}");
				return;
			}
			
			// set specified encoder
			
			String apiTypeKey = parts[2];
			
			if ("messages".equals(apiTypeKey)){
				if (!r.isSuspended()){
					r.suspend();
				}
			}
			
			String codecMimeType = Encoder.getKeyToMimeType(apiTypeKey);
			if (!codecMimeType.equals(codec.getMimeType())){
				// request to switch codec types on 
				codec = CodecFactory.getCodec(codecMimeType);
			}
			
			response.addHeader("Content-Type", codec.getMimeType());

			ArrayList<MethodEntry> info = null;
			if (parts.length == 3) {
				// *** /api/messages  **
				
				ServiceEnvironment si = Runtime.getLocalServices();
				
				/* TODO - relfect with javdoc info
				log.info("inspecting");
				
				Method[] methods = clazz.getDeclaredMethods();
				info = new ArrayList<MethodInfo>();
				for (Method method : methods) {
					if (!filter.contains(method.getName())) {
						MethodInfo m = new MethodInfo();
						m.name = method.getName();
						Class<?>[] types = method.getParameterTypes();
						m.parameterTypes = new String[types.length];
						for (int i = 0; i < types.length; ++i) {
							m.parameterTypes[i] = types[i].getSimpleName()
						}
						m.returnType = method.getReturnType().getSimpleName(); // NULL
																				// ?
						info.add(m);
					}
				}
				*/
				
				respond(out, codec, "getLocalServices", si);
				return;
			} else if (parts.length == 4) {
				// *** /api/messages/runtime  ***
{'fraction_non_alphanumeric': 0.14881801508049725, 'fraction_numerical': 0.0034644385571632364, 'mean_word_length': 3.814569536423841, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 6, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1067981', 'n_tokens_mistral': 6818, 'n_tokens_neox': 5885, 'n_words': 1381}