import json
import os
import sys
from pathlib import Path

import cv2
import numpy as np

repo = Path.home() / 'TibiaPilotNG'
sys.path.insert(0, str(repo))
os.environ['TIBIAPILOT_OUTPUT_IDX'] = '2'

from src.repositories.radar.config import floorsImgs, floorsLevelsImgs, pixelsColorsValues
from src.repositories.radar.core import getCoordinate, getFloorLevel
from src.repositories.radar.extractors import getRadarImage
from src.repositories.radar.locators import getRadarToolsPosition
from src.utils.coordinate import getCoordinateFromPixel, getPixelFromCoordinate
from src.utils.core import getScreenshot


def result(method, scale, score, position, start, shape):
    x = start[0] + position[0] + shape[1] // 2
    y = start[1] + position[1] + shape[0] // 2
    return {
        'method': method,
        'scale': scale,
        'score': round(float(score), 4),
        'coordinate': [*getCoordinateFromPixel((x, y)), floor],
    }


screenshot = getScreenshot()
tools = getRadarToolsPosition(screenshot)
detected_floor = getFloorLevel(screenshot)
left, top, width, _ = tools
floor_image = screenshot[top - 7:top + 60, left + width + 8:left + width + 10]
floor_scores = [
    float(cv2.matchTemplate(
        floor_image, template, cv2.TM_CCOEFF_NORMED,
    )[0, 0])
    for template in floorsLevelsImgs
]
floor = detected_floor if detected_floor is not None else int(np.argmax(floor_scores))
radar = getRadarImage(screenshot, tools).copy()
radar[52:58, 51:57] = 128
palette = np.array(sorted(set(pixelsColorsValues.values())), dtype=np.int16)
quantized = palette[
    np.abs(radar[:, :, None].astype(np.int16) - palette).argmin(axis=2)
].astype(np.uint8)
dominant = int(np.bincount(quantized.ravel()).argmax())
profile = json.loads((repo / 'file.json').read_text(encoding='utf-8-sig'))
waypoints = profile['_default']['1']['config']['ng_cave']['waypoints']['items']
last_coordinate = json.loads(
    (repo / '.pilotng-last-coordinate.json').read_text(encoding='utf-8')
)
route = [item['coordinate'] for item in waypoints if item['coordinate'][2] == floor]
route_pixels = [getPixelFromCoordinate(item) for item in route]
x0 = max(0, min(item[0] for item in route_pixels) - 150)
y0 = max(0, min(item[1] for item in route_pixels) - 150)
x1 = min(floorsImgs[floor].shape[1], max(item[0] for item in route_pixels) + 150)
y1 = min(floorsImgs[floor].shape[0], max(item[1] for item in route_pixels) + 150)
area = floorsImgs[floor][y0:y1, x0:x1]
results = []

for scale in (1.0, 0.5, 0.25):
    raw = radar if scale == 1 else cv2.resize(
        radar, None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST,
    )
    quant = quantized if scale == 1 else cv2.resize(
        quantized, None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST,
    )
    if area.shape[0] < raw.shape[0] or area.shape[1] < raw.shape[1]:
        continue
    for name, candidate in (('raw', raw), ('quantized', quant)):
        match = cv2.matchTemplate(area, candidate, cv2.TM_CCOEFF_NORMED)
        _, score, _, position = cv2.minMaxLoc(match)
        results.append(result(name, scale, score, position, (x0, y0), candidate.shape))
    for name, mask in (
        ('nonzero-mask', (quant > 0).astype(np.uint8) * 255),
        ('features-mask', ((quant > 0) & (quant != dominant)).astype(np.uint8) * 255),
    ):
        if not np.any(mask):
            continue
        match = cv2.matchTemplate(area, quant, cv2.TM_CCORR_NORMED, mask=mask)
        _, score, _, position = cv2.minMaxLoc(match)
        if np.isfinite(score):
            results.append(result(name, scale, score, position, (x0, y0), quant.shape))

output = {
    'floor': floor,
    'coreCoordinate': getCoordinate(
        screenshot, previousCoordinate=last_coordinate, waypoints=waypoints,
    ),
    'detectedFloor': detected_floor,
    'floorScores': [round(score, 4) for score in floor_scores],
    'tools': tools,
    'dominant': dominant,
    'results': sorted(results, key=lambda item: item['score'], reverse=True),
}
Path('C:/ProgramData/TibiaPilotNG/coordinate-match.json').write_text(
    json.dumps(output), encoding='utf-8',
)
