# TextSegmentationUtil.java

    package com.yumu.noveltranslator.util;

    import java.util.ArrayList;
    import java.util.List;

    /**
     * 文本分段工具类
     * 将长文本拆分为符合字符限制的片段，支持按句子、段落边界分割
     */
    public class TextSegmentationUtil {

        /** 每段最大字符数（统一限制，不再按引擎区分） */
        private static final int DEFAULT_MAX_CHARS_PER_SEGMENT = 2000;

        // 句子结束标点符号（中英文）
        private static final String SENTENCE_ENDINGS = ".!?。！？…\n\r";

        /**
         * 根据指定的翻译引擎分割文本
         *
         * @param text 待分割的文本
         * @param engineName 翻译引擎名称（如 "google", "deepl", "mymemory", "baidu", "libre", "youdao"）
         * @return 分割后的文本片段列表
         */
        public static List<String> segmentByTextEngine(String text, String engineName) {
            if (text == null || text.isEmpty()) {
                return new ArrayList<>();
            }

            return segmentText(text, DEFAULT_MAX_CHARS_PER_SEGMENT);
        }

        /**
         * 按指定字符数限制分割文本
         * 优先在句子边界处分割，其次在段落边界处分割，最后按字符数硬分割
         *
         * @param text 待分割的文本
         * @param maxChars 每段最大字符数
         * @return 分割后的文本片段列表
         */
        public static List<String> segmentText(String text, int maxChars) {
            if (text == null || text.isEmpty() || maxChars <= 0) {
                return new ArrayList<>();
            }

            List<String> segments = new ArrayList<>();

            // 如果文本长度不超过限制，直接返回
            if (text.length() <= maxChars) {
                segments.add(text);
                return segments;
            }

            int startPos = 0;
            while (startPos < text.length()) {
                int endPos = Math.min(startPos + maxChars, text.length());

                // 如果到达文本末尾，添加最后一段
                if (startPos >= text.length()) {
                    break;
                }

                // 如果当前段已经很短（接近或等于最大限制），直接截取
                if (endPos - startPos < maxChars) {
                    segments.add(text.substring(startPos));
                    break;
                }

                // 尝试在句子边界处分割
                int sentenceBreak = findSentenceBreak(text, startPos, endPos);
                if (sentenceBreak > startPos) {
                    segments.add(text.substring(startPos, sentenceBreak));
                    startPos = sentenceBreak;
                    continue;
                }

                // 尝试在段落边界处分割
                int paragraphBreak = findParagraphBreak(text, startPos, endPos);
                if (paragraphBreak > startPos) {
                    segments.add(text.substring(startPos, paragraphBreak));
                    startPos = paragraphBreak;
                    continue;
                }

                // 按单词边界分割，避免在单词中间切断
                int wordBreak = findWordBreak(text, startPos, endPos);
                if (wordBreak > startPos) {
                    segments.add(text.substring(startPos, wordBreak));
                    startPos = wordBreak;
                    continue;
                }

                // 如果以上都找不到合适的分割点，则强制按字符数分割
                segments.add(text.substring(startPos, endPos));
                startPos = endPos;
            }

            return segments;
        }

        /**
         * 查找句子边界分割点
         * 优化：避免重复创建 Pattern 和 Matcher，直接遍历字符查找
         */
        private static int findSentenceBreak(String text, int startPos, int endPos) {
            // 从后往前找句子结束符，优先在最靠近 endPos 的位置分割
            for (int i = endPos - 1; i >= startPos; i--) {
                char c = text.charAt(i);
                if (isSentenceEnding(c)) {
                    // 返回句子结束符之后的位置，保留换行符在当前段末尾
                    return i + 1;
                }
            }
            return -1; // 未找到合适的分割点
        }

        /**
         * 判断字符是否为句子结束符
         */
        private static boolean isSentenceEnding(char c) {
            return SENTENCE_ENDINGS.indexOf(c) >= 0;
        }

        /**
         * 查找段落边界分割点
         */
        private static int findParagraphBreak(String text, int startPos, int endPos) {
            // 查找换行符作为段落边界
            for (int i = endPos - 1; i >= startPos; i--) {
                if (text.charAt(i) == '\n') {
                    // 返回换行符之后的位置，保留换行在当前段末尾
                    return i + 1;
                }
            }
            return -1; // 未找到合适的分割点
        }

        /**
         * 查找单词边界分割点
         */
        private static int findWordBreak(String text, int startPos, int endPos) {
            // 从后往前找空格，避免在单词中间分割
            for (int i = endPos - 1; i >= startPos; i--) {
                if (Character.isWhitespace(text.charAt(i))) {
                    return i + 1; // 返回空格后位置，这样不会把单词分开
                }
            }
            return -1; // 未找到合适的分割点
        }

        /**
         * 获取指定引擎的最大字符数
         *
         * @param engineName 翻译引擎名称
         * @return 最大字符数限制
         */
        public static int getMaxCharsForEngine(String engineName) {
            return DEFAULT_MAX_CHARS_PER_SEGMENT;
        }

        /**
         * 计算文本的分段数量（预估）
         *
         * @param text 文本内容
         * @param engineName 翻译引擎名称
         * @return 预计分段数量
         */
        public static int estimateSegmentsCount(String text, String engineName) {
            if (text == null || text.isEmpty()) {
                return 0;
            }

            int maxChars = getMaxCharsForEngine(engineName);
            if (maxChars <= 0) {
                return 1; // 如果没有限制，当作一段
            }

            int baseCount = (int) Math.ceil((double) text.length() / maxChars);

            // 考虑智能分割可能减少段落数量
            List<String> segments = segmentByTextEngine(text, engineName);
            return Math.min(baseCount, segments.size());
        }
    }