/*
 * (C) Copyright 2015-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *   ohun@live.cn (夜色)
 */

package com.mpush.core.router;

import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;
import com.mpush.api.connection.Connection;
import com.mpush.api.connection.SessionContext;
import com.mpush.api.event.ConnectionCloseEvent;
import com.mpush.api.event.UserOfflineEvent;
import com.mpush.api.router.RouterManager;
import com.mpush.tools.event.EventBus;
import com.mpush.tools.event.EventConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by ohun on 2015/12/23.
 *
 * @author ohun@live.cn
 */
public final class LocalRouterManager extends EventConsumer implements RouterManager<LocalRouter> {
    private static final Logger LOGGER = LoggerFactory.getLogger(LocalRouterManager.class);
    private static final Map<Integer, LocalRouter> EMPTY = new HashMap<>(0);

    /**
     * 本地路由表
     */
    private final Map<String, Map<Integer, LocalRouter>> routers = new ConcurrentHashMap<>();

    @Override
    public LocalRouter register(String userId, LocalRouter router) {
        LOGGER.info("register local router success userId={}, router={}", userId, router);
        return routers.computeIfAbsent(userId, s -> new HashMap<>(1)).put(router.getClientType(), router);
    }

    @Override
    public boolean unRegister(String userId, int clientType) {
        LocalRouter router = routers.getOrDefault(userId, EMPTY).remove(clientType);
        LOGGER.info("unRegister local router success userId={}, router={}", userId, router);
        return true;
    }

    @Override
    public Set<LocalRouter> lookupAll(String userId) {
        return new HashSet<>(routers.getOrDefault(userId, EMPTY).values());
    }

    @Override
    public LocalRouter lookup(String userId, int clientType) {
        LocalRouter router = routers.getOrDefault(userId, EMPTY).get(clientType);
        LOGGER.info("lookup local router userId={}, router={}", userId, router);
        return router;
    }

    public Map<String, Map<Integer, LocalRouter>> routers() {
        return routers;
    }

    /**
     * 监听链接关闭事件，清理失效的路由
     *
     * @param event
     */
    @Subscribe
    @AllowConcurrentEvents
    void on(ConnectionCloseEvent event) {
        Connection connection = event.connection;
        if (connection == null) return;
        SessionContext context = connection.getSessionContext();

        String userId = context.userId;
        if (userId == null) return;

        EventBus.post(new UserOfflineEvent(event.connection, userId));
        int clientType = context.getClientType();
        LocalRouter localRouter = routers.getOrDefault(userId, EMPTY).get(clientType);
        if (localRouter == null) return;

        String connId = connection.getId();
        //2.检测下，是否是同一个链接, 如果客户端重连，老的路由会被新的链接覆盖
        if (connId.equals(localRouter.getRouteValue().getId())) {
            //3.删除路由
            routers.getOrDefault(userId, EMPTY).remove(clientType);
            LOGGER.info("clean disconnected local route, userId={}, route={}", userId, localRouter);
        } else { //如果不相等，则log一下
            LOGGER.info("clean disconnected local route, not clean:userId={}, route={}", userId, localRouter);
        }
    }
}

@@ -3242,79 +3242,8 @@
 n;%0A%0A
-        EventBus.post(new UserOfflineEvent(event.connection, userId));%0A
     
@@ -3590,16 +3590,17 @@
     //3.
+ 
 %E5%88%A0%E9%99%A4%E8%B7%AF%E7%94%B1%0A   
@@ -3656,32 +3656,228 @@
 ve(clientType);%0A
+            //4. %E5%8F%91%E9%80%81%E7%94%A8%E6%88%B7%E4%B8%8B%E7%BA%BF%E4%BA%8B%E4%BB%B6, %E5%8F%AA%E6%9C%89%E8%80%81%E7%9A%84%E8%B7%AF%E7%94%B1%E5%AD%98%E5%9C%A8%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E6%89%8D%E5%8F%91%E9%80%81%EF%BC%8C%E5%9B%A0%E4%B8%BA%E6%9C%89%E5%8F%AF%E8%83%BD%E5%8F%88%E7%94%A8%E6%88%B7%E9%87%8D%E8%BF%9E%E4%BA%86%EF%BC%8C%E8%80%8C%E8%80%81%E7%9A%84%E9%93%BE%E6%8E%A5%E5%8F%88%E6%98%AF%E5%9C%A8%E6%96%B0%E8%BF%9E%E6%8E%A5%E4%B9%8B%E5%90%8E%E6%89%8D%E6%96%AD%E5%BC%80%E7%9A%84%0A            //%E8%BF%99%E4%B8%AA%E6%97%B6%E5%80%99%E5%B0%B1%E4%BC%9A%E6%9C%89%E9%97%AE%E9%A2%98%EF%BC%8C%E4%BC%9A%E5%AF%BC%E8%87%B4%E7%94%A8%E6%88%B7%E5%8F%98%E6%88%90%E4%B8%8B%E7%BA%BF%E7%8A%B6%E6%80%81%EF%BC%8C%E5%AE%9E%E9%99%85%E7%94%A8%E6%88%B7%E5%BA%94%E8%AF%A5%E6%98%AF%E5%9C%A8%E7%BA%BF%E7%9A%84%E3%80%82%0A            EventBus.post(new UserOfflineEvent(event.connection, userId));%0A
             LOGG

修复网络异常断开、重连的时候用户在线列表变成下线的bug