package com.yonge.netty.server.handler;

import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.yonge.toolset.utils.json.JsonUtil;

@Component
public class NettyChannelManager {

	private static final Logger LOGGER = LoggerFactory.getLogger(NettyChannelManager.class);

	/**
	 * {@link Channel#attr(AttributeKey)} 属性中，表示 Channel 对应的用户
	 */
	private static final AttributeKey<String> CHANNEL_ATTR_KEY_USER = AttributeKey.newInstance("user");

	/**
	 * Channel 映射
	 */
	private ConcurrentMap<ChannelId, Channel> channels = new ConcurrentHashMap<ChannelId, Channel>();

	/**
	 * 用户与 Channel 的映射。
	 *
	 * 通过它，可以获取用户对应的 Channel。这样，我们可以向指定用户发送消息。
	 */
	private ConcurrentMap<String, Channel> userChannels = new ConcurrentHashMap<String, Channel>();

	/**
	* 添加 Channel 到 {@link #channels} 中
	*
	* @param channel Channel
	*/
	public void add(Channel channel) {
		channels.put(channel.id(), channel);
	}

	/**
	 * 添加指定用户到 {@link #userChannels} 中
	 *
	 * @param channel Channel
	 * @param user 用户
	 */
	public void addUser(Channel channel, String user) {
		Channel existChannel = channels.get(channel.id());
		if (existChannel == null) {
			LOGGER.error("[addUser][连接({}) 不存在]", channel.id());
			return;
		}
		// 设置属性
		channel.attr(CHANNEL_ATTR_KEY_USER).set(user);
		// 添加到 userChannels
		userChannels.put(user, channel);

		LOGGER.info("[add][用户({})加入，总数({})]", user, channels.size());
	}

	/**
	 * 从channel中获取user
	 * @param channel
	 * @return
	 */
	public String getUser(Channel channel) {
		if (channel.hasAttr(CHANNEL_ATTR_KEY_USER)) {
			return channel.attr(CHANNEL_ATTR_KEY_USER).get();
		}
		return null;
	}

	/**
	 * 将 Channel 从 {@link #channels} 和 {@link #userChannels} 中移除
	 *
	 * @param channel Channel
	 */
	public void remove(Channel channel) {
		// 移除 channels
		channels.remove(channel.id());

		String user = "";
		// 移除 userChannels
		if (channel.hasAttr(CHANNEL_ATTR_KEY_USER)) {
			user = channel.attr(CHANNEL_ATTR_KEY_USER).get();
			userChannels.remove(user);
		}
		LOGGER.info("[remove][用户({})移除，总数({})]", user, channels.size());
	}

	/**
	 * 向指定用户发送消息
	 *
	 * @param user 用户
	 * @param message 消息体
	 */
	public void sendTextMessage(String user, Object message) {
		// 获得用户对应的 Channel
		Channel channel = userChannels.get(user);
		if (channel == null) {
			LOGGER.error("[send][连接不存在]");
			return;
		}
		if (!channel.isActive()) {
			LOGGER.error("[send][连接({})未激活]", channel.id());
			return;
		}
		// 发送消息
		channel.writeAndFlush(new TextWebSocketFrame(JsonUtil.toJSONString(message)));
	}

	/**
	 * 向所有用户发送消息
	 *
	 * @param message 消息体
	 */
	public void sendAll(Object message) {
		for (Channel channel : channels.values()) {
			if (!channel.isActive()) {
				LOGGER.error("[send][连接({})未激活]", channel.id());
				return;
			}
			// 发送消息
			channel.writeAndFlush(message);
		}
	}

}
