/*
 * Copyright (C) 2020 The zfoo 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 com.zfoo.protocol.generate;

import com.zfoo.protocol.collection.CollectionUtils;
import com.zfoo.protocol.collection.tree.GeneralTree;
import com.zfoo.protocol.collection.tree.TreeNode;
import com.zfoo.protocol.registration.ProtocolRegistration;
import com.zfoo.protocol.util.StringUtils;

import java.util.*;

/**
 * EN: When generating a protocol, the final generation path of the protocol will use this class.
 * CN: 生成协议的时候，协议的最终生成路径会使用这个类
 *
 * @author godotg
 */
public abstract class GenerateProtocolPath {

    /**
     * EN: The paths and temporary variables generated by the protocol will be destroyed after startup is completed.
     * CN: 协议生成的路径，临时变量，启动完成就会销毁
     */
    private static Map<Short, String> protocolPathMap = new HashMap<>();


    public static void clear() {
        protocolPathMap.clear();
        protocolPathMap = null;
    }

    /**
     * 获取协议生成的路径
     */
    public static String protocolPathPeriod(short protocolId) {
        return protocolPathMap.get(protocolId);
    }


    public static String protocolPathSlash(short protocolId) {
        var protocolPath = protocolPathMap.get(protocolId);
        return protocolPath.replaceAll(StringUtils.PERIOD_REGEX, StringUtils.SLASH);
    }

    /**
     * 获取协议生成的首字母大写的路径
     */
    public static String capitalizeProtocolPathFold(short protocolId) {
        return StringUtils.joinWith(StringUtils.SLASH, Arrays.stream(protocolPathSlash(protocolId).split(StringUtils.SLASH)).map(it -> StringUtils.capitalize(it)).toArray());
    }

    public static String relativePath(short protocolId, short relativeProtocolId) {
        // 不是折叠协议的话，protocolPathMap一定是空，这里返回“”，上层会解析为同一个文件下
        if (CollectionUtils.isEmpty(protocolPathMap)) {
            return StringUtils.EMPTY;
        }
        var protocolPath = protocolPathMap.get(protocolId);
        var relativePath = protocolPathMap.get(relativeProtocolId);
        if (relativePath.startsWith(protocolPath)) {
            return StringUtils.format(".{}", StringUtils.substringAfterFirst(relativePath, protocolPath).replaceAll(StringUtils.PERIOD_REGEX, StringUtils.SLASH));
        }

        var splits = protocolPath.split(StringUtils.PERIOD_REGEX);
        var builder = new StringBuilder();

        for (var i = splits.length; i > 0; i--) {
            builder.append("../");
            var path = StringUtils.joinWith(StringUtils.PERIOD, Arrays.stream(splits).limit(i).toList().toArray());
            if (relativePath.startsWith(path)) {
                builder.append(StringUtils.substringAfterFirst(relativePath, path).replaceAll(StringUtils.PERIOD_REGEX, StringUtils.SLASH));
                return builder.toString();
            }
        }
        builder.append(relativePath.replaceAll(StringUtils.PERIOD_REGEX, StringUtils.SLASH));
        return builder.toString();
    }

    public static Map<String, Set<Short>> mergerProtocolPathMap() {
        var oneProtocolMap = new HashMap<String, Set<Short>>();
        for(var entry : protocolPathMap.entrySet()) {
            var protocolId = entry.getKey();
            var path = entry.getValue();
            oneProtocolMap.computeIfAbsent(path, it -> new HashSet<>()).add(protocolId);
        }
        return oneProtocolMap;
    }

    /**
     * 解析协议的路径
     *
     * @param protocolRegistrations 需要解析的路径
     */
    public static void initProtocolPath(List<ProtocolRegistration> protocolRegistrations) {
        // 将需要生成的协议的路径添加到多叉树中
        var protocolPathTree = new GeneralTree<ProtocolRegistration>();
        protocolRegistrations.forEach(it -> protocolPathTree.addNode(it.protocolConstructor().getDeclaringClass().getCanonicalName(), it));

        var rootTreeNode = protocolPathTree.getRootNode();

        if (CollectionUtils.isEmpty(rootTreeNode.getChildren())) {
            return;
        }

        var queue = new LinkedList<>(rootTreeNode.getChildren());
        while (!queue.isEmpty()) {
            var treeNode = queue.poll();
            var childChildren = treeNode.getChildren();
            // 如果子节点为空，则以当前节点为路径
            if (CollectionUtils.isEmpty(childChildren)) {
                toProtocolPath(treeNode);
                continue;
            }

            // 如果子节点的协议数据有一个不为空的，则以当前节点为路径
            if (childChildren.stream().anyMatch(it -> it.getData() != null)) {
                toProtocolPath(treeNode);
                continue;
            }

            // 继续深度便利子节点的路径
            for (var child : childChildren) {
                queue.offer(child);
            }
        }
    }

    private static void toProtocolPath(TreeNode<ProtocolRegistration> protocolTreeNode) {
        var allChildren = protocolTreeNode.flatTreeNodes()
                .stream()
                .filter(it -> it.getData() != null)
                .toList();
        var pathBefore = StringUtils.substringBeforeLast(protocolTreeNode.fullName(), StringUtils.PERIOD);
        for (var child : allChildren) {
            var protocolSimpleName = child.getData().protocolConstructor().getDeclaringClass().getSimpleName();
            var splits = Arrays.stream(StringUtils.substringBeforeLast(StringUtils.substringAfterFirst(child.fullName(), pathBefore), protocolSimpleName)
                            .split(StringUtils.PERIOD_REGEX))
                    .filter(it -> StringUtils.isNotBlank(it))
                    .toArray();
            protocolPathMap.put(child.getData().protocolId(), StringUtils.joinWith(StringUtils.PERIOD, splits));
        }
    }

}
