package com.example.myapplication.service;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.util.Log;
import com.google.mlkit.vision.common.InputImage;
import com.google.mlkit.vision.text.TextRecognition;
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions;
// 拉丁识别器（如依赖不可用，仍保留中文识别路径）
// import com.google.mlkit.vision.text.latin.TextRecognizerOptions;

import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import com.tom_roush.pdfbox.android.PDFBoxResourceLoader;
import com.tom_roush.pdfbox.pdmodel.PDDocument;
import com.tom_roush.pdfbox.text.PDFTextStripper;

public class OCRService {
    public interface Callback {
        void onSuccess(String text);
        void onFailure(Exception e);
    }

    public static void recognize(Context ctx, InputImage image, Callback cb) {
        com.google.mlkit.vision.text.TextRecognizer recognizer =
                TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build());
        recognizer.process(image)
                .addOnSuccessListener(visionText -> {
                    if (cb != null) cb.onSuccess(visionText.getText());
                })
                .addOnFailureListener(e -> {
                    if (cb != null) cb.onFailure(e);
                });
    }

    /**
     * 从文件路径提取文本的便捷方法
     * @param filePath 文件路径
     * @return 提取的文本，如果失败返回空字符串
     */
    public static String extractTextFromFile(Context ctx, String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            return "";
        }

        try {
            File file = new File(filePath);
            if (!file.exists()) {
                Log.e("OCRService", "文件不存在: " + filePath);
                return "";
            }

            // 根据文件扩展名判断处理方式
            String extension = getFileExtension(filePath);
            if (isImageFile(extension)) {
                // 首次：标准预处理
                String txt = extractTextFromImage(ctx, filePath, false);
                if (txt != null && !txt.trim().isEmpty()) return txt;
                // 兜底：高对比度预处理重试一次
                return extractTextFromImage(ctx, filePath, true);
            } else if (isPdfFile(extension)) {
                return extractTextFromPdf(ctx, filePath);
            } else {
                Log.w("OCRService", "不支持的文件类型: " + extension);
                return "";
            }
        } catch (Exception e) {
            Log.e("OCRService", "处理文件失败: " + filePath, e);
            return "";
        }
    }

    private static String extractTextFromImage(Context ctx, String imagePath, boolean highContrast) {
        try {
            Bitmap bitmap = loadAndPreprocessImage(imagePath, 2500, highContrast);
            if (bitmap == null) {
                Log.e("OCRService", "无法解码图片: " + imagePath);
                return "";
            }
            Bitmap bin = adaptiveBinarize(bitmap);
            Bitmap cropped = cropContent(bin);
            java.util.List<Bitmap> lines = segmentLines(cropped);
            StringBuilder sb = new StringBuilder();
            for (Bitmap ln : lines) {
                String t = com.example.myapplication.service.TesseractService.recognizeBitmap(ctx, ln, "chi_sim", 7, 1);
                if (t != null && !t.trim().isEmpty()) {
                    if (sb.length() > 0) sb.append('\n');
                    sb.append(t.trim());
                }
                ln.recycle();
            }
            if (sb.length() > 0) {
                if (bitmap != null && !bitmap.isRecycled()) bitmap.recycle();
                if (bin != null && !bin.isRecycled()) bin.recycle();
                if (cropped != null && !cropped.isRecycled()) cropped.recycle();
                return sb.toString();
            }

            InputImage image = InputImage.fromBitmap(bitmap, 0);
            com.google.mlkit.vision.text.TextRecognizer recognizer =
                    TextRecognition.getClient(new ChineseTextRecognizerOptions.Builder().build());
            final String[] result = {""};
            final Exception[] exception = {null};
            CountDownLatch latch = new CountDownLatch(1);
            recognizer.process(image)
                    .addOnSuccessListener(visionText -> { result[0] = visionText.getText(); latch.countDown(); })
                    .addOnFailureListener(e -> { exception[0] = e; latch.countDown(); });
            try { latch.await(5, TimeUnit.SECONDS); } catch (InterruptedException e) { Log.e("OCRService", "OCR处理被中断", e); }
            if (bitmap != null && !bitmap.isRecycled()) bitmap.recycle();
            if (bin != null && !bin.isRecycled()) bin.recycle();
            if (cropped != null && !cropped.isRecycled()) cropped.recycle();
            if (exception[0] != null) return "";
            return result[0] == null ? "" : result[0];
        } catch (Exception e) {
            Log.e("OCRService", "图片OCR处理失败: " + imagePath, e);
            return "";
        }
    }


    private static Bitmap loadAndPreprocessImage(String path, int maxDim, boolean highContrast) {
        try {
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
            Bitmap src = BitmapFactory.decodeFile(path, opts);
            if (src == null) return null;

            int w = src.getWidth();
            int h = src.getHeight();
            int max = Math.max(w, h);
            float scale = maxDim > 0 && max > maxDim ? (maxDim * 1f / max) : 1f;
            Bitmap scaled = src;
            if (scale < 1f) {
                int nw = Math.max(1, (int) (w * scale));
                int nh = Math.max(1, (int) (h * scale));
                scaled = Bitmap.createScaledBitmap(src, nw, nh, true);
                if (scaled != src) src.recycle();
            }

            if (!highContrast) return scaled;

            Bitmap out = Bitmap.createBitmap(scaled.getWidth(), scaled.getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(out);
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            ColorMatrix cm = new ColorMatrix();
            float c = 1.35f; // 对比度提升
            cm.set(new float[]{
                    c, 0, 0, 0, 0,
                    0, c, 0, 0, 0,
                    0, 0, c, 0, 0,
                    0, 0, 0, 1, 0
            });
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(scaled, 0, 0, paint);
            if (scaled != out) scaled.recycle();
            return out;
        } catch (Exception e) {
            Log.e("OCRService", "预处理失败: " + path, e);
            return BitmapFactory.decodeFile(path);
        }
    }

    private static Bitmap adaptiveBinarize(Bitmap src) {
        int w = src.getWidth();
        int h = src.getHeight();
        int[] pix = new int[w * h];
        src.getPixels(pix, 0, w, 0, 0, w, h);
        int[] gray = new int[w * h];
        for (int i = 0; i < pix.length; i++) {
            int p = pix[i];
            int r = (p >> 16) & 0xFF;
            int g = (p >> 8) & 0xFF;
            int b = p & 0xFF;
            gray[i] = (int)(0.299 * r + 0.587 * g + 0.114 * b);
        }
        int win = Math.max(15, Math.min(w, 25));
        int hw = win / 2;
        long[] sum = new long[w * h];
        for (int y = 0; y < h; y++) {
            long row = 0;
            for (int x = 0; x < w; x++) {
                row += gray[y * w + x];
                sum[y * w + x] = row + (y > 0 ? sum[(y - 1) * w + x] : 0);
            }
        }
        int[] out = new int[w * h];
        int c = 10;
        for (int y = 0; y < h; y++) {
            int y0 = Math.max(0, y - hw);
            int y1 = Math.min(h - 1, y + hw);
            for (int x = 0; x < w; x++) {
                int x0 = Math.max(0, x - hw);
                int x1 = Math.min(w - 1, x + hw);
                int area = (x1 - x0 + 1) * (y1 - y0 + 1);
                long s = sum[y1 * w + x1] - (x0 > 0 ? sum[y1 * w + (x0 - 1)] : 0) - (y0 > 0 ? sum[(y0 - 1) * w + x1] : 0) + (y0 > 0 && x0 > 0 ? sum[(y0 - 1) * w + (x0 - 1)] : 0);
                int mean = (int)(s / area);
                int g = gray[y * w + x];
                int v = g < mean - c ? 0 : 255;
                out[y * w + x] = 0xFF000000 | (v << 16) | (v << 8) | v;
            }
        }
        Bitmap bin = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        bin.setPixels(out, 0, w, 0, 0, w, h);
        return bin;
    }

    private static Bitmap cropContent(Bitmap src) {
        int w = src.getWidth();
        int h = src.getHeight();
        int[] pix = new int[w * h];
        src.getPixels(pix, 0, w, 0, 0, w, h);
        int top = 0, bottom = h - 1, left = 0, right = w - 1;
        int thr = Math.max(10, w / 50);
        for (int y = 0; y < h; y++) {
            int cnt = 0;
            for (int x = 0; x < w; x++) { if ((pix[y * w + x] & 0xFF) < 128) cnt++; }
            if (cnt > thr) { top = y; break; }
        }
        for (int y = h - 1; y >= 0; y--) {
            int cnt = 0;
            for (int x = 0; x < w; x++) { if ((pix[y * w + x] & 0xFF) < 128) cnt++; }
            if (cnt > thr) { bottom = y; break; }
        }
        for (int x = 0; x < w; x++) {
            int cnt = 0;
            for (int y = 0; y < h; y++) { if ((pix[y * w + x] & 0xFF) < 128) cnt++; }
            if (cnt > Math.max(5, h / 80)) { left = x; break; }
        }
        for (int x = w - 1; x >= 0; x--) {
            int cnt = 0;
            for (int y = 0; y < h; y++) { if ((pix[y * w + x] & 0xFF) < 128) cnt++; }
            if (cnt > Math.max(5, h / 80)) { right = x; break; }
        }
        int cw = Math.max(1, right - left + 1);
        int ch = Math.max(1, bottom - top + 1);
        Bitmap out = Bitmap.createBitmap(src, left, top, cw, ch);
        return out;
    }

    private static java.util.List<Bitmap> segmentLines(Bitmap src) {
        int w = src.getWidth();
        int h = src.getHeight();
        int[] pix = new int[w * h];
        src.getPixels(pix, 0, w, 0, 0, w, h);
        int[] proj = new int[h];
        for (int y = 0; y < h; y++) {
            int cnt = 0;
            for (int x = 0; x < w; x++) { if ((pix[y * w + x] & 0xFF) < 128) cnt++; }
            proj[y] = cnt;
        }
        java.util.List<Bitmap> lines = new java.util.ArrayList<>();
        int minRow = Math.max(8, w / 80);
        int y = 0;
        while (y < h) {
            while (y < h && proj[y] <= minRow) y++;
            if (y >= h) break;
            int start = y;
            while (y < h && proj[y] > minRow) y++;
            int end = y - 1;
            int lh = Math.max(1, end - start + 1);
            Bitmap ln = Bitmap.createBitmap(src, 0, start, w, lh);
            lines.add(ln);
        }
        return lines;
    }

    private static String extractTextFromPdf(Context ctx, String filePath) {
        try {
            if (ctx != null) {
                try { PDFBoxResourceLoader.init(ctx.getApplicationContext()); } catch (Exception ignored) {}
            }
            PDDocument doc = PDDocument.load(new File(filePath));
            try {
                PDFTextStripper stripper = new PDFTextStripper();
                try { stripper.setSortByPosition(true); } catch (Exception ignored) {}
                String text = stripper.getText(doc);
                if (text != null && !text.trim().isEmpty() && text.trim().length() >= 500) return text;
            } finally {
                doc.close();
            }
        } catch (Exception e) {
            Log.w("OCRService", "PDF文本直读失败，路径: " + filePath, e);
        }
        PdfOcrService.Config cfg = new PdfOcrService.Config();
        String ocrText = PdfOcrService.recognizePdf(ctx, filePath, cfg);
        return ocrText == null ? "" : ocrText;
    }

    private static String getFileExtension(String filePath) {
        int lastDot = filePath.lastIndexOf('.');
        if (lastDot > 0 && lastDot < filePath.length() - 1) {
            return filePath.substring(lastDot + 1).toLowerCase();
        }
        return "";
    }

    private static boolean isImageFile(String extension) {
        return extension.equals("jpg") || extension.equals("jpeg") || 
               extension.equals("png") || extension.equals("bmp") || 
               extension.equals("webp");
    }

    private static boolean isPdfFile(String extension) {
        return extension.equals("pdf");
    }
}