package com.googlecode.jsonrpc4j.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.googlecode.jsonrpc4j.JsonRpcClient;
import com.googlecode.jsonrpc4j.JsonRpcServer;
import com.googlecode.jsonrpc4j.ProxyUtil;
import com.googlecode.jsonrpc4j.RequestInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.atomic.AtomicBoolean;

public class LocalThreadServer<T> extends Thread implements AutoCloseable {
	private static final Logger logger = LoggerFactory.getLogger(LocalThreadServer.class);
	
	private final InputStream serverInput = new PipedInputStream();
	private final OutputStream serverOutput = new PipedOutputStream();
	private final AtomicBoolean isAlive = new AtomicBoolean(true);
	
	private final JsonRpcServer jsonRpcServer;
	private final Class<?> remoteInterface;
	private final Object service;
	
	public LocalThreadServer(Object service, final Class<?> remoteInterface) {
		this.remoteInterface = remoteInterface;
		this.service = service;
		jsonRpcServer = new JsonRpcServer(new ObjectMapper(), service, remoteInterface);
		start();
	}
	
	public void setRequestInterceptor(RequestInterceptor interceptor) {
		jsonRpcServer.setRequestInterceptor(interceptor);
	}
	
	public T client(Class<T> clazz) throws IOException {
		InputStream clientInput = new PipedInputStream((PipedOutputStream) serverOutput);
		OutputStream clientOutput = new PipedOutputStream((PipedInputStream) serverInput);
		return ProxyUtil.createClientProxy(ClassLoader.getSystemClassLoader(), clazz, new JsonRpcClient(), clientInput, clientOutput);
	}
	
	@Override
	public void run() {
		logger.debug("started local thread server for interface {} and handler {}", remoteInterface, service);
		while (isAlive.get()) {
			try {
				final int available = serverInput.available();
				if (available > 0) {
					jsonRpcServer.handleRequest(serverInput, serverOutput);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	@Override
	public void close() throws Exception {
		isAlive.set(false);
	}
}