/*
 * Copyright 2008-2009 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.
 */
package net.hasor.rsf.container;
import net.hasor.core.AppContext;
import net.hasor.core.BindInfo;
import net.hasor.core.EventContext;
import net.hasor.rsf.*;
import net.hasor.rsf.address.AddressPool;
import net.hasor.rsf.address.RouteTypeEnum;
import net.hasor.rsf.domain.ProtocolStatus;
import net.hasor.rsf.domain.RsfEvent;
import net.hasor.rsf.domain.RsfException;
import net.hasor.rsf.domain.RsfServiceType;
import net.hasor.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;

/**
 *
 * @version : 2015年12月6日
 * @author 赵永春 (zyc@hasor.net)
 */
public class RsfBeanContainer {
    protected            Logger                                               logger       = LoggerFactory.getLogger(getClass());
    private final static Supplier[]                                           EMPTY_FILTER = new Supplier[0];
    private final        ConcurrentMap<String, ServiceDefine<?>>              serviceMap   = new ConcurrentHashMap<>();
    private final        ConcurrentMap<String, ConcurrentMap<String, String>> aliasNameMap = new ConcurrentHashMap<>();
    private final        List<FilterDefine>                                   filterList   = new ArrayList<>();
    private final        Object                                               filterLock   = new Object();
    private final        ConcurrentMap<String, Supplier<RsfFilter>[]>         filterCache  = new ConcurrentHashMap<>();
    private final        AddressPool                                          addressPool;
    private              AppContext                                           appContext;

    public RsfBeanContainer(AddressPool addressPool) {
        this.addressPool = addressPool;
    }

    public AppContext getAppContext() {
        return appContext;
    }

    /**
     * 计算指定服务上配置的过滤器。{@link RsfFilter}按照配置方式分为共有和私有。
     * 共有Filter的生效范围是所有Service，私有Filter的生效范围仅Service。
     * 每一个Filter在配置的时候都需要指定ID，根据ID私有Filter可以覆盖共有Filter的配置。
     * @param serviceID 服务ID
     */
    public Supplier<RsfFilter>[] getFilterProviders(String serviceID) {
        ServiceDefine<?> info = this.serviceMap.get(serviceID);
        if (info == null) {
            return EMPTY_FILTER;
        }
        Supplier[] result = filterCache.get(serviceID);
        if (result == null) {
            List<String> cacheIds = new LinkedList<>();
            Map<String, FilterDefine> cacheFilters = new HashMap<>();
            //2.计算最终结果。
            List<FilterDefine> publicList = this.filterList;
            if (!publicList.isEmpty()) {
                for (FilterDefine filter : publicList) {
                    String filterID = filter.filterID();
                    cacheFilters.put(filterID, filter);
                    cacheIds.add(filterID);
                }
            }
            List<FilterDefine> snapshotsList = info.getFilterSnapshots();
            if (snapshotsList != null && !snapshotsList.isEmpty()) {
                for (FilterDefine filter : snapshotsList) {
                    String filterID = filter.filterID();
                    cacheFilters.put(filterID, filter);//保存或覆盖已有。
                    if (cacheIds.contains(filterID)) {
                        cacheIds.remove(filterID);//如果全局Filter已经定义了这个ID，那么从已有顺序中删除，在尾部追加私有Filter。
                    }
                    cacheIds.add(filterID);
                }
            }
            //3.产出最终结果(过滤器链前端是public，后段是private)。
            List<Supplier<RsfFilter>> filterArrays = new ArrayList<>(cacheIds.size());
            for (String filterID : cacheIds) {
                FilterDefine define = cacheFilters.get(filterID);
                filterArrays.add(define);
            }
            result = filterArrays.toArray(new Supplier[0]);
            this.filterCache.put(serviceID, result);
        }
        return result;
    }

    /**
     * 根据服务id获取服务对象。如果服务未定义或者服务未声明提供者，则返回null。
     * @param rsfBindInfo 服务ID。
     * @return 服务提供者
     */
    public <T> Supplier<T> getProvider(RsfBindInfo<T> rsfBindInfo) {
        ServiceDefine<?> info = this.serviceMap.get(rsfBindInfo.getBindID());
        if (info == null)
            return null;
        Supplier<?> target = info.getCustomerProvider();
        if (target != null) {
            return (Supplier<T>) target;
        }
        return null;
    }

    /**
     * 根据服务id获取服务元信息。
     * @param serviceID 服务ID。
     */
    public RsfBindInfo<?> getRsfBindInfo(String serviceID) {
        ServiceDefine<?> info = this.serviceMap.get(serviceID);
        if (info == null)
            return null;
        return info.getDomain();
    }

    /**
     * 根据服务id获取服务元信息。
     * @param aliasType 名字分类。
     * @param aliasName 别名。
     */
    public RsfBindInfo<?> getRsfBindInfo(String aliasType, String aliasName) {
        ConcurrentMap<String, String> aliasNameMaps = this.aliasNameMap.get(aliasType);
        if (aliasNameMaps == null) {
            return null;
        }
        String serviceID = aliasNameMaps.get(aliasName);
        if (serviceID == null) {
            return null;
        }
        return this.serviceMap.get(serviceID);
    }

    /**
     * 根据类型获取服务元信息。如果类型上配置了{@link RsfService @RsfService}注解，则使用该注解的配置信息。
     * 否则将使用RSF默认配置下的Group、Version。
     * @param serviceType 服务类型。
     */
    public <T> RsfBindInfo<T> getRsfBindInfo(Class<T> serviceType) {
        RsfSettings rsfSettings = this.addressPool.getRsfEnvironment().getSettings();
        String serviceGroup = rsfSettings.getDefaultGroup();
        String serviceName = serviceType.getName();
        String serviceVersion = rsfSettings.getDefaultVersion();
        //覆盖
        RsfService serviceInfo = serviceType.getAnnotation(RsfService.class);
        if (serviceInfo != null) {
            if (!StringUtils.isBlank(serviceInfo.group())) {
                serviceGroup = serviceInfo.group();
            }
            if (!StringUtils.isBlank(serviceInfo.name())) {
                serviceName = serviceInfo.name();
            }
            if (!StringUtils.isBlank(serviceInfo.version())) {
                serviceVersion = serviceInfo.version();
            }
        }
        return (RsfBindInfo<T>) getRsfBindInfo(serviceGroup, serviceName, serviceVersion);
    }

    /**
     * 根据服务坐标获取服务元信息。
     * @param group 组别
     * @param name 服务名
     * @param version 服务版本
     */
    public RsfBindInfo<?> getRsfBindInfo(String group, String name, String version) {
        String serviceID = "[" + group + "]" + name + "-" + version;//String.format("[%s]%s-%s", group, name, version);
        return this.getRsfBindInfo(serviceID);
    }

    /**获取所有已经注册的服务名称。*/
    public List<String> getServiceIDs() {
        return new ArrayList<>(this.serviceMap.keySet());
    }

    /**根据别名系统获取所有已经注册的服务名称。*/
    public List<String> getServiceIDs(String category) {
        ConcurrentMap<String, String> aliasNameMaps = this.aliasNameMap.get(category);
        if (aliasNameMaps == null) {
            return Collections.EMPTY_LIST;
        }
        return new ArrayList<>(aliasNameMaps.keySet());
    }

    /**获取环境对象。*/
    public RsfEnvironment getEnvironment() {
        return this.addressPool.getRsfEnvironment();
    }

    /* ----------------------------------------------------------------------------------------- */

    /**创建{@link RsfApiBinder}。*/
    public RsfPublisher createPublisher(final RsfBeanContainer container, final RsfContext rsfContext) {
        return new ContextRsfBindBuilder() {
            @Override
            protected RsfBeanContainer getContainer() {
                return container;
            }

            @Override
            protected RsfContext getRsfContext() {
                return rsfContext;
            }
        };
    }

    /**
     * 添加一个全局服务过滤器。
     * @param define 过滤器对象。
     */
    public void publishFilter(FilterDefine define) {
        String filterID = Objects.requireNonNull(define.filterID());
        synchronized (this.filterLock) {
            for (FilterDefine filter : this.filterList) {
                if (filterID.equals(filter.filterID())) {
                    throw new IllegalStateException("repeate filterID :" + filterID);
                }
            }
            this.filterList.add(define);
            this.filterCache.clear();
        }
    }

    /**
     * 发布服务
     * @param serviceDefine 服务定义。
     */
    public synchronized <T> boolean publishService(ServiceDefine<T> serviceDefine) {
        String serviceID = serviceDefine.getDomain().getBindID();
        if (this.serviceMap.containsKey(serviceID)) {
            String serviceType = this.serviceMap.get(serviceID).getDomain().getServiceType().name();
            String logMessage = "a " + serviceType + " of the same name already exists , serviceID -> " + serviceID;
            this.logger.error(logMessage);
            throw new IllegalStateException(logMessage);
        }
        this.logger.info("service to public, id= {}", serviceID);
        ServiceDefine<?> info = this.serviceMap.putIfAbsent(serviceID, serviceDefine);
        //
        EventContext eventContext = this.addressPool.getRsfEnvironment().getEventContext();
        if (RsfServiceType.Provider == serviceDefine.getServiceType()) {
            //服务提供者
            if (serviceDefine.getCustomerProvider() == null) {
                throw new RsfException(ProtocolStatus.Forbidden, "Provider Not set the implementation class.");
            }
            try {
                eventContext.fireSyncEvent(RsfEvent.Rsf_ProviderService, serviceDefine);
            } catch (Throwable e) {
                logger.error(e.getMessage(), e);
            }
        } else {
            //服务消费者
            try {
                eventContext.fireSyncEvent(RsfEvent.Rsf_ConsumerService, serviceDefine);
            } catch (Throwable e) {
                logger.error(e.getMessage(), e);
            }
        }
        // .收录别名
        Set<String> aliasTypes = serviceDefine.getAliasTypes();
        for (String aliasType : aliasTypes) {
            ConcurrentMap<String, String> aliasMap = this.aliasNameMap.get(aliasType);
            if (aliasMap == null) {
                aliasMap = new ConcurrentHashMap<>();
                this.aliasNameMap.putIfAbsent(aliasType, aliasMap);
            }
            String aliasName = serviceDefine.getAliasName(aliasType);
            if (StringUtils.isBlank(aliasName)) {
                continue;
            }
            aliasMap.putIfAbsent(aliasName, serviceID);
        }
        //
        // .追加地址
        this.addressPool.appendStaticAddress(serviceID, serviceDefine.getAddressSet());
        // .更新流控
        String flowControl = serviceDefine.getFlowControl();
        if (StringUtils.isNotBlank(flowControl)) {
            this.addressPool.updateFlowControl(serviceID, flowControl);
        }
        // .更新路由
        Map<RouteTypeEnum, String> scriptMap = serviceDefine.getRouteScript();
        if (scriptMap != null && !scriptMap.isEmpty()) {
            for (Map.Entry<RouteTypeEnum, String> routeEnt : scriptMap.entrySet()) {
                this.addressPool.updateRoute(serviceID, routeEnt.getKey(), routeEnt.getValue());
            }
        }
        //
        return true;
    }

    /**
     * 回收发布的服务
     * @param serviceID 服务定义。
     */
    public synchronized boolean recoverService(String serviceID) {
        if (this.serviceMap.containsKey(serviceID)) {
            //
            // .发布删除消息( 1.Center解除注册、2.地址本回收)
            EventContext eventContext = this.getEnvironment().getEventContext();
            RsfBindInfo<?> rsfBindInfo = this.serviceMap.get(serviceID);
            try {
                eventContext.fireSyncEvent(RsfEvent.Rsf_DeleteService, rsfBindInfo);
            } catch (Throwable e) {
                logger.error(e.getMessage(), e);
            }
            //
            // .回收服务
            this.serviceMap.remove(serviceID);
            //
            for (Map.Entry<String, ConcurrentMap<String, String>> aliasEntry : this.aliasNameMap.entrySet()) {
                ConcurrentMap<String, String> aliasSet = aliasEntry.getValue();
                ArrayList<String> toRemove = new ArrayList<>();
                for (Map.Entry<String, String> entry : aliasSet.entrySet()) {
                    if (serviceID.equals(entry.getValue())) {
                        toRemove.add(entry.getKey());
                    }
                }
                //
                for (String key : toRemove) {
                    aliasSet.remove(key);
                }
            }
            //
            return true;
        }
        return false;
    }
    //
    /* ----------------------------------------------------------------------------------------- */
    //

    /**
     * 发布的服务
     * @param appContext 用于查找服务的容器上下文。
     */
    public void lookUp(AppContext appContext) {
        this.appContext = appContext;
        List<BindInfo<FilterDefine>> filterList = appContext.findBindingRegister(FilterDefine.class);
        for (BindInfo<FilterDefine> defile : filterList) {
            FilterDefine fd = appContext.getInstance(defile);
            if (fd != null) {
                this.publishFilter(fd);
            }
        }
        List<BindInfo<ServiceDefine>> serviceList = appContext.findBindingRegister(ServiceDefine.class);
        for (BindInfo<ServiceDefine> defile : serviceList) {
            ServiceDefine sd = appContext.getInstance(defile);
            if (sd != null) {
                this.publishService(sd);
            }
        }
        //
        this.logger.info("lookUp finish.");
    }
}