package com.etechd.l3mon;

import android.graphics.Bitmap;
import android.graphics.Color;

/**
 * Coincidencia de plantilla por correlación normalizada sobre imágenes reducidas.
 */
public final class TemplateMatcher {
    private TemplateMatcher() {
    }

    public static float match(Bitmap screen, Bitmap template) {
        if (screen == null || template == null) {
            return 0f;
        }
        if (template.getWidth() > screen.getWidth() || template.getHeight() > screen.getHeight()) {
            return 0f;
        }

        Bitmap scaledScreen = scaleToMaxWidth(screen, 720);
        float scale = (float) scaledScreen.getWidth() / (float) screen.getWidth();
        int tplW = Math.max(8, Math.round(template.getWidth() * scale));
        int tplH = Math.max(8, Math.round(template.getHeight() * scale));
        Bitmap scaledTemplate = Bitmap.createScaledBitmap(template, tplW, tplH, true);

        float[][] screenGray = toGray(scaledScreen);
        float[][] tplGray = toGray(scaledTemplate);

        if (scaledTemplate != template) {
            scaledTemplate.recycle();
        }
        if (scaledScreen != screen) {
            scaledScreen.recycle();
        }

        int rows = screenGray.length;
        int cols = screenGray[0].length;
        int tRows = tplGray.length;
        int tCols = tplGray[0].length;
        if (rows < tRows || cols < tCols) {
            return 0f;
        }

        float best = 0f;
        int step = Math.max(1, Math.min(tCols, tRows) / 8);
        for (int y = 0; y <= rows - tRows; y += step) {
            for (int x = 0; x <= cols - tCols; x += step) {
                float score = normalizedCorrelation(screenGray, tplGray, x, y);
                if (score > best) {
                    best = score;
                    if (best >= 0.98f) {
                        return best;
                    }
                }
            }
        }
        return best;
    }

    public static boolean contains(Bitmap screen, Bitmap template, float threshold) {
        return match(screen, template) >= threshold;
    }

    /** Similitud 0–1 entre dos capturas del mismo tamaño aproximado (escala común). */
    public static float similarity(Bitmap a, Bitmap b) {
        if (a == null || b == null || a.isRecycled() || b.isRecycled()) {
            return 0f;
        }

        Bitmap sa = scaleToMaxWidth(a, 64);
        Bitmap sb = scaleToMaxWidth(b, 64);
        int w = Math.min(sa.getWidth(), sb.getWidth());
        int h = Math.min(sa.getHeight(), sb.getHeight());
        if (w < 8 || h < 8) {
            recycleIfNotOriginal(sa, a);
            recycleIfNotOriginal(sb, b);
            return 0f;
        }

        if (sa.getWidth() != w || sa.getHeight() != h) {
            Bitmap resized = Bitmap.createScaledBitmap(sa, w, h, true);
            recycleIfNotOriginal(sa, a);
            sa = resized;
        }
        if (sb.getWidth() != w || sb.getHeight() != h) {
            Bitmap resized = Bitmap.createScaledBitmap(sb, w, h, true);
            recycleIfNotOriginal(sb, b);
            sb = resized;
        }

        float[][] ga = toGray(sa);
        float[][] gb = toGray(sb);
        recycleIfNotOriginal(sa, a);
        recycleIfNotOriginal(sb, b);

        return normalizedCorrelation(ga, gb, 0, 0);
    }

    private static void recycleIfNotOriginal(Bitmap scaled, Bitmap original) {
        if (scaled != null && scaled != original && !scaled.isRecycled()) {
            scaled.recycle();
        }
    }

    private static Bitmap scaleToMaxWidth(Bitmap src, int maxWidth) {
        if (src.getWidth() <= maxWidth) {
            return src;
        }
        float ratio = (float) maxWidth / (float) src.getWidth();
        int h = Math.max(1, Math.round(src.getHeight() * ratio));
        return Bitmap.createScaledBitmap(src, maxWidth, h, true);
    }

    private static float[][] toGray(Bitmap bitmap) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        float[][] out = new float[h][w];
        int[] pixels = new int[w * h];
        bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                int c = pixels[y * w + x];
                out[y][x] = (0.299f * Color.red(c) + 0.587f * Color.green(c) + 0.114f * Color.blue(c)) / 255f;
            }
        }
        return out;
    }

    private static float normalizedCorrelation(float[][] screen, float[][] tpl, int startX, int startY) {
        int tRows = tpl.length;
        int tCols = tpl[0].length;
        float sumA = 0f;
        float sumB = 0f;
        float sumAA = 0f;
        float sumBB = 0f;
        float sumAB = 0f;
        int n = tRows * tCols;

        for (int y = 0; y < tRows; y++) {
            for (int x = 0; x < tCols; x++) {
                float a = screen[startY + y][startX + x];
                float b = tpl[y][x];
                sumA += a;
                sumB += b;
                sumAA += a * a;
                sumBB += b * b;
                sumAB += a * b;
            }
        }

        float numerator = n * sumAB - sumA * sumB;
        float denom = (float) Math.sqrt((n * sumAA - sumA * sumA) * (n * sumBB - sumB * sumB));
        if (denom <= 0.0001f) {
            return 0f;
        }
        float score = numerator / denom;
        if (score < 0f) {
            return 0f;
        }
        if (score > 1f) {
            return 1f;
        }
        return score;
    }
}
