package com.group.service.io;

import com.taurus.core.util.ICallback;
import com.taurus.core.util.Logger;
import com.group.service.*;
import com.group.service.TaurusClient.ConnectionProtocol;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

/**
 * SocketEngine
 * 
 * @author daixiwei daixiwei15@126.com
 *
 */
public class SocketEngine implements ISocketLayer {
	private static final Logger		logger			= Logger.getLogger(SocketEngine.class);

	ISocket							_socket;
	private String					_sessionToken;
	private String					_serverAddress;

	private ICallback<SocketCode>	_onConnect;
	ICallback<Message>		_onEvent;
	private Queue<Runnable>			_actionQueue	= new LinkedList<Runnable>();

	SystemController				_controller;
	LagMonitor						_lagMonitor;
	IOHandler						_packetHandler;
	TaurusClient _client;
	
	public SocketEngine(TaurusClient client) {
		_socket = new SocketTcp(this);
		_controller = new SystemController(this);
		_packetHandler = new IOHandler(this, _controller);
		_lagMonitor = new LagMonitor(this, NetManager.TIMEOUT_TIME, 10);
	}

	@Override
	public void connect(String ip, String game) {
		_serverAddress = ip;
		_socket.connect();
	}

	@Override
	public void disconnect() {
		_socket.disconnect();
		if (_lagMonitor != null)
			_lagMonitor.destroy();
		_lagMonitor = null;
	}

	@Override
	public void processEvent() {
		if (_socket.isConnected() && _controller.isTimeOut()) {
			if (_onConnect != null)
				_onConnect.action(SocketCode.TimeoutDisconnect);
			return;
		}

		while (true) {
			Runnable myAction;
			synchronized (_actionQueue) {
				if (_actionQueue.size() <= 0) {
					break;
				}
				myAction = _actionQueue.poll();
			}
			myAction.run();
		}
	}

	public void write(Message msg) {
		try {
			_controller.sendRequest(msg);
		} catch (IOException e) {
			logger.error(e);
		}
	}

	void onStatusChanged(SocketCode statusCode) {
		if (statusCode != SocketCode.Connect) {
			_socket.disconnect();
			this.disconnect();
		}
		System.out.println("status : " + statusCode);
		logger.info("status : " + statusCode);
		if (statusCode == SocketCode.Connect) {
			this.onHandshake();
		} else {
			if (_onConnect != null)
				_onConnect.action(statusCode);
		}
	}

	void onHandshake() {
		_controller.refreshTime();
		_lagMonitor.start();
		if (_onConnect != null)
			_onConnect.action(SocketCode.Connect);
	}

	void enqueueActionForDispatch(Runnable action) {
		synchronized (_actionQueue) {
			_actionQueue.offer(action);
		}
	}

	void enqueueStatusCallback(final SocketCode statusValue) {
		synchronized (_actionQueue) {
			_actionQueue.offer(new Runnable() {
				@Override
				public void run() {
					onStatusChanged(statusValue);
				}
			});
		}
	}




	public String getServerAddress() {
		return _serverAddress;
	}

	@Override
	public ConnectionProtocol getProtocol() {
		return ConnectionProtocol.Tcp;
	}
	
	public int getAveragePingTime() {
		return _lagMonitor != null ? _lagMonitor.getAveragePingTime() : 1000;
	}

	public void setSession(String session) {
		_sessionToken = session == null ? "" : session;
	}

	public String getSession() {
		return _sessionToken;

	}

	@Override
	public void setOnConnect(ICallback<SocketCode> onConnect) {
		_onConnect = onConnect;
	}

	@Override
	public void setOnEvent(ICallback<Message> onEvent) {
		_onEvent = onEvent;
	}

	public int getProtocolCompression() {
		return 100;
	}
}
