package com.armundia.core.websocket;

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.json.bind.Jsonb;
import javax.websocket.Session;

import com.armundia.core.JsonbProvider;
import com.armundia.core.websocket.pojo.SessionContainer;
import com.armundia.core.websocket.pojo.WebSocketAnnotatedMethod;
import com.armundia.core.websocket.pojo.WebSocketEvent;

@ApplicationScoped
public class WebSocket {
	private static final Logger logger = Logger.getLogger(WebSocket.class.getName());
	Map<String, WebSocketAnnotatedMethod> on;
	private Jsonb jsonb = new JsonbProvider().getContext(null);

	@Inject
	private WebSocketApplication application;
	
	public <T> T fromJson(String json, Class<T> clazz) {
		return jsonb.fromJson(json, clazz);
	}

	public <T> void emit(String message, T data) {
		String json = jsonb.toJson(data);
		WebSocketEvent event = new WebSocketEvent(message, json);

		for (SessionContainer sessionContainer : application.getSessions()) {
			sendObject(event, sessionContainer);
		}
	}

	public <T> void emit(String message, T data, SessionContainer sessionContainer) {
		String json = jsonb.toJson(data);
		WebSocketEvent event = new WebSocketEvent(message, json);

		sendObject(event, sessionContainer);
	}
	
	private void sendObject(WebSocketEvent event, SessionContainer sessionContainer) {
		Session session = sessionContainer.session;
		if (session.isOpen()) {
			try {
				logger.info("Sending message [" + event.message + "] to " + session.getId());
				session.getBasicRemote().sendObject(event);
			} catch (Exception e) {
				logger.log(Level.SEVERE, "Errore in invio messaggio", e);
			}
		}
	}
}

/* FIXME: functionalities that doesnt work properly with Stateful beans
	@PostConstruct
	public void init() {
		on = WebSocketAnnotationScanner.getOnMessages();
		logger.log(Level.INFO, "on: {0}", on);
	}

	public void onMessage(Session session, WebSocketEvent event) throws ArmundiaException {
		WebSocketAnnotatedMethod annotatedMethod = on.get(event.message);
		if (annotatedMethod != null) {
			Object object = getObject(annotatedMethod.clazz);

			List<Object> arguments = getArguments(annotatedMethod.argumentsType, event.data, session);
			try {
				if (arguments.isEmpty()) {
					annotatedMethod.method.invoke(object);
				} else if (arguments.size() == 1){
					annotatedMethod.method.invoke(object, arguments.get(0));
				} else if (arguments.size() == 2) {
					annotatedMethod.method.invoke(object, arguments.get(0), arguments.get(1));
				}
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				logger.log(Level.SEVERE, e.getMessage());

				throw new ArmundiaException(e);
			}
		} else {
			logger.log(Level.WARNING, "Message not handled: {0}", event.message);
		}
	}

	private <T> T getObject(Class<T> clazz) throws ArmundiaException {
		T object = getBean(clazz);
		if (object == null) {
			// new class from clazz
			try {
				object = clazz.getDeclaredConstructor().newInstance();
			} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
					| InvocationTargetException | NoSuchMethodException | SecurityException e) {
				logger.log(Level.SEVERE, e.getMessage());

				throw new ArmundiaException(e);
			}
		}

		return object;
	}

	private <T> T getBean(Class<T> beanClass) {
		Instance<T> instance = CDI.current().select(beanClass);
		if (instance.isResolvable()) {
			return instance.get();
		}

		return null;
	}

	private List<Object> getArguments(Class<?>[] clazzs, String data, Session session) {
		if (data == null || clazzs == null) {
			return new ArrayList<>();
		}
		SessionContainer sessionContainer = application.getSession(session);

		List<Object> arguments = new ArrayList<>();
		for (Class<?> clazz : clazzs) {
			if (clazz == SessionContainer.class) {
				arguments.add(sessionContainer);
			} else {
				Object argument = jsonb.fromJson(data, clazz);
				arguments.add(argument);
			}
		}

		return arguments;
	}
 */
