package com.cisdi.data.emserver;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import com.cisdi.data.emserver.gateway.impl.EmiaProIoSession;
import com.cisdi.data.emserver.gateway.impl.EmiaProSessionFactory;
import com.cisdi.data.emserver.gateway.vo.OtherParameterVo;
import com.cisdi.data.emserver.gateway.impl.AliveCheckTask;
import com.cisdi.data.emserver.gateway.impl.EmiaProChannelInitializer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.cisdi.data.common.exception.BusinessException;

import com.cisdi.data.sdk.consts.ServiceName;
import com.cisdi.data.sdk.enums.GatewayState;
import com.cisdi.data.sdk.gateway.base.SocketGatewayBase;
import com.cisdi.data.sdk.gateway.netty.IoSession;
import com.cisdi.data.sdk.gateway.netty.SessionFactory;
import com.cisdi.data.sdk.gateway.netty.TcpIoService;
import com.cisdi.data.sdk.gateway.netty.impl.DefaultTcpIoService;
import com.cisdi.data.sdk.param.ListenSocketParam;
import com.cisdi.data.sdk.procotol.message.SocketReturnMessage;
import com.cisdi.data.sdk.service.PlatformService;
import com.cisdi.data.sdk.service.RouteService;
import com.cisdi.data.sdk.vo.DeviceVo;
import com.cisdi.data.sdk.vo.ExeResultVo;
import com.cisdi.data.sdk.vo.SslConfigVo;


public class EmiaProSocketGateway extends SocketGatewayBase {
	
	private static Logger logger = LoggerFactory.getLogger(EmiaProSocketGateway.class);
	
	TcpIoService ioService = null;
	SessionFactory sessionFactory = null;
	OtherParameterVo parameterVo = null;
	AtomicBoolean shouldRun = null;
	
	// 当配置为单个设备模式时，此为该设备id
	String onlyOneDeviceId = null;
	
	/**
	 * 下发的时候，查配置路由，如果配置路由里面有匹配的，则走配置路由；如果没有匹配的，则原路返回
	 */
	@Override
	public ExeResultVo sendReturnMessage(SocketReturnMessage returnMsg) {
		ExeResultVo resultVo = new ExeResultVo();
		resultVo.setMessage("该协议不允许下发数据");
		resultVo.setSuccess(false);
		
		logger.info("plc网关:{}->设备，消息:{},结果:{}",instanceVo.getRunId(), returnMsg, resultVo);
		
		return resultVo;
		
	}


	@Override
	public void start() {
		if(state == GatewayState.RUNNING) {
			return;
		}
		
		logger.info("EMIA-PRO网关:{}读取启动配置参数:{},支持多个设备:{}",instanceVo.getRunId(), instanceVo.getParameter(),
				instanceVo.getSuportMultiDevice());
		
		// 不支持多个设备，必须强制配置一个设备id
		if(instanceVo.getSuportMultiDevice() != null && instanceVo.getSuportMultiDevice() == false) {
			RouteService routeService = (RouteService)serviceProvider.getByName(ServiceName.Route);
			List<DeviceVo> deviceVos = routeService.findByRunId(instanceVo.getRunId());
			
			if(deviceVos.size() > 1) {
				throw new BusinessException("网关Id:" +instanceVo.getRunId() + "为EMIA-PRO网关，当前配置为只支持单个设备，但配置了多个设备。");
			}
			
			onlyOneDeviceId = deviceVos.size() > 0 ? deviceVos.get(0).getDeviceId() : null;
			
			if(StringUtils.isEmpty(onlyOneDeviceId)) {
				throw new BusinessException("runId=" + getInstanceVo().getRunId() + "未正确配置网关和设备关联");
			}
		}
		
		
		DefaultTcpIoService defaultIoService = new DefaultTcpIoService();
		SessionFactory factory = new EmiaProSessionFactory(onlyOneDeviceId);
		factory.init(serviceProvider, this);
		
		SslConfigVo sslConfigVo = null;
		
		ListenSocketParam socketParam = JSON.parseObject(getInstanceVo().getParameter(), ListenSocketParam.class);
		
		if(socketParam != null && Boolean.TRUE.equals(socketParam.getEnableSsl())) {
			
			PlatformService platformService = (PlatformService)serviceProvider.getByName(ServiceName.Platform);
			
			sslConfigVo = platformService.getSslConfig();
		}
		
		if(StringUtils.isNotEmpty(socketParam.getOtherParameter())) {
			String paramString = socketParam.getOtherParameter();
			
			OtherParameterVo otherVo = JSON.parseObject(paramString, OtherParameterVo.class);
			
			parameterVo = otherVo;
		}
		
		EmiaProChannelInitializer channelInitializer =
				new EmiaProChannelInitializer(factory, getInstanceVo().getByteOrder(), sslConfigVo);
		
		defaultIoService.init(instanceVo, factory, channelInitializer);
		
		ioService = defaultIoService;
		sessionFactory = factory;
		
		boolean open = ioService.open();
		
		if(open) {
			state = GatewayState.RUNNING;
			logger.info("plc网关:{}启动成功 参数:{}", instanceVo.getRunId(), getInstanceVo().getParameter());
			
			shouldRun = new AtomicBoolean(true);
			
			Integer keepAlive = socketParam.getKeepAlive();

			AliveCheckTask task = new AliveCheckTask(keepAlive, shouldRun, factory);
			
			Thread thread = new Thread(task, "EMIA-PRO-alive-check-thread-" + getInstanceVo().getRunId());
			thread.start();
		}else {
			logger.info("plc网关:{}启动失败 参数:{}", instanceVo.getRunId(), getInstanceVo().getParameter());
		}
	}

	@Override
	public void shutdown() {
		if(state == GatewayState.CLOSED) {
			return;
		}
		
		if(sessionFactory != null) {
        	for (IoSession ioSession : sessionFactory.getSessions()) {
        		EmiaProIoSession emiaProIoSession = (EmiaProIoSession)ioSession;
        		emiaProIoSession.close();
        	}		       	
		}
		
		boolean close = ioService.close();
		if(close == true) {
			sessionFactory = null;
			state = GatewayState.CLOSED;	
			logger.info("plc网关:{}关闭成功", instanceVo.getRunId());
		}else {
			logger.info("plc网关:{}关闭失败", instanceVo.getRunId());
		}
	}
	
	@Override
    public Set<String> getActiveDeviceIds(){
		if(onlyOneDeviceId != null) {
			Set<String> set = new HashSet<String>();
			set.add(onlyOneDeviceId);
			return set;
		}else {
			return sessionFactory.getActiveDeviceIds();		
		}
	}
}
