package net.northking.cctp.se.component;

import lombok.extern.slf4j.Slf4j;
import net.northking.cctp.element.core.*;
import net.northking.cctp.element.core.exception.KeywordNotFoundException;
import net.northking.cctp.element.core.exception.NoSuchLibraryException;
import net.northking.cctp.element.core.keyword.KeywordArgument;
import net.northking.cctp.element.core.keyword.KeywordInfo;
import net.northking.cctp.element.core.scanner.LibraryDefinition;
import net.northking.cctp.element.core.scanner.ResourceScanner;
import net.northking.cctp.element.core.support.ThreadLevelCache;
import net.northking.cctp.element.log.Slf4jLogger;
import net.northking.cctp.element.scanner.JarResourceProvider;
import net.northking.cctp.element.system.SystemProperties;
import net.northking.cctp.element.util.Assert;
import net.northking.cctp.se.config.AtuServerConfig;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ComponentManager
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
@Slf4j
@Component
public class LibraryDefinitionManager implements InitializingBean {

    private static final Map<String, LibraryDefinition> registeredLibraryMap = new ConcurrentHashMap<>(8);

    @Autowired
    private AtuServerConfig atuServerConfig;

    @Override
    public void afterPropertiesSet() throws Exception {
        String basePath = atuServerConfig.getComponentBasePath();
        if (StringUtils.isBlank(basePath)) {
            basePath = SystemUtils.getUserHome().getPath();
        }
        basePath = StringUtils.removeEnd(basePath, File.separator);
        // 组件工作时存放文件基础目录
        System.setProperty(SystemProperties.BASE_STORAGE_DIR_KEY, basePath + File.separator + "workspace");
    }

    /**
     * 重新加载组件库
     */
    public void reload(String localPath) throws Exception {
        LibraryDefinition definition = load(localPath);
        register(definition);
    }

    /**
     * 根据组件库名称获取新的组件库实例
     */
    public ComponentLibrary newInstance(String name) throws Exception {
        LibraryDefinition definition = registeredLibraryMap.get(name);
        if (definition == null) {
            throw new NoSuchLibraryException("库未加载：" + name);
        }
        return newInstance(definition);
    }

    /**
     * 获取当前加载的库版本
     */
    public Map<String, String> getLoadedLibraryJarInfo() {
        Map<String, String> loadedMap = new HashMap<>();
        for (Map.Entry<String, LibraryDefinition> entry : registeredLibraryMap.entrySet()) {
            RegisterInfo registerInfo = entry.getValue().getRegisterInfo();
            loadedMap.put(entry.getKey(), registerInfo.getVersion());
        }
        return loadedMap;
    }

    /**
     * 创建组件库实例
     */
    private ComponentLibrary newInstance(LibraryDefinition definition) throws Exception {
        ContextInitializer initializer = new ContextInitializer();
        initializer.setLibraryDefinition(definition);
        initializer.registerLogger(new Slf4jLogger());
        LibraryContext context = initializer.initialize();
        return AtuLibrary.wrapper(context.getLibrary(), context);
    }

    /**
     * 组件库注册
     */
    private static synchronized void register(LibraryDefinition definition) {
        Assert.notNull(definition);
        RegisterInfo registerInfo = definition.getRegisterInfo();
        String name = registerInfo.getName();
        LibraryDefinition old = registeredLibraryMap.put(name, definition);
        if (old == null) {
            log.info("库:[{}]注册成功，当前版本：{}", name, registerInfo.getVersion());
        } else {
            log.info("库:[{}]变更成功，版本切换：{}->{}", name, old.getRegisterInfo().getVersion(), registerInfo.getVersion());
        }
    }

    /**
     * 加载组件库jar包
     */
    private LibraryDefinition load(String localPath) throws Exception {
        Assert.notNull(localPath);
        log.debug("------ 加载组件库资源:{}开始 ------", localPath);
        JarResourceProvider provider = new JarResourceProvider();
        provider.add(localPath);
        provider.initialize();
        log.debug("------ 加载组件库资源:{}结束 ------", localPath);
        ResourceScanner scanner = new ResourceScanner();
        scanner.setResourceLoader(provider);
        return scanner.doScan();
    }


    public static class AtuLibrary implements ComponentLibrary {

        private Library source;

        private LibraryContext context;

        public AtuLibrary(Library source, LibraryContext context) {
            this.source = source;
            this.context = context;
        }

        public static ComponentLibrary wrapper(Library source, LibraryContext context) {
            return new AtuLibrary(source, context);
        }

        @Override
        public Object runKeyword(String keywordName, Map<String, Object> args) throws Exception {
            return source.runKeyword(keywordName, args);
        }

        @Override
        public Object runKeyword(String keywordName, IExecuteContext context) throws Exception {
            return source.runKeyword(keywordName, context);
        }

        @Override
        public String[] getKeywordNames() {
            return source.getKeywordNames();
        }

        @Override
        public String getKeywordDocumentation(String keywordName) throws KeywordNotFoundException {
            return source.getKeywordDocumentation(keywordName);
        }

        @Override
        public List<KeywordArgument> getKeywordArguments(String keywordName) throws Exception {
            return source.getKeywordArguments(keywordName);
        }

        /**
         * Get keyword component parameters
         *
         * @param keywordName
         * @return
         * @throws Exception
         */
        @Override
        public Map<String, KeywordArgument> getKeywordArgumentMap(String keywordName) throws Exception {
            return source.getKeywordArgumentMap(keywordName);
        }

        @Override
        public String getKeywordAlias(String keywordName) throws KeywordNotFoundException {
            return source.getKeywordAlias(keywordName);
        }

        @Override
        public boolean existKeyword(String keywordName) {
            return source.existKeyword(keywordName);
        }

        @Override
        public List<KeywordInfo> getAllKeywords() throws Exception {
            return source.getAllKeywords();
        }

        @Override
        public Iterator<KeywordInfo> iterator() {
            return source.iterator();
        }

        @Override
        public void destroy() {
            try {
                source.destroy();
            } finally {
                ThreadLevelCache.remove();
            }
        }

        @Override
        public String getName() {
            return source.getName();
        }

        @Override
        public String getChName() {
            return source.getChName();
        }

        @Override
        public String getVersion() {
            return source.getVersion();
        }

        @Override
        public Integer getType() {
            return source.getType();
        }

        @Override
        public String getIntro() {
            return source.getIntro();
        }

        @Override
        public String getChangeLog() {
            return source.getChangeLog();
        }

        @Override
        public void refreshThreadCache() {
            context.refreshThreadCache();
        }
    }

}
