import math
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
import random


def distance(p1, p2):
    """Calculate Euclid distance"""
    x1, y1 = p1
    x2, y2 = p2
    dist = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

    return dist


def crop_image(image, crop_size=1280):
    """Crop center image"""
    h, w = image.shape[:2]
    x_center, y_center = w // 2, h // 2
    half_size = crop_size // 2

    xmin, ymin = x_center - half_size, y_center - half_size
    xmax, ymax = x_center + half_size, y_center + half_size

    xmin = max(xmin, 0)
    ymin = max(ymin, 0)
    xmax = min(xmax, w)
    ymax = min(ymax, h)

    return image[ymin:ymax, xmin:xmax]


def _closest_point(corners, A):
    """Find closest A in corrers point"""
    distances = [distance(A, p) for p in corners]
    return corners[np.argmin(distances)]


def _re_order_corners(image_size, corners) -> list:
    """Order by corners by clockwise angle"""
    h, w = image_size
    tl = _closest_point(corners, (0, 0))
    tr = _closest_point(corners, (w, 0))
    br = _closest_point(corners, (w, h))
    bl = _closest_point(corners, (0, h))

    return [tl, tr, br, bl]


def _validate_corner(corners, ratio_thres=0.5, epsilon=1e-3) -> bool:
    """Check corners is valid
    Invalid: 3 points, duplicate points, ....
    """
    c_tl, c_tr, c_br, c_bl = corners
    e_top = distance(c_tl, c_tr)
    e_right = distance(c_tr, c_br)
    e_bottom = distance(c_br, c_bl)
    e_left = distance(c_bl, c_tl)

    min_tb = min(e_top, e_bottom)
    max_tb = max(e_top, e_bottom)
    min_lr = min(e_left, e_right)
    max_lr = max(e_left, e_right)

    # Nếu các điểm trùng nhau thì độ dài các cạnh sẽ bằng 0
    if min(max_tb, max_lr) < epsilon:
        return False

    ratio = min(min_tb / max_tb, min_lr / max_lr)
    if ratio < ratio_thres:
        return False

    return True


def dewarp_by_polygon(
    image, corners, need_validate=False, need_reorder=True, trace_trans=None
):
    """Crop and dewarp from 4 corners of images

    Args:
        image (np.array)
        corners (list): Ex : [(3347, 512), (3379, 2427), (638, 2524), (647, 495)]
        need_validate (bool, optional): validate 4 points. Defaults to False.
        need_reorder (bool, optional): validate 4 points. Defaults to True.

    Returns:
        dewarped: image after dewarp
        corners: location of 4 corners after reorder
    """
    h, w = image.shape[:2]

    if need_reorder:
        corners = _re_order_corners((h, w), corners)

    dewarped = image

    if need_validate:
        validate = _validate_corner(corners)
    else:
        validate = True

    if validate:
        # perform dewarp
        target_w = int(
            max(distance(corners[0], corners[1]), distance(corners[2], corners[3]))
        )
        target_h = int(
            max(distance(corners[0], corners[3]), distance(corners[1], corners[2]))
        )
        target_corners = [
            [0, 0],
            [target_w, 0],
            [target_w, target_h],
            [0, target_h],
        ]

        pts1 = np.float32(corners)
        pts2 = np.float32(target_corners)
        transform_matrix = cv2.getPerspectiveTransform(pts1, pts2)

        dewarped = cv2.warpPerspective(image, transform_matrix, (target_w, target_h))
        if trace_trans is not None:
            trace_trans["dewarp_method"]["polygon"][
                "transform_matrix"
            ] = transform_matrix

    return (dewarped, corners, trace_trans)


def vis_ocr(image, boxes, txts=[], scores=None, drop_score=0.5):
    """
    Args:
        image (np.ndarray / PIL): BGR image or PIL image
        boxes (list / np.ndarray): list of polygon boxes
        txts (list): list of text labels
        scores (list, optional): probality. Defaults to None.
        drop_score (float, optional): . Defaults to 0.5.
        font_path (str, optional): Path of font. Defaults to "test/fonts/latin.ttf".
    Returns:
        np.ndarray: BGR image
    """

    if len(txts) == 0:
        txts = [""] * len(boxes)

    if isinstance(image, np.ndarray):
        image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
    if isinstance(boxes, list):
        boxes = np.array(boxes)

    h, w = image.height, image.width
    img_left = image.copy()
    img_right = Image.new("RGB", (w, h), (255, 255, 255))
    draw_left = ImageDraw.Draw(img_left)
    draw_right = ImageDraw.Draw(img_right)
    for idx, (box, txt) in enumerate(zip(boxes, txts)):
        if scores is not None and scores[idx] < drop_score:
            continue
        color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        draw_left.polygon(
            [
                box[0][0],
                box[0][1],
                box[1][0],
                box[1][1],
                box[2][0],
                box[2][1],
                box[3][0],
                box[3][1],
            ],
            fill=color,
        )
        draw_right.polygon(
            [
                box[0][0],
                box[0][1],
                box[1][0],
                box[1][1],
                box[2][0],
                box[2][1],
                box[3][0],
                box[3][1],
            ],
            outline=color,
        )
        box_height = math.sqrt(
            (box[0][0] - box[3][0]) ** 2 + (box[0][1] - box[3][1]) ** 2
        )
        box_width = math.sqrt(
            (box[0][0] - box[1][0]) ** 2 + (box[0][1] - box[1][1]) ** 2
        )
        if box_height > 2 * box_width:
            font_size = max(int(box_width * 0.9), 10)
            font = ImageFont.load_default()
            cur_y = box[0][1]
            for c in txt:
                char_size = font.getsize(c)
                draw_right.text((box[0][0] + 3, cur_y), c, fill=(0, 0, 0), font=font)
                cur_y += char_size[1]
        else:
            font_size = max(int(box_height * 0.8), 10)
            font = ImageFont.load_default()
            draw_right.text([box[0][0], box[0][1]], txt, fill=(0, 0, 0), font=font)
    img_left = Image.blend(image, img_left, 0.5)

    img_show = Image.new("RGB", (w * 2, h), (255, 255, 255))
    img_show.paste(img_left, (0, 0, w, h))
    img_show.paste(img_right, (w, 0, w * 2, h))
    img_show = cv2.cvtColor(np.array(img_show), cv2.COLOR_RGB2BGR)
    return img_show
