import json
import sys
from pathlib import Path

import cv2
import numpy as np


repo = Path.home() / 'TibiaPilotNG'
sys.path.insert(0, str(repo))

from src.repositories.radar.config import floorsImgs, pixelsColorsValues
from src.utils.coordinate import getPixelFromCoordinate


radar = cv2.imread(str(repo / '.runtime-radar.png'), cv2.IMREAD_GRAYSCALE)
floor = floorsImgs[7]
start = (32347, 32227, 7)
cx, cy = getPixelFromCoordinate(start)
palette = np.array(sorted(set(pixelsColorsValues.values())), dtype=np.int16)


def score(image, template, method, mask=None):
    match = cv2.matchTemplate(image, template, method, mask=mask)
    minimum, maximum, minimum_position, maximum_position = cv2.minMaxLoc(match)
    if method == cv2.TM_SQDIFF_NORMED:
        return float(1 - minimum), minimum_position
    return float(maximum), maximum_position


nearest_indexes = np.abs(radar[:, :, None].astype(np.int16) - palette).argmin(axis=2)
nearest = palette[nearest_indexes].astype(np.uint8)
distance = np.abs(radar.astype(np.int16) - nearest.astype(np.int16))
center_mask = np.ones(radar.shape, dtype=np.uint8) * 255
center_mask[48:62, 47:61] = 0

results = {
    'radarShape': list(radar.shape),
    'start': list(start),
    'palette': palette.tolist(),
    'distancePercentiles': {
        str(value): float(np.percentile(distance, value))
        for value in (25, 50, 75, 90, 95)
    },
    'matches': [],
}

area_padding = 80
x0 = cx - 53 - area_padding
y0 = cy - 54 - area_padding
x1 = cx + 53 + area_padding
y1 = cy + 55 + area_padding
area = floor[y0:y1, x0:x1]

for threshold in (0, 2, 4, 8, 12, 16, 24, 32):
    mask = np.where(distance <= threshold, center_mask, 0).astype(np.uint8)
    if cv2.countNonZero(mask) < 50:
        continue
    category_match = None
    for value in palette:
        image_value = np.where(area == value, 1, 0).astype(np.float32)
        template_value = np.where(
            (nearest == value) & (mask > 0), 1, 0,
        ).astype(np.float32)
        current = cv2.matchTemplate(
            image_value, template_value, cv2.TM_CCORR,
        )
        category_match = current if category_match is None else category_match + current
    _, category_score, _, category_position = cv2.minMaxLoc(
        category_match / cv2.countNonZero(mask),
    )
    results['matches'].append({
        'variant': 'category',
        'threshold': threshold,
        'maskedPixels': cv2.countNonZero(mask),
        'score': round(float(category_score), 6),
        'coordinate': [
            x0 + category_position[0] + 53 + 31744,
            y0 + category_position[1] + 54 + 30976,
            7,
        ],
    })
    for name, template in (('raw', radar), ('quantized', nearest)):
        value, position = score(area, template, cv2.TM_CCORR_NORMED, mask)
        results['matches'].append({
            'variant': name,
            'threshold': threshold,
            'maskedPixels': cv2.countNonZero(mask),
            'score': round(value, 6),
            'coordinate': [
                x0 + position[0] + 53 + 31744,
                y0 + position[1] + 54 + 30976,
                7,
            ],
        })

for name, template in (('raw', radar), ('quantized', nearest)):
    for method_name, method in (
        ('ccoeff', cv2.TM_CCOEFF_NORMED),
        ('ccorr', cv2.TM_CCORR_NORMED),
        ('sqdiff', cv2.TM_SQDIFF_NORMED),
    ):
        value, position = score(area, template, method)
        results['matches'].append({
            'variant': name,
            'method': method_name,
            'score': round(value, 6),
            'coordinate': [
                x0 + position[0] + 53 + 31744,
                y0 + position[1] + 54 + 30976,
                7,
            ],
        })

print(json.dumps(results, separators=(',', ':')))
