package com.charego.lingo.client.multiplayer;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.web.client.RootUriTemplateHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.messaging.converter.ByteArrayMessageConverter;
import org.springframework.messaging.converter.CompositeMessageConverter;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;

@Configuration
public class MultiplayerConfig {

	@Bean
	public TaskExecutor taskExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(4);
		executor.setMaxPoolSize(4);
		executor.setThreadNamePrefix("TaskThread-");
		executor.initialize();
		return executor;
	}

	@Bean
	public WebSocketClient webSocketClient() {
		List<Transport> transports = new ArrayList<>();
		transports.add(new WebSocketTransport(new StandardWebSocketClient()));
		transports.add(new RestTemplateXhrTransport());
		return new SockJsClient(transports);
	}

	@Bean
	public WebSocketStompClient stompClient(WebSocketClient webSocketClient, MessageConverter messageConverter) {
		WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
		stompClient.setMessageConverter(messageConverter);
		stompClient.setTaskScheduler(heartbeatScheduler());
		stompClient.setDefaultHeartbeat(heartbeatValue());
		return stompClient;
	}

	@Bean
	public MessageConverter messageConverter() {
		List<MessageConverter> converters = new ArrayList<>();
		converters.add(new StringMessageConverter());
		converters.add(new ByteArrayMessageConverter());
		converters.add(new MappingJackson2MessageConverter());
		return new CompositeMessageConverter(converters);
	}

	@Bean
	public RestTemplate restTemplate(Environment env) {
		String webServerUrl = env.getRequiredProperty("lingo.url");
		RestTemplate restTemplate = new RestTemplate();
		restTemplate.setUriTemplateHandler(new RootUriTemplateHandler(webServerUrl));
		return restTemplate;
	}

	private TaskScheduler heartbeatScheduler() {
		// Single thread unless/until more threads are needed
		final ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
		taskScheduler.setPoolSize(1);
		taskScheduler.setThreadNamePrefix("Keep-Alive-");
		taskScheduler.initialize();
		return taskScheduler;
	}

	private long[] heartbeatValue() {
		return new long[] { 25000, 25000 };
	}

}
