import cv2
import numpy as np
from pathlib import Path

"""
Hodnoty treba upravit, podla robota. Najlepsie je ich zistit podla edit_contrast.py
"""
CONTRAST_VALUE = 0.930
SATURATION_VALUE = 1.410
THRESHOLD_VALUE = 101

MARGIN_OMEGA = 0.07
MARGIN_PHI = 0.05
MARGIN_PSI = 0.02

RING_NUMBER = 5
RING_WIDTH_FRAC = 0.20

"""
RGB hodnoty pre farby kruhov, treba upravit podla robota
"""
COLOR_MARGIN = 35

COLOR_RED_BGR = (50, 60, 180)
COLOR_GREEN_BGR = (90, 120, 45)
COLOR_BLUE_BGR = (140, 80, 5)
COLOR_YELLOW_BGR = (35, 155, 185)
COLOR_BLACK_BGR = (60, 40, 50)



RED_PRICE = -1
GREEN_PRICE = 1
BLUE_PRICE = 2
YELLOW_PRICE = 0
BLACK_PRICE = -2

REF_SIZE = 64  # Velkost referencneho obrazka pre porovnanie (64x64 px)

# Mapovanie: H = Omega, S = Phi, U = Psi — overiť správnosť!
_ROOT_DIR = Path(__file__).resolve().parents[2]
_DATA_DIR = _ROOT_DIR / "ReferenceData" / "Contours"

Letter_GREEKOMEGA_Contour = np.load(_DATA_DIR / "Image_Omega_Contour.npy")
Letter_GREEKPHI_Contour = np.load(_DATA_DIR / "Image_Phi_Contour.npy")
Letter_GREEKPSI_Contour = np.load(_DATA_DIR / "Image_Psi_Contour.npy")

# ---------------------------------------------------------------------------
# Detekcia obete
# ---------------------------------------------------------------------------


def get_victim_on_wall(colour_frame, flip_mode=0):
    """
    Urci pismeno alebo hodnotu kruhu na stene pomocou kamery.
    ### Parametre:
        - colour_frame: BGR frame z kamery (numpy array)
        - flip_mode: Rezim preklopenia obrazu pred spracovanim, 0 = bez preklopenia, 1 = horizontalne, 2 = vertikalne, 3 = obe osi
    ### Vracia:
        - Pismeno ktore tam je z [Omega, Phi, Psi] alebo hodnotu kruhu - cislo, v stringu
        - Ak tam ziadne nie je vrati "None", ak error tak "Error"
    """

    def convert_BGR_to_Colors(b,g,r):
        """
        Prevod BGR hodnot na farby.
        ### Parametre:
            - b: hodnota modrej zložky
            - g: hodnota zelenej zložky
            - r: hodnota červenej zložky
        ### Vracia:
            - Farbu ako string ("Red", "Green", "Blue", "Yellow", "Black", "None")
        """
        def in_tolerance(value, target):
            """
            Skontroluje, ci je hodnota v tolerancii od targetu.
            ### Parametre:
                - value: hodnota ktoru chceme skontrolovat
                - target: hodnota ku ktorej chceme porovnat
            ### Vracia:
                - True ak je hodnota v tolerancii od cielu, inak False.
            """
            return abs(float(value) - float(target)) <= COLOR_MARGIN

        if in_tolerance(b, COLOR_RED_BGR[0]) and in_tolerance(g, COLOR_RED_BGR[1]) and in_tolerance(r, COLOR_RED_BGR[2]):
            return "Red"
        elif in_tolerance(b, COLOR_GREEN_BGR[0]) and in_tolerance(g, COLOR_GREEN_BGR[1]) and in_tolerance(r, COLOR_GREEN_BGR[2]):
            return "Green"
        elif in_tolerance(b, COLOR_BLUE_BGR[0]) and in_tolerance(g, COLOR_BLUE_BGR[1]) and in_tolerance(r, COLOR_BLUE_BGR[2]):
            return "Blue"
        elif in_tolerance(b, COLOR_YELLOW_BGR[0]) and in_tolerance(g, COLOR_YELLOW_BGR[1]) and in_tolerance(r, COLOR_YELLOW_BGR[2]):
            return "Yellow"
        elif in_tolerance(b, COLOR_BLACK_BGR[0]) and in_tolerance(g, COLOR_BLACK_BGR[1]) and in_tolerance(r, COLOR_BLACK_BGR[2]):
            return "Black"
        else:
            return "None"

    if colour_frame is None:
        return "None"

    if flip_mode == 1:
        colour_frame = cv2.flip(colour_frame, 1)
    elif flip_mode == 2:
        colour_frame = cv2.flip(colour_frame, 0)
    elif flip_mode == 3:
        colour_frame = cv2.flip(colour_frame, -1)

    hsv = cv2.cvtColor(colour_frame, cv2.COLOR_BGR2HSV)
    hsv[:, :, 1] = np.clip(hsv[:, :, 1] * SATURATION_VALUE, 0, 255)
    bgr_scaled = cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2BGR)

    contrasted = cv2.convertScaleAbs(bgr_scaled, alpha=float(CONTRAST_VALUE), beta=0)
    gray_scale_frame = cv2.cvtColor(contrasted, cv2.COLOR_BGR2GRAY)

    _, frame_thresh = cv2.threshold(gray_scale_frame, int(THRESHOLD_VALUE), 255, cv2.THRESH_BINARY_INV)

    frame_contours, _ = cv2.findContours(frame_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if not frame_contours:
        return "None"
    
    biggest_contour = max(frame_contours, key=cv2.contourArea)
    (x, y), radius = cv2.minEnclosingCircle(biggest_contour)
    center = (int(x), int(y))
    radius = int(radius)

    pixels_per_ring = radius * RING_WIDTH_FRAC

    ring_colours = []
    circle_value = 0

    for ring_index in range(RING_NUMBER):
        ring_inner_radius = ring_index * pixels_per_ring
        ring_outer_radius = (ring_index + 1) * pixels_per_ring

        mask = np.zeros(colour_frame.shape[: 2], dtype=np.uint8)

        if ring_index == 0:
            cv2.circle(mask, center, int(ring_outer_radius), 255, -1)
        else:
            cv2.circle(mask, center, int(ring_outer_radius), 255, -1)
            cv2.circle(mask, center, int(ring_inner_radius), 0, -1)

        mean_b, mean_g, mean_r, _ = cv2.mean(colour_frame, mask=mask)
        ring_colours.append(convert_BGR_to_Colors(mean_b, mean_g, mean_r))

    for i in ring_colours:
        if i == "None":
            break
        elif i == "Red":
            circle_value += RED_PRICE
        elif i == "Green":
            circle_value += GREEN_PRICE
        elif i == "Blue":
            circle_value += BLUE_PRICE
        elif i == "Yellow":
            circle_value += YELLOW_PRICE
        elif i == "Black":
            circle_value += BLACK_PRICE

    if "None" not in ring_colours:
        return str(circle_value)

    # Orezeme prahovany obrazok podla ohranicujuceho obdlznika pisma a zmenime na REF_SIZE x REF_SIZE
    fh, fw = frame_thresh.shape
    x, y, w, h = cv2.boundingRect(biggest_contour)
    pad_x = max(1, int(w * 0.1))
    pad_y = max(1, int(h * 0.1))
    x1 = max(0, x - pad_x); y1 = max(0, y - pad_y)
    x2 = min(fw, x + w + pad_x); y2 = min(fh, y + h + pad_y)
    letter_img = cv2.resize(frame_thresh[y1:y2, x1:x2], (REF_SIZE, REF_SIZE))

    omega_score = cv2.matchShapes(letter_img, Letter_GREEKOMEGA_Contour, cv2.CONTOURS_MATCH_I2, 0.0)
    phi_score = cv2.matchShapes(letter_img, Letter_GREEKPHI_Contour, cv2.CONTOURS_MATCH_I2, 0.0)
    psi_score = cv2.matchShapes(letter_img, Letter_GREEKPSI_Contour, cv2.CONTOURS_MATCH_I2, 0.0)

    scores = {"Omega": omega_score, "Phi": phi_score, "Psi": psi_score}
    margins = {"Omega": MARGIN_OMEGA, "Phi": MARGIN_PHI, "Psi": MARGIN_PSI}
    best = min(scores, key=scores.get)
    if scores[best] < margins[best]:
        return best
    return "None"
