package com.ssyf.common.core.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;

import java.util.Arrays;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串工具
 *
 * @author: huwei
 * @date: 2019/9/11 17:43
 * @version: 1.3.0
 */
public class StringUtil {
    /**
     * 将给定对象的字符串表现形式依据传入顺序串联到一起
     *
     * @param objects 串联对象，若对象不是String类型，则取对象的String.valueOf(obj)字符串
     * @return
     */
    public static String concat(Object... objects) {
        return concatMark(null, objects);
    }

    /**
     * 将给定对象的字符串表现形式依据传入顺序串联到一起
     *
     * @param joinMark 连接符
     * @param objects  串联对象，若对象不是String类型，则取对象的String.valueOf(obj)字符串
     * @return
     */
    public static String concatMark(String joinMark, Object... objects) {

        String innerJoinMark = StrUtil.isBlank(joinMark) ? "" : joinMark;
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < objects.length; ++i) {
            stringBuffer.append(objects[i]).append(innerJoinMark);
        }
        //删除最后一个连接符
        stringBuffer.delete(stringBuffer.length() - innerJoinMark.length(), stringBuffer.length());

        return stringBuffer.toString();
    }

    /**
     * 将指定字符串指定下标的字符，转换为大写
     *
     * @param str   源字符串
     * @param index 指定下标，该下标必须在str的长度范围内才有效；
     * @return
     */
    public static String toUpperCase(String str, int index) {
        return toUpperCase(str, index, 1);
    }

    /**
     * 将指定字符串指定下标的字符，转换为大写
     *
     * @param str    源字符串
     * @param index  指定下标，该下标必须在str的长度范围内才有效；
     * @param length 转换个数，从指定下标开始，超出str长度，以str长度为准；
     * @return
     */
    public static String toUpperCase(String str, int index, int length) {
        if (StrUtil.isBlank(str) || index < 0 || index >= str.length()) {
            return str;
        }
        int end = length + index > str.length() || length < 1 ? str.length() : length + index;
        String target = str.substring(index, end).toUpperCase();
        return new StringBuffer(str).delete(index, end).insert(index, target).toString();
    }

    /**
     * 删除指定字符串指定的前缀
     *
     * @param str    源字符串
     * @param prefix 前缀
     * @return
     */
    public static String removePrefix(String str, String prefix) {
        if (StrUtil.isBlank(str) || StrUtil.isBlank(prefix)) {
            return str;
        }
        StringBuffer stringBuffer = new StringBuffer(str);
        while (stringBuffer.indexOf(prefix) == 0) {
            stringBuffer.delete(0, prefix.length());
        }
        return stringBuffer.toString();
    }

    /**
     * 删除指定的后缀
     *
     * @param str    源字符串
     * @param suffix 后缀
     * @return
     */
    public static String removeSuffix(String str, String suffix) {
        if (StrUtil.isBlank(str) || StrUtil.isBlank(suffix)) {
            return str;
        }
        StringBuffer stringBuffer = new StringBuffer(str);
        while (stringBuffer.lastIndexOf(suffix) == stringBuffer.length() - suffix.length()) {
            stringBuffer.delete(stringBuffer.length() - suffix.length(), stringBuffer.length());
        }
        return stringBuffer.toString();
    }

    /**
     * 添加前缀
     *
     * @param str       源字符串
     * @param prefix    前缀
     * @param allLength 添加后总体长度，添加后的字符总体长度总是小于等于allLength ，出现这样的原因是：prefix 与str不能取整；
     * @return
     */
    public static String addPrefix(String str, String prefix, int allLength) {
        StringBuffer stringBuffer = new StringBuffer();
        while (stringBuffer.length() + prefix.length() + str.length() <= allLength) {
            stringBuffer.append(prefix);
        }
        return stringBuffer.append(str).toString();
    }

    /**
     * 添加后缀
     *
     * @param str       源字符串
     * @param suffix    后缀
     * @param allLength 添加后总体长度，添加后的字符总体长度总是小于等于allLength ，出现这样的原因是：suffix 与str不能取整；
     * @return
     */
    public static String addSuffix(String str, String suffix, int allLength) {
        StringBuffer stringBuffer = new StringBuffer(str);
        while (stringBuffer.length() + suffix.length() <= allLength) {
            stringBuffer.append(suffix);
        }
        return stringBuffer.toString();
    }

    /**
     * 驼峰命令字符转指定字符链接
     *
     * @param src      源数据
     * @param linkChar 链接字符串
     * @return
     */
    public static String camelCaseToCharLink(String src, String linkChar) {
        StringBuffer sb = new StringBuffer();
        boolean upperCase = false;
        for (int i = 0; i < src.length(); i++) {
            char c = src.charAt(i);
            boolean nextUpperCase = true;
            if (i < (src.length() - 1)) {
                nextUpperCase = Character.isUpperCase(src.charAt(i + 1));
            }
            if ((i >= 0) && Character.isUpperCase(c)) {
                if (!upperCase || !nextUpperCase) {
                    if (i > 0) {
                        sb.append(linkChar);
                    }
                }
                upperCase = true;
            } else {
                upperCase = false;
            }
            sb.append(Character.toLowerCase(c));
        }
        return sb.toString();
    }

    /**
     * 将指定字符串转换为驼峰命名规则
     *
     * @param src      源数据
     * @param linkChar 链接字符
     * @return
     */
    public static String toCamelCase(String src, char linkChar) {
        StringBuffer sb = new StringBuffer();
        boolean upperCase = false;
        for (int i = 0; i < src.length(); i++) {
            char c = src.charAt(i);
            if (c == linkChar) {
                upperCase = true;
            } else if (upperCase) {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 替换占位符，默认占位符格式${key} ,可以使用英文字符点“.”实现无限层级的值获取，示例：${user.header.eye}
     *
     * @param str 原始字符串
     * @param evn 参数环境
     * @return
     */
    public static String replacePlaceholder(String str, Object evn) {
        return replacePlaceholder(str, BeanUtil.beanToMap(evn));
    }

    /**
     * 替换占位符 ,可以使用英文字符点“.”实现无限层级的值获取，示例：${user.header.eye}
     *
     * @param str               原始字符串
     * @param evn               参数环境
     * @param placeholderPrefix 占位符前缀
     * @param placeholderSuffix 占位符后缀
     * @return
     */
    public static String replacePlaceholder(String str, Object evn, Character[] placeholderPrefix, Character[] placeholderSuffix) {
        return replacePlaceholder(str,BeanUtil.beanToMap(evn), placeholderPrefix, placeholderSuffix);
    }

    /**
     * 替换占位符，默认占位符格式${key} ,可以使用英文字符点“.”实现无限层级的值获取，示例：${user.header.eye}
     *
     * @param str 原始字符串
     * @param evn 参数环境
     * @return
     */
    public static String replacePlaceholder(String str, Map<String, Object> evn) {
        return replacePlaceholder(str, evn, new Character[]{'$', '{'}, new Character[]{'}'});
    }

    /**
     * 替换占位符 ,可以使用英文字符点“.”实现无限层级的值获取，示例：${user.header.eye}
     *
     * @param str               原始字符串
     * @param evn               参数环境
     * @param placeholderPrefix 占位符前缀
     * @param placeholderSuffix 占位符后缀
     * @return
     */
    public static String replacePlaceholder(String str, Map<String, Object> evn, Character[] placeholderPrefix, Character[] placeholderSuffix) {
        StringBuffer placeholderPatternBuf = new StringBuffer();
        Arrays.stream(placeholderPrefix).forEach(prefix -> {
            placeholderPatternBuf.append("\\").append(prefix);
        });
        placeholderPatternBuf.append("(.*?)");
        Arrays.stream(placeholderSuffix).forEach(suffix -> {
            placeholderPatternBuf.append("\\").append(suffix);
        });
        Pattern pattern = Pattern.compile(placeholderPatternBuf.toString());
        String targetStr = str;
        Matcher matcher = pattern.matcher(targetStr);
        while (matcher.find()) {
            String placeholder = matcher.group();
            String placeholderKey = placeholder.substring(placeholderPrefix.length, placeholder.length() - placeholderSuffix.length).trim();
            String[] placeholderKeys = placeholderKey.split("\\.");
            Map<String, Object> temp = evn;
            Object placeholderValue = null;
            for (int i = 0; i < placeholderKeys.length; ++i) {
                placeholderValue = temp == null ? null : temp.get(placeholderKeys[i]);
                if (placeholderValue == null) {
                    break;
                }
                temp = BeanUtil.beanToMap(placeholderValue);
            }
            if (placeholderValue != null) {
                targetStr = targetStr.replace(placeholder, placeholderValue.toString());
            }
        }
        return targetStr;
    }

    /**
     * 创建给定父编码的下一个孩子编码 ，父编码为空或空串，等同于创建起始编码
     *
     * @param parentCode 父编码
     * @param allLength  编码整体长度
     * @param nodeLength 每一个节点编码的长度
     * @return
     */
    static public String createChildCascadeCoding(String parentCode, int allLength, int nodeLength) {
        //去除补码 0
        String originalCode = removeSuffix(parentCode, "0");
        // 源码在去掉补码的时候，是否删除了源码的0位
        int deleteOriginalCodeLength = originalCode.length() % nodeLength;
        if (deleteOriginalCodeLength != 0) {
            if (parentCode.length() >= nodeLength) {
                //后补余
                originalCode = addSuffix(originalCode, "0", originalCode.length() + deleteOriginalCodeLength);
            } else {
                //前补余
                originalCode = addPrefix(originalCode, "0", originalCode.length() + deleteOriginalCodeLength);
            }
        }
        if (originalCode.length() + nodeLength > allLength) {
            throw new RuntimeException("StringUtil#createChildCascadeCoding handler ,cause: result length  " + (parentCode.length() + nodeLength) + "gt allLength " + allLength);
        }
        return addSuffix(
                new StringBuffer().append(originalCode).append(addSuffix("", "0", nodeLength - 1)).append("1").toString()
                , "0", allLength);
    }

    /**
     * 创建给定兄弟编码的下一个编码 ，兄弟编码为空或空串，等同于创建起始编码
     *
     * @param allLength  编码整体长度
     * @param nodeLength 每一个节点编码的长度
     * @return
     */
    static public String createBrotherCascadeCoding(String brotherCode, int allLength, int nodeLength) {
        //去除补码 0
        String originalCode = removeSuffix(brotherCode, "0");
        // 源码在去掉补码的时候，是否删除了源码的0位
        int deleteOriginalCodeLength = originalCode.length() % nodeLength;
        if (deleteOriginalCodeLength != 0) {
            if (brotherCode.length() >= nodeLength) {
                //后补余
                originalCode = addSuffix(originalCode, "0", originalCode.length() + deleteOriginalCodeLength);
            } else {
                //前补余
                originalCode = addPrefix(originalCode, "0", originalCode.length() + deleteOriginalCodeLength);
            }
        }
        //获取父节点code
        String parentCode;
        //兄弟节点code
        String brotherOriginalCode;
        if (originalCode.length() > nodeLength) {
            parentCode = originalCode.substring(0, originalCode.length() - nodeLength);
            brotherOriginalCode = originalCode.substring(parentCode.length());
        } else {
            parentCode = "";
            brotherOriginalCode = originalCode.length() == 0 ? "0" : originalCode;
        }

        //当前节点+1
        int currentCodeInt;
        try {
            currentCodeInt = Integer.parseInt(brotherOriginalCode) + 1;
        } catch (Exception e) {
            throw new RuntimeException("当前编码不是数字编码：" + brotherCode);
        }
        String currentOriginalCode = String.valueOf(currentCodeInt);
        if (currentOriginalCode.length() > nodeLength) {
            throw new RuntimeException("下个节点超出限定：" + currentOriginalCode + ";限制长度：" + nodeLength);
        }
        //补码
        if (currentOriginalCode.length() < nodeLength) {
            currentOriginalCode = addPrefix(currentOriginalCode, "0", nodeLength);
        }
        return addSuffix(parentCode + currentOriginalCode, "0", allLength);
    }
}
