package com.jstarcraft.core.codec.schema;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.jstarcraft.core.codec.annotation.ProtocolConfiguration;

/**
 * 编解码XML解析器
 * 
 * @author Birdy
 */
public class CodecXmlParser extends AbstractBeanDefinitionParser {

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

    /** 默认资源匹配符 */
    protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    /** 资源搜索分析器(负责查找ProtocolConfiguration) */
    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    /** 元数据分析器(负责获取注解) */
    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    @Override
    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {

        BeanDefinitionBuilder factory = BeanDefinitionBuilder.genericBeanDefinition(CodecDefinitionFactory.class);

        // 要创建的对象信息
        HashSet<Class<?>> protocolClasses = new HashSet<>();

        // 检查XML内容
        NodeList nodes = element.getChildNodes();
        for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) {
            Node node = nodes.item(nodeIndex);
            if (node.getNodeType() != Node.ELEMENT_NODE) {
                continue;
            }
            String name = node.getLocalName();

            if (name.equals(ElementDefinition.PACKAGE.getName())) {
                // 自动包扫描处理
                String packageName = ((Element) node).getAttribute(AttributeDefinition.NAME.getName());
                String[] names = getResources(packageName);
                for (String resource : names) {
                    try {
                        Class<?> clazz = Class.forName(resource);
                        if (!protocolClasses.add(clazz)) {
                            throw new IllegalStateException();
                        }
                    } catch (ClassNotFoundException exception) {
                        throw new IllegalStateException(exception);
                    }
                }
            }
            if (name.equals(ElementDefinition.CLASS.getName())) {
                try {
                    String className = ((Element) node).getAttribute(AttributeDefinition.NAME.getName());
                    Class<?> clazz = Class.forName(className);
                    if (!protocolClasses.add(clazz)) {
                        throw new IllegalStateException();
                    }
                } catch (Exception exception) {
                    throw new IllegalStateException(exception);
                }
            }
        }
        factory.addPropertyValue("protocolClasses", protocolClasses);
        AbstractBeanDefinition definition = factory.getBeanDefinition();
        return definition;
    }

    /**
     * 获取指定包下的静态资源对象
     * 
     * @param packageName 包名
     * @return
     * @throws IOException
     */
    private String[] getResources(String packageName) {
        try {
            // 搜索资源
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(packageName)) + "/" + DEFAULT_RESOURCE_PATTERN;
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            // 提取资源
            Set<String> names = new HashSet<String>();
            String name = ProtocolConfiguration.class.getName();
            for (Resource resource : resources) {
                if (!resource.isReadable()) {
                    continue;
                }
                // 判断是否静态资源
                MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                ClassMetadata classMetadata = metadataReader.getClassMetadata();
                AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                if (annotationMetadata.hasAnnotation(name)) {
                    names.add(classMetadata.getClassName());
                } else {
                    String[] interfaceNames = classMetadata.getInterfaceNames();
                    for (String interfaceName : interfaceNames) {
                        metadataReader = this.metadataReaderFactory.getMetadataReader(interfaceName);
                        annotationMetadata = metadataReader.getAnnotationMetadata();
                        if (annotationMetadata.hasAnnotation(name)) {
                            names.add(classMetadata.getClassName());
                            break;
                        }
                    }
                }
            }
            return names.toArray(new String[0]);
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    /**
     * 协议Schema定义的元素
     * 
     * @author Birdy
     */
    enum ElementDefinition {

        /** 根配置元素(属性id) */
        CONFIGURATION("configuration"),

        /** 包定义元素(属性name,index) */
        PACKAGE("package"),
        /** 类定义元素(属性name,index) */
        CLASS("class");

        private String name;

        private ElementDefinition(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }

    /**
     * 协议Schema定义的属性
     * 
     * @author Birdy
     */
    enum AttributeDefinition {

        // /** 标识 */
        // INDEX("index"),

        /** 名称 */
        NAME("name");

        private String name;

        private AttributeDefinition(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

    }

}