package com.shuangyueliao.chat.queue.rabbitmq;

import com.rabbitmq.client.*;
import com.shuangyueliao.chat.entity.Account;
import com.shuangyueliao.chat.entity.UserInfo;
import com.shuangyueliao.chat.handler.UserInfoManager;
import com.shuangyueliao.chat.mapper.AccountMapper;
import com.shuangyueliao.chat.proto.ChatProto;
import com.shuangyueliao.chat.queue.OfflineInfoTransmit;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeoutException;

/**
 * @author shuangyueliao
 * @create 2019/8/27 22:53
 * @Version 0.1
 */
public class RabbitmqOfflineInfoHelper implements OfflineInfoTransmit {
    private static final String EXCHANGE_NAME = "chat_exchange_topic";
    private com.rabbitmq.client.Channel rabbitmqChannel = null;
    private ConcurrentMap<Channel, UserInfo> userInfos = UserInfoManager.userInfos;
    private ConcurrentMap<Channel, com.rabbitmq.client.Channel> consumers = new ConcurrentHashMap<>();
    @Autowired
    private AccountMapper accountMapper;

    @PostConstruct
    public void init() throws IOException {
        Connection connection = ConnectionUtil.getConnection();
        rabbitmqChannel = connection.createChannel();
        //声明交换机
        rabbitmqChannel.exchangeDeclare(EXCHANGE_NAME, "topic");
        List<Account> accounts = accountMapper.selectList(null);
        for (Account account : accounts) {
            rabbitmqChannel.queueDeclare("queue_" + account.getId(), false, false, false, null);
            rabbitmqChannel.queueBind("queue_" + account.getId(), EXCHANGE_NAME, "p2p." + account.getId());
            rabbitmqChannel.queueBind("queue_" + account.getId(), EXCHANGE_NAME, "group." + account.getGroupNumber());
        }
    }

    @Override
    public void pushP2P(Integer userId, String message) {
        try {
            rabbitmqChannel.basicPublish(EXCHANGE_NAME, "p2p." + userId, false, false, null, message.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void pushGroup(String groupNumber, String message) {
        try {
            rabbitmqChannel.basicPublish(EXCHANGE_NAME, "group." + groupNumber, false, false, null, message.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void registerPull(Channel channel) {
        UserInfo userInfo = userInfos.get(channel);
        Connection connection = ConnectionUtil.getConnection();
        try {
            com.rabbitmq.client.Channel connectionChannel = connection.createChannel();
            Consumer consumer = new DefaultConsumer(connectionChannel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
                        throws IOException {
                    super.handleDelivery(consumerTag, envelope, properties, body);
                    String s = new String(body, "UTF-8");
                    UserInfoManager.rwLock.readLock().lock();
                    try {
                        if (channel.isOpen()) {
                            channel.writeAndFlush(new TextWebSocketFrame(ChatProto.buildMessProto(userInfo.getId(), userInfo.getUsername(), s)));
                            connectionChannel.basicAck(envelope.getDeliveryTag(), false);
                        } else {
                            throw new RuntimeException();
                        }
                    } finally {
                        UserInfoManager.rwLock.readLock().unlock();
                    }
                }
            };
            consumers.put(channel, connectionChannel);
            connectionChannel.basicConsume("queue_" + userInfo.getId(), false, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void unregisterPull(Channel channel) {
        com.rabbitmq.client.Channel remove = consumers.remove(channel);
        try {
            remove.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }
}