import cv2
import numpy as np

# return -1 if user press 'q'. return 1 if user press 'Enter'.
def display_image(window_title, image):
    cv2.imshow(window_title, image)
    while True:
        click = cv2.getWindowProperty(window_title, cv2.WND_PROP_AUTOSIZE)
        if click < 0:
            return -1

        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            return -1

        # 'Enter' key is detected!
        if key == 13:
            return 1


class PointSelector(object):

    """
    ---------------------------------------------------
    | A simple gui point selector.                    |
    | Usage:                                          |
    |                                                 |
    | 1. call the `loop` method to show the image.    |
    | 2. click on the image to select key points,     |
    |    press `d` to delete the last points.         |
    | 3. press `q` to quit, press `Enter` to confirm. |
    ---------------------------------------------------
    """

    POINT_COLOR = (0, 0, 255)
    FILL_COLOR = (0, 255, 255)

    def __init__(self, image, title="PointSelector"):
        self.image = image
        self.title = title
        self.keypoints = []

    def draw_image(self):
        """
        Display the selected keypoints and draw the convex hull.
        """
        # the trick: draw on another new image
        new_image = self.image.copy()

        # draw the selected keypoints
        for i, pt in enumerate(self.keypoints):
            cv2.circle(new_image, pt, 6, self.POINT_COLOR, -1)
            cv2.putText(new_image, str(i), (pt[0], pt[1] - 15),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.6, self.POINT_COLOR, 2)

        # draw a line if there are two points
        if len(self.keypoints) == 2:
            p1, p2 = self.keypoints
            cv2.line(new_image, p1, p2, self.POINT_COLOR, 2)

        # draw the convex hull if there are more than two points
        if len(self.keypoints) > 2:
            mask = self.create_mask_from_pixels(self.keypoints,
                                                self.image.shape)
            new_image = self.draw_mask_on_image(new_image, mask)

        cv2.imshow(self.title, new_image)

    def onclick(self, event, x, y, flags, param):
        """
        Click on a point (x, y) will add this points to the list
        and re-draw the image.
        """
        if event == cv2.EVENT_LBUTTONDOWN:
            print("click ({}, {})".format(x, y))
            self.keypoints.append((x, y))
            self.draw_image()

    def loop(self):
        """
        Press "q" will exist the gui and return False
        press "d" will delete the last selected point.
        Press "Enter" will exist the gui and return True.
        """
        cv2.namedWindow(self.title)
        cv2.setMouseCallback(self.title, self.onclick, param=())
        cv2.imshow(self.title, self.image)

        while True:
            click = cv2.getWindowProperty(self.title, cv2.WND_PROP_AUTOSIZE)
            if click < 0:
                return False

            key = cv2.waitKey(1) & 0xFF

            # press q to return False
            if key == ord("q"):
                return False

            # press d to delete the last point
            if key == ord("d"):
                if len(self.keypoints) > 0:
                    x, y = self.keypoints.pop()
                    print("Delete ({}, {})".format(x, y))
                    self.draw_image()

            # press Enter to confirm
            if key == 13:
                return True

    def create_mask_from_pixels(self, pixels, image_shape):
        """
        Create mask from the convex hull of a list of pixels.
        """
        pixels = np.int32(pixels).reshape(-1, 2)
        hull = cv2.convexHull(pixels)
        mask = np.zeros(image_shape[:2], np.int8)
        cv2.fillConvexPoly(mask, hull, 1, lineType=8, shift=0)
        mask = mask.astype(bool)
        return mask

    def draw_mask_on_image(self, image, mask):
        """
        Paint the region defined by a given mask on an image.
        """
        new_image = np.zeros_like(image)
        new_image[:, :] = self.FILL_COLOR
        mask = np.array(mask, dtype=np.uint8)
        new_mask = cv2.bitwise_and(new_image, new_image, mask=mask)
        cv2.addWeighted(image, 1.0, new_mask, 0.5, 0.0, image)
        return image
