import urllib
import os
import PIL
import cv2
import numpy as np
from matplotlib import pyplot as plt
from pma_python import core


class Annotator:
    def __init__(self):
        """
        Constructor Initializes parameters and local instance variables
        """
        # Init instance variables so to know what we have got
        self.pmacoreURL = ""
        self.pmacoreUsername = ""
        self.pmacorePassword = ""
        self.targetDir = ""
        self.size = 1500.0
        self.client = None
        self.filename = ""

    def Start(self):
        """
        Starts to automatic annotate a folder with the specified settings

        returns: None
        """

        # connect to PMA.core
        core.connect(pmacoreURL=self.pmacoreURL, pmacoreUsername=self.pmacoreUsername, pmacorePassword=self.pmacorePassword)

        filename = self.filename

        # get file PMA.core
        info = core.get_slide_info(filename)

        if (info == None):
            print("Error in file " + filename)

        width = info["Width"]
        height = info["Height"]
        scale = 1.0
        if(width >= height):
            scale = self.size / width
        else:
            scale = self.size / height

        parameters = {'pathOrUid': filename, 'timeframe': 0, 'layer': 0, 'x': 0, 'y': 0, 'width': width, 'height': height, 'scale': scale}

        if not os.path.exists(self.targetDir):
            os.makedirs(self.targetDir)

        print("Fetching file: " + filename)
        fname = os.path.splitext(info["Filename"].split('/')[-1])[0] + ".jpg"
        fname = os.path.join(self.targetDir, fname)
        convertToJPG = core.get_region(filename, 0, 0, width, height, scale)
        convertToJPG.save(fname, "JPEG")

        print("Contouring file")
        plt.axis([0, width, height, 0])
        wktContours = self.Contours(fname, 1.0 / scale)

        print("Found " + str(len(wktContours)) + " annotations")

    def Contours(self, imagePath, scale):
        """
        Tries to analyze an image and find contours

        returns: the Well Known Text of the calculated geometry
        """
        img = cv2.imread(imagePath, cv2.IMREAD_COLOR)
        # convert to HSV color space
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        (h, s, v) = cv2.split(hsv)

        # blur the saturation channel
        s = cv2.GaussianBlur(s, (15, 15), 0)

        # find automatic threshold
        th, median = self.OtsuThreshold(s)

        # threshold with the automatic value
        ret, thresh = cv2.threshold(s, th, 255, cv2.THRESH_BINARY)

        # edge detect with canny
        # edges = cv2.Canny(thresh, 0.66 * median, 1.99 * median,
        # L2gradient=True)

        # find the contours
        contours0, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

        # simplify and cut out small ones
        contours = [cv2.approxPolyDP(cnt, 3, closed=True) for cnt in contours0 if cv2.arcLength(cnt, False) > 100 or cv2.contourArea(cnt, False) > 1000]

        cv2.drawContours(img, contours, -1, (255, 45, 21), 3, cv2.LINE_AA)

        contourValues = self.ContourToWKT(contours, scale, False)

        # UNCOMMENT to remove all annotations before adding the new
        core.clear_all_annotations(self.filename, None)
        # using new add_annotations method from pma-python SDK (send annotation back to PMA.core)
        i = 0
        for annotation in contourValues:
            i += 1
            core.add_annotation(self.filename, "1", "annotation" + str(i), annotation, color="#34baeb", layerID=0)

        anns = core.get_annotations(self.filename)
        
        for ann in anns:
            surface_area = core.get_annotation_surface_area(self.filename, ann["LayerID"], ann["AnnotationID"], None)
            print(f"Surface Area for annotation: {ann['AnnotationID']}")
            print(f"\t{surface_area} mm2")
        return contourValues

    def ContourToWKT(self, contours, scale, plot=False):
        """
        Converts openCV contours to WKT

        returns: the Well Known Text of the calculated geometry
        """
        wktList = []
        for index, contour in enumerate(contours):
            wkt = 'POLYGON(('
            x1 = []
            y1 = []
            for i, v in enumerate(contour):
                x1.append(v[0][0] * scale)
                y1.append(v[0][1] * scale)
                wkt += str(int(v[0][0] * scale)) + " " + str(int((v[0][1] * scale)))
                if i < len(contour) - 1:
                    wkt += ","
            wkt += "," + str(int(x1[0])) + " " + str(int(y1[0]))
            if plot:
                plt.plot(x1, y1, '-')
            wkt += '))'
            wktList.append(wkt)

        if plot:
            plt.show()

        return wktList

    def OtsuThreshold(self, img):
        """
        Tries to analyze an image and find a good threshold with Otsu's method

        returns: the threshold value, and the median value
        """

        hist = cv2.calcHist([img], [0], None, [256], [0, 256])

        sum0 = sum([t * bin for t, bin in enumerate(hist)])

        median = np.median(img)
        # print "median = " + str(median)

        sumB = 0
        wB = 0
        wF = 0

        varMax = 0
        threshold = 0
        total = img.size

        for t, bin in enumerate(hist):
            wB += bin   # Weight Background
            if wB == 0:
                continue

            wF = total - wB  # Weight Background
            if wF == 0:
                break

            sumB += t * bin

            mB = float(sumB / wB)
            mF = float((sum0 - sumB) / wF)

            # Calculate Between Class Variance
            varBetween = float(wB) * float(wF) * (mB - mF) * (mB - mF)

            if varBetween > varMax:
                varMax = varBetween
                threshold = t

        return threshold, median


def usage():
    print("AutoAnnotator.py [--link|-l SERVERURL] [--username|-u USERNAME] [--password|-p PASSWORD] [--filename|-f Path to slide] [--targetDir|-t tempDirectory]")
    print("PATH		 : A path to a remote root directory with images")
    print("SIZE		 : The size of the images to read")
    return


if __name__ == '__main__':
    import sys
    import getopt

    try:
        opts, args = getopt.getopt(sys.argv[1:], "l:u:p:t:f:", ["link", "username", "password", "targetDir", "filename"])

    except getopt.GetoptError as err:
        # print help information and exit:
        print(err)  # will print something like "option -a not recognized"
        usage()
        sys.exit(2)

    annotator = Annotator()
    for o, a in opts:
        if o == "-v":
            verbose = True
        elif o in ("-l", "--link"):
            annotator.pmacoreURL = a
        elif o in ("-u", "--username"):
            annotator.pmacoreUsername = a
        elif o in ("-p", "--password"):
            annotator.pmacorePassword = a
        elif o in ("-t", "--targetDir"):
            annotator.targetDir = a
        elif o in ("-f", "--filename"):
            annotator.filename = a
        else:
            assert False, "unhandled option"
    annotator.Start()

    print("Done")
