"""Sample on the diffractometer."""

import logging

import numpy as np

from . import util
from .context import TemporaryGeometry
from .engine import Parameter
from .util import Lattice
from .util import libhkl

__all__ = """
    check_lattice
    HklSample
""".split()
logger = logging.getLogger(__name__)

def check_lattice(lattice):
    """Check an Hkl.Lattice for validity

    Raises
    ------
    ValueError
    """

    # TODO: assertion is raised if alpha/beta/gamma are invalid,
    #       which is not propagated back as a Python exception but a segfault.
    a = lattice.a_get()
    b = lattice.b_get()
    c = lattice.c_get()
    alpha = lattice.alpha_get()
    beta = lattice.beta_get()
    gamma = lattice.gamma_get()

    lt = Lattice(a, b, c, alpha, beta, gamma)
    for k, v in lt._asdict().items():
        if v is None:
            raise ValueError(f'Lattice parameter "{k}" unset or invalid')

    lt = Lattice(
        a.value_get(util.units["user"]),
        b.value_get(util.units["user"]),
        c.value_get(util.units["user"]),
        alpha.value_get(util.units["user"]),
        beta.value_get(util.units["user"]),
        gamma.value_get(util.units["user"]),
    )
    logger.debug("Lattice OK: %s", lt)

class HklSample(object):
    """Represents a sample in diffractometer calculations

    Parameters
    ----------
    calc : instance of CalcRecip
        Reciprocal space calculation class
    name : str
        A user-defined name used to refer to the sample
    sample : Hkl.Sample, optional
        A Sample instance from the wrapped Hkl library. Created
        automatically if not specified.
    units : {'user', 'default'}
        Units to use
    lattice : np.ndarray, optional
        The lattice
    U : np.ndarray, optional
        The crystal orientation matrix, U
    UB : np.ndarray, optional
        The UB matrix, where U is the crystal orientation matrix and B is
        the transition matrix of a non-orthonormal (the reciprocal of the
        crystal) in an orthonormal system
    ux : np.ndarray, optional
        ux part of the U matrix
    uy : np.ndarray, optional
        uy part of the U matrix
    uz : np.ndarray, optional
        uz part of the U matrix
    reflections :
        All reflections for the current sample in the form::

            [(h, k, l), ...]

        This assumes the hkl engine is used; generally, the ordered set of
        positions for the engine in-use should be specified.

    PUBLIC API

    .. autosummary::

        ~name
        ~add_reflection
        ~remove_reflection
        ~swap_orientation_reflections
        ~clear_reflections
        ~affine
        ~compute_UB
        ~lattice
        ~reciprocal
        ~reflection_measured_angles
        ~reflection_theoretical_angles
        ~reflections
        ~reflections_details
        ~U
        ~UB
        ~ux
        ~uy
        ~uz

    PRIVATE API

    .. autosummary::

        ~hkl_calc
        ~hkl_sample
        ~__repr__
        ~__str__
        ~_create_reflection
        ~_get_reflection_dict
        ~_refl_matrix
        ~_repr_info
    """

    def __init__(self, calc, sample=None, units="user", **kwargs):
        if sample is None:
            sample = libhkl.Sample.new("")

        self._calc = calc
        self._sample = sample
        self._sample_dict = calc._samples

        self._unit_name = units
        try:
            self._units = util.units[self._unit_name]
        except KeyError:
            raise ValueError(
                f"Unit name '{self._unit_name}' not found.   Allowed names: {list(util.units.keys())}"
            )

        # List of reflections used in computing the U & UB matrices.
        # If UB is computed by refinement of more than two reflections,
        # the code that sets that up will also need to manage this list.
        self._orientation_reflections = []

        for name in "lattice name U UB ux uy uz reflections".split():
            value = kwargs.pop(name, None)
            if value is not None:
                try:
                    setattr(self, name, value)
                except Exception as ex:
                    # These kwargs are funneled down to the gi wrapper
                    # and could raise just about anything. Tack on the
                    # kwarg to help debugging if necessary:
                    ex.message = "%s (attribute=%s)" % (ex, name)
                    raise

        if kwargs:
            raise ValueError("Unsupported kwargs for HklSample: %s" % tuple(kwargs.keys()))

    @property
    def hkl_calc(self):
        """
        The HklCalc instance associated with the sample
        """
        return self._calc

    @property
    def hkl_sample(self):
        """
        The HKL library sample object
        """
        return self._sample

    @property
    def name(self):
        """
        The name of the currently selected sample
        """
        return self._sample.name_get()

    @name.setter
    def name(self, new_name):
        """Replace the current sample

        Parameters
        ----------
        new_name : str
        """
        if new_name in self._sample_dict:
            raise ValueError("Sample with that name already exists")
        sample = self._sample
        old_name = sample.name_get()

        sample.name_set(new_name)

        del self._sample_dict[old_name]
        self._sample_dict[new_name] = self

    @property
    def reciprocal(self):
        """The reciprocal lattice"""
        lattice = self._sample.lattice_get()
        reciprocal = lattice.copy()
        lattice.reciprocal(reciprocal)
        return reciprocal.get(self._units)

    @property
    def lattice(self):
        """The lattice (a, b, c, alpha, beta, gamma)

        a, b, c [nm]
        alpha, beta, gamma [deg]
        """
        lattice = self._sample.lattice_get()
        lattice = lattice.get(self._units)
        return Lattice(*lattice)

    @lattice.setter
    def lattice(self, lattice):
        if not isinstance(lattice, libhkl.Lattice):
            a, b, c, alpha, beta, gamma = lattice
            alpha, beta, gamma = np.radians((alpha, beta, gamma))
            lattice = libhkl.Lattice.new(a, b, c, alpha, beta, gamma)

        check_lattice(lattice)
        self._sample.lattice_set(lattice)
        # TODO: notes mention that lattice should not change, but is it alright
        #       if init() is called again? or should reflections be cleared,
        #       etc?

    @property
    def U(self):
        """
        The crystal orientation matrix, U
        """
        return util.to_numpy(self._sample.U_get())

    @U.setter
    def U(self, new_u):
        self._orientation_reflections = []
        self._sample.U_set(util.to_hkl(new_u))

    def _get_parameter(self, param):
        return Parameter(param, units=self._unit_name)

    @property
    def ux(self):
        """
        ux part of the U matrix
        """
        return self._get_parameter(self._sample.ux_get())

    @property
    def uy(self):
        """
        uy part of the U matrix
        """
        return self._get_parameter(self._sample.uy_get())

    @property
    def uz(self):
        """
        uz part of the U matrix
        """
        return self._get_parameter(self._sample.uz_get())

    @property
    def UB(self):
        """
        The UB matrix, where U is the crystal orientation matrix and B is the
        transition matrix of a non-orthonormal (the reciprocal of the crystal)
        in an orthonormal system

        If written to, the B matrix will be kept constant:
            U * B = UB -> U = UB * B^-1
        """
        return util.to_numpy(self._sample.UB_get())

    @UB.setter
    def UB(self, new_ub):
        self._orientation_reflections = []
        self._sample.UB_set(util.to_hkl(new_ub))

    def _create_reflection(self, h, k, l, detector=None):
        """
        Create a new reflection with the current geometry/detector
        """
        if detector is None:
            detector = self._calc._detector

        return libhkl.SampleReflection.new(self._calc._geometry, detector, h, k, l)

    def compute_UB(self, r1, r2):
        """Compute the UB matrix with two reflections.

        Using the Busing and Levy method, compute the UB matrix for two sample
        reflections, r1 and r2.

        Returns the UB matrix or raises gi.repository.GLib.GError
        (a change from 0.3.15 and before).  Returns ``None`` if no error
        raised but computation was not successful.

        Parameters
        ----------
        r1 : HklReflection
            Reflection 1
        r2 : HklReflection
            Reflection 2

        Returns
        -------
        UB matrix or raises ``gi.repository.GLib.GError``
        """
        if self._sample.compute_UB_busing_levy(r1, r2):
            # TODO: this list defines the order of the orientation reflections
            self._orientation_reflections = [r1, r2]
            return self.UB

    @property
    def reflections(self):
        """
        All reflections for the current sample in the form:
            [(h, k, l), ...]
        """
        return [refl.hkl_get() for refl in self._sample.reflections_get()]

    @reflections.setter
    def reflections(self, refls):
        self.clear_reflections()
        self._orientation_reflections = []
        for refl in refls:
            self.add_reflection(*refl)

    def add_reflection(self, h, k, l, position=None, detector=None, compute_ub=False):
        """Add a reflection, optionally specifying the detector to use

        Parameters
        ----------
        h : float
            Reflection h
        k : float
            Reflection k
        l : float
            Reflection l
        detector : Hkl.Detector, optional
            The detector
        position : tuple or namedtuple, optional
            The physical motor position that this reflection corresponds to
            If not specified, the current geometry of the calculation engine is
            assumed.
        compute_ub : bool, optional
            Calculate the UB matrix with the last two reflections
        """
        calc = self._calc
        if detector is None:
            detector = calc._detector

        if compute_ub and len(self.reflections) < 1:
            raise RuntimeError("Cannot calculate the UB matrix with less than two reflections")

        if compute_ub:
            r1 = self._sample.reflections_get()[-1]

        with TemporaryGeometry(calc):
            if position is not None:
                calc.physical_positions = position
            r2 = self._sample.add_reflection(calc._geometry, detector, h, k, l)

        if compute_ub:
            self.compute_UB(r1, r2)

        return r2

    def remove_reflection(self, refl):
        """Remove a specific reflection"""
        if not isinstance(refl, libhkl.SampleReflection):
            index = self.reflections.index(refl)
            refl = self._sample.reflections_get()[index]

        return self._sample.del_reflection(refl)

    def clear_reflections(self):
        """Clear all reflections for the current sample."""
        reflections = self._sample.reflections_get()
        for refl in reflections:
            self._sample.del_reflection(refl)
        self._orientation_reflections = []

    def _refl_matrix(self, fcn):
        """Get a reflection angle matrix."""
        sample = self._sample
        refl = sample.reflections_get()
        refl_matrix = np.zeros((len(refl), len(refl)))

        for i, r1 in enumerate(refl):
            for j, r2 in enumerate(refl):
                if i != j:
                    refl_matrix[i, j] = fcn(r1, r2)

        return refl_matrix

    @property
    def reflection_measured_angles(self):
        return self._refl_matrix(self._sample.get_reflection_measured_angle)

    @property
    def reflection_theoretical_angles(self):
        return self._refl_matrix(self._sample.get_reflection_theoretical_angle)

    def affine(self):
        """
        Refine (affine) the sample lattice parameters from the list of reflections.
        """
        return self._sample.affine()

    def _repr_info(self):
        r = [
            f"name={self.name!r}",
            f"lattice={self.lattice!r}",
            f"ux={self.ux!r}",
            f"uy={self.uy!r}",
            f"uz={self.uz!r}",
            f"U={self.U!r}",
            f"UB={self.UB!r}",
            f"reflections={self.reflections!r}",
        ]

        return r

    def __repr__(self):
        return f"{self.__class__.__name__}({', '.join(self._repr_info())})"

    def __str__(self):
        info = self._repr_info()
        info.append(f"reflection_measured_angles={self.reflection_measured_angles!r}")
        info.append(f"reflection_theoretical_angles={self.reflection_theoretical_angles!r}")
        return f"{self.__class__.__name__}({', '.join(info)}))"

    def _get_reflection_dict(self, refl):
        """Return dictionary with reflection details."""
        h, k, l = refl.hkl_get()
        geom = refl.geometry_get()
        return {
            "reflection": {"h": h, "k": k, "l": l},
            "flag": refl.flag_get(),  # not used by hklpy
            "wavelength": geom.wavelength_get(1),
            "position": dict(zip(geom.axis_names_get(), geom.axis_values_get(1))),
            "orientation_reflection": refl in self._orientation_reflections,
        }

    @property
    def reflections_details(self):
        """
        Return a list with details of all reflections.

        NOTE: reflections_details() uses the canonical
        names for the real positioners.  The mapping
        to physical axis names happens in :mod`hkl.calc`.
        """
        refls = self._sample.reflections_get()
        for r in self._orientation_reflections:
            if r not in refls:
                # Edge case when orientation reflection was
                # deleted from the list in libhkl.
                refls.append(r)
        return [self._get_reflection_dict(r) for r in refls]

    def swap_orientation_reflections(self):
        """Swap the 2 [UB] reflections, re-compute & return new [UB]."""
        if len(self._orientation_reflections) != 2:
            raise ValueError(
                "Must have exactly 2 orientation reflections defined"
                " in order to make a swap and re-compute [UB]."
                f"  There are {len(self._orientation_reflections)}"
                " orientation reflection(s) defined now."
            )
        refls = self._orientation_reflections
        return self.compute_UB(*refls[::-1])


from PIL import Image, ImageOps, ImageDraw, ImageFont
import numpy as np
import math
import matplotlib.pyplot as plt
import cv2
import os

# Function to Convert Images to grayscale
def ICV_grayscale(img):

    height, width = img.shape[:2]

    imgOutput= np.zeros(shape=(height,width))
    for i in range(height):
        for j in range(width):
           imgOutput[i, j] = (int(img[i, j, 0]*0.299)+int(img[i, j, 1]*0.587)+int(img[i, j, 2]*0.114))

    #plt.imshow(imgOutput)
    #plt.show()
    return imgOutput
#ICV_grayscale("Dataset/DatasetA/car-1.JPG")

############################################################
# START Question - 1 - Transformations
############################################################

def ICV_transform_Image(fileName, conv, rotAngle, skewAngle):

    thetaShear = np.radians(skewAngle) # angle to shear
    thetaRot = np.radians(rotAngle)# angle to rotate

    cos1 = (np.cos(thetaRot))# calculating cosine for rotation
    sin1 = (np.sin(thetaRot))# calculating sine for rotation
    shearX = ((np.tan(thetaShear)))# calculating shear angle
    #shearY = (1/(np.tan(thetaShear)))

    # Rotation Matrix
    Tr = np.array([
        [cos1,sin1],
        [-sin1,cos1]])

    # Skew Matrix
    Tsk = np.array([
        [1,-shearX],
        [0,1]]).T
    Ts = np.array([
        [2,0],
        [0,2]])

    # Combined Matrix i
    Tcomb1 = Tsk@Tr

    # Combined Matrix ii
    Tcomb2 = Tr@Tsk

    if(conv == 0):
        Tf = Tsk
    if(conv == 1):
        Tf = Tr
    if(conv == 2):
        Tf = Tcomb1
    if(conv == 3):
        Tf = Tcomb2

    img = plt.imread(fileName)

    height, width = img.shape[:2]

    main_dimension = height
    if(height>=width):
        main_dimension = height
    else:
        main_dimension = width

    center_x = main_dimension
    center_y = main_dimension
    imgOutput = np.ndarray((main_dimension*2,main_dimension*2,3))

    #loop over the entire image
    for i in range(height):
        for j in range(width):
            pixelValue = img[i, j, :]

            # positioning relative to the center of the input image
            i_transfO = i-(height/2)
            j_transfO = j-(width/2)

            #transforming image coordinates
            i_transf = i_transfO*Tf[0,0] + Tf[0,1]*j_transfO
            j_transf = i_transfO*Tf[1,0] + Tf[1,1]*j_transfO

            #centering image coordinates back relative to the output image
            i_transf+= center_x
            j_transf+=center_y
            # the transformed coordinates are used for the new image
            if(0<i_transf < main_dimension*2 and 0<j_transf < main_dimension*2):
                imgOutput[int(i_transf), int(j_transf), :] = pixelValue

    plt.imshow(imgOutput.astype('uint8'))
    plt.show()

    if(conv>0):
        imgOutputInv = np.ndarray((main_dimension*2,main_dimension*2,3))
        center_x = height/2
        center_y = width/2
        TInv = np.linalg.inv(Tf)#computing the inverse matrix

        for i in range(height):
            for j in range(width):

                i_transfI = i-center_x
                j_transfI = j-center_y

                inputCoords= np.array([i_transfI, j_transfI])
                i_transfI, j_transfI = np.dot(TInv,inputCoords)

                i_transfI+= center_x
                j_transfI+=center_y
                # the inverse transformed coordinates are used for the original image this time
                if(0<i_transfI < height and 0<j_transfI < width):
                    imgOutputInv[int(i+main_dimension/2), int(j+main_dimension/2), :] = img[int(i_transfI), int(j_transfI), :]

        plt.imshow(imgOutputInv.astype('uint8'))
        plt.show()

#ICV_transform_Image("name_image.JPG", 1, -50, 60)
ICV_transform_Image("Dataset/DatasetA/car-1.JPG", 1, 20, 50)

def ICV_createTextImage():

    imgOutput = Image.new('RGB', (400, 100), color = 'blue')
    #rect = patches.Rectangle((50,100),40,30,linewidth=1,edgecolor='r',facecolor='none')
    fnt = ImageFont.truetype('arial.ttf', 72)
    draw = ImageDraw.Draw(imgOutput)
    draw.text((0,0), "SHEHRYAR", font=fnt,fill=(255,255,255))

    imgOutput.save('name_image.jpg')
    plt.imshow(imgOutput)
    plt.show()
#ICV_createTextImage()

############################################################
# END Question - 1 - Transformations
############################################################

############################################################
# START Question - 2 - Convolutions
############################################################

# gray = 1 or 0, filter = 0,1,2 for average, A and B respectively
def ICV_filter_Image(fileName, gray, filter):

    #Mean filter
    filterAvg = np.array([
        [1/9,1/9,1/9],
        [1/9,1/9,1/9],
        [1/9,1/9,1/9]])

    #Laplacian filter
    filterB = np.array([
        [0,1,0],
        [1,-4,1],
        [0,1,0]])

    # Gaussian blur
    filterA = np.array([
        [1/16,2/16,1/16],
        [2/16,4/16,2/16],
        [1/16,2/16,1/16]])

    if(filter == 0):
       Tfilter = filterAvg
    elif(filter == 1):
       Tfilter = filterA
    elif(filter == 2):
       Tfilter = filterB

    img = plt.imread(fileName)
    if(gray == 1):
        img = ICV_grayscale(img)

    height, width = img.shape[:2]

    imgOutput = np.ndarray((height,width,3))

    upper = 0
    lower = 0
    right = 0
    left = 0

    # if image is grayscale
    if(gray == 1):
        # iterating over the whole image
        for i in range(height):
            for j in range(width):

                # conditional statements to ignore the borders
                if(i-1>height):
                    upper = img[i-1, j]*(Tfilter[0,1])

                if(i+1<height):
                    lower = img[i+1, j]*(Tfilter[2,1])
                    lower_left = img[i+1, j-1]*(Tfilter[2,0])

                if(j+1<width):
                    right = img[i, j+1]*(Tfilter[1,2])
                    upper_right= img[i-1, j+1]*(Tfilter[0,2])

                if(j+1<width) and (i+1<height):
                    lower_right = img[i+1, j+1]*(Tfilter[2,2])

                left = img[i, j-1]*(Tfilter[1,0])
                upper_left = img[i-1, j-1]*(Tfilter[0,0])

                # the center value is computed by also adding the filtered values of the neighbouring pixels
                imgOutput[i, j] = (img[i, j]*(Tfilter[1,1]) + upper+lower+left+right+lower_left+lower_right+upper_left+upper_right)

                # each newly assigned pixel value is checked for an overflow of value
                for channel in range(3):
                    if(imgOutput[i, j, channel] > 255):
                        imgOutput[i, j, channel] = 255
                    if(imgOutput[i, j, channel] < 0):
                        imgOutput[i, j, channel] = 0

    # if image is not grayscale
    else:

        for i in range(height):
            for j in range(width):

                if(i-1>height):
                    upper = img[i-1, j, :]*(Tfilter[0,1])

                if(i+1<height):
                    lower = img[i+1, j, :]*(Tfilter[2,1])
                    lower_left = img[i+1, j-1, :]*(Tfilter[2,0])

                if(j+1<width):
                    right = img[i, j+1, :]*(Tfilter[1,2])
                    upper_right= img[i-1, j+1, :]*(Tfilter[0,2])

                if(j+1<width) and (i+1<height):
                    lower_right = img[i+1, j+1, :]*(Tfilter[2,2])

                left = img[i, j-1, :]*(Tfilter[1,0])
                upper_left = img[i-1, j-1, :]*(Tfilter[0,0])

                imgOutput[i, j, :] = (img[i, j, :]*(Tfilter[1,1]) + upper+lower+left+right+lower_left+lower_right+upper_left+upper_right)

                for channel in range(3):
                    if(imgOutput[i, j, channel] > 255):
                        imgOutput[i, j, channel] = 255
                    if(imgOutput[i, j, channel] < 0):
                        imgOutput[i, j, channel] = 0

    plt.axis('off')

    plt.imshow(imgOutput.astype('uint8'))
    plt.savefig("test.jpg", bbox_inches='tight')
    plt.show()

#ICV_filter_Image("test.JPG",1,2)
#ICV_filter_Image("Dataset/DatasetA/car-1.JPG",1,2)

############################################################
# END Question - 2 - Convolutions
############################################################

############################################################
# START Question - 3 - Histograms and Video Segmentation
############################################################

# intersection = 1 or 0
def ICV_histogram_Image(imagehist, intersection):

    img = plt.imread(imagehist)
    imgGray = ICV_grayscale(img)

    height, width = img.shape[:2]

    #no_pixels_channel = width*height

    #create integer arrays for 256 values
    red_hist = [0]*256
    green_hist = [0]*256
    blue_hist = [0]*256
    final_hist = [0]*256

    #iterate over the whole image for image values
    for i in range(height):
        for j in range(width):
            # use the image pixel value as the index value of each channel's histogram and increment the indexed value of that channel histogram
            final_hist[int(imgGray[i, j])] += 1

    # if intersection is not required also compute the histograms of the channels separately
    if(intersection == 0):
        #iterate over the whole image for image values
        for i in range(height):
            for j in range(width):
                # use the image pixel value as the index value of each channel's histogram and increment the indexed value of that channel histogram
                red_hist[int(img[i, j,0])] += 1
                green_hist[int(img[i, j, 1])] += 1
                blue_hist[int(img[i, j, 2])] += 1

        #graph for color channels histogram
        for i in range(0, 256):

            #normalize the histogram values
            red_hist[i]/=1
            green_hist[i]/=1
            blue_hist[i]/=1

            plt.bar(i, red_hist[i], color='red', alpha=0.6)
            plt.bar(i, green_hist[i], color='green', alpha=0.6)
            plt.bar(i, blue_hist[i], color='blue', alpha=0.8)

        plt.xlabel('Pixel Range (0-255)')
        plt.ylabel('Pixel Frequencies')
        plt.title('RGB Histogram')
        plt.show()

        # graph the grayscale histogram
        for i in range(0, 256):
            plt.bar(i, final_hist[i], color='black', alpha=0.8)

        plt.xlabel('Pixel Range (0-255)')
        plt.ylabel('Pixel Frequencies')
        plt.title('Grayscale Histogram')
        plt.show()

    return final_hist, np.sum(final_hist)

#ICV_histogram_Image("cap1.JPG",0)

# seq_intersection = 0 or 1, normalize = 0 or 1
# function takes the intersection between the histograms of two images, and returns the overlap value
def ICV_histogram_intersection(img_1, img_2, seq_intersection, normalize):

    sum_hist = 0.0

    # getting the histograms of the two frames
    hist_1, no = ICV_histogram_Image(img_1,1)
    hist_2, no_pixels = ICV_histogram_Image(img_2,1)
    inter_hist = [0]*256

    # to calculate the intersection value, take the minimum of value amongst the two histograms.
    # Also calculate the sum of the intersection values
    # If required to normalize divide by the number of pixels
    for i in range(0,256):
        inter_hist[i] = min(hist_1[i], hist_2[i])
        if(normalize == 1):
            inter_hist[i] /= no_pixels
        sum_hist += inter_hist[i]

    # check whether if it is a video sequence, plotting individual intersections would not be efficient
    if(seq_intersection == 0):
        #plotting the values of the two histograms together
        for i in range(0,256):

            plt.bar(i, hist_1[i], color='gray', alpha=0.8)
            plt.bar(i, hist_2[i], color='yellow', alpha=0.5)

        plt.xlabel('Pixel Range (0-255)')
        plt.ylabel('Pixel Frequencies')
        plt.title('Overlap of the two histograms')
        plt.show()

        #plotting the intersection values of the histograms
        for i in range(0,256):
            plt.bar(i, inter_hist[i], color='black', alpha=0.8)

        plt.xlabel('Pixel Range (0-255)')
        plt.ylabel('Pixel Frequencies')
        plt.title('Plot for intersection values')
        plt.show()

    print("Intersection Value: ", (sum_hist))
    if(normalize == 1):
        print("Normalized Intersection Value: ", (sum_hist)*100)

    return (sum_hist)
#ICV_histogram_intersection("cap0.JPG","cap1.JPG",0, 0)

# normalize = 1 and 0
def ICV_capVid(filename, normalize):
    cam = cv2.VideoCapture(filename)
    inter_vals = [0]*int(cam.get(cv2.CAP_PROP_FRAME_COUNT))
    i=0

    if(normalize ==1):
        max_val = 1.0 # for normailzed value between 0 and 1
    else:
        max_val = 80000

    while(cam.isOpened()):
        ret, frame = cam.read()
        if ret == False:
            break
        cv2.imwrite('cap'+str(i)+'.jpg',frame)

        # only if one frame has been captured, then start with taking intersections
        if(i>0):
            img_1 = 'cap'+str(i-1)+'.jpg'
            img_2 = 'cap'+str(i)+'.jpg'
            inter_vals[i] = ICV_histogram_intersection(img_1, img_2,1,normalize)
            sec = ((int(cam.get(cv2.CAP_PROP_POS_MSEC)))/1000)# convert milliseconds to seconds
            print(i, sec, img_1, img_2, inter_vals[i])
            plt.bar(sec, (max_val-inter_vals[i]), color='black', alpha=0.8)
        i+=1

    plt.xlabel('Time (seconds)')
    plt.ylabel('Intersection Values (max intersection value-intersection value)')
    plt.show()
    cam.release()
    cv2.destroyAllWindows()

#ICV_capVid("Dataset/DatasetB.AVI",1)

############################################################
# END Question - 3 - Histograms and Video Segmentation
############################################################

############### References
#https://stackabuse.com/affine-image-transformations-in-python-with-numpy-pillow-and-opencv/

############### README

############### TRANSFORMATIONS
############################################################
#ICV_transform_Image(filename, transformation, rotation angle, skew angle)
#transformations: 0=skew matrix, 1=rotation matrix, 2=rotation+skew, 3=skew+rotation

#ICV_transform_Image("Dataset/DatasetA/car-1.JPG", 3, 20, 50)

############### CONVOLUTIONS
############################################################
#ICV_filter_Image(filname,convert it to grayscale?,filter)
#filter: 0=average filter, 1=filter A, 2=filter=B
#convert it to grayscale?: 0=No, 1=Yes

#ICV_filter_Image("Dataset/DatasetA/car-1.JPG",1,2)

############### HISTORGRAMS AND VIDEO SEGMENTATION
############################################################
#ICV_histogram_Image(filename,is for intersection?)
#is for intersection?: 0=No, 1=Yes

#ICV_histogram_Image("cap1.JPG",0)

#ICV_histogram_intersection(filename1,filename2, is video sequence?, to normalize?)
#is video sequence?: 0=No, 1=Yes
#to normalize?: 0=No, 1=Yes

#ICV_histogram_intersection("cap0.JPG","cap1.JPG",0, 0)

#ICV_capVid(filename, to normalize?)
#to normalize?: 0=No, 1=Yes

#ICV_capVid(filename,1)


# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import uuid

def _format_uuid_string(string):
    return (string.replace("urn:", "")
                  .replace("uuid:", "")
                  .strip("{}")
                  .replace("-", "")
                  .lower())

def is_uuid_like(val):
    """Returns validation of a value as a UUID.

    :param val: Value to verify
    :type val: string
    :returns: bool

    .. versionchanged:: 1.1.1
       Support non-lowercase UUIDs.
    """
    try:
        return str(uuid.UUID(val)).replace("-", "") == _format_uuid_string(val)
    except (TypeError, ValueError, AttributeError):
        return False

TRUE_STRINGS = ("1", "t", "true", "on", "y", "yes")
FALSE_STRINGS = ("0", "f", "false", "off", "n", "no")

def bool_from_string(subject, strict=False, default=False):
    """Interpret a subject as a boolean.

    A subject can be a boolean, a string or an integer. Boolean type value
    will be returned directly, otherwise the subject will be converted to
    a string. A case-insensitive match is performed such that strings
    matching 't','true', 'on', 'y', 'yes', or '1' are considered True and,
    when `strict=False`, anything else returns the value specified by
    'default'.

    Useful for JSON-decoded stuff and config file parsing.

    If `strict=True`, unrecognized values, including None, will raise a
    ValueError which is useful when parsing values passed in from an API call.
    Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'.
    """
    if isinstance(subject, bool):
        return subject
    if not isinstance(subject, str):
        subject = str(subject)

    lowered = subject.strip().lower()

    if lowered in TRUE_STRINGS:
        return True
    elif lowered in FALSE_STRINGS:
        return False
    elif strict:
        acceptable = ", ".join(
            "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS))
        msg = ("Unrecognized value '%(val)s', acceptable values are:"
               " %(acceptable)s") % {"val": subject,
                                     "acceptable": acceptable}
        raise ValueError(msg)
    else:
        return default

def format_float_to_str(num):
    """Format number into human-readable float format.

     More precise it convert float into the string and remove redundant
     zeros from the floating part.
     It will format the number by the following examples:
     0.0000001 -> 0.0
     0.000000 -> 0.0
     37 -> 37.0
     1.0000001 -> 1.0
     1.0000011 -> 1.000001
     1.0000019 -> 1.000002

    :param num: Number to be formatted
    :return: string representation of the number
    """

    num_str = "%f" % num
    float_part = num_str.split(".")[1].rstrip("0") or "0"
    return num_str.split(".")[0] + "." + float_part


"""
Avaible commands: .stickertoimage
"""
from telethon import events
from telethon.errors.rpcerrorlist import YouBlockedUserError

from userbot.utils import lightning_cmd

@borg.on(lightning_cmd(pattern="stickertoimage ?(.*)"))
async def _(event):
    if event.fwd_from:
        return
    if not event.reply_to_msg_id:
        await event.edit("```Reply to any user message.```")
        return
    reply_message = await event.get_reply_message()
    if not reply_message.media:
        await event.edit("```Reply to sticker message```")
        return
    chat = "@DownloadStickersBot"
    reply_message.sender
    if reply_message.sender.bot:
        await event.edit("```Reply to actual users message.```")
        return
    await event.edit("```Making a sticker to image```")
    async with event.client.conversation(chat) as conv:
        try:
            response = conv.wait_event(
                events.NewMessage(incoming=True, from_users=658563529)
            )
            await event.client.forward_messages(chat, reply_message)
            response = await response
        except YouBlockedUserError:
            await event.reply("```Please unblock me (@DownloadStickersBot) ```")
            return
        if response.text.startswith("Hi!"):
            await event.edit(
                "```Can you kindly disable your forward privacy settings for good?```"
            )
        else:
            await event.delete()
            await event.client.send_message(event.chat_id, response.message)


from collections.abc import Iterable, Mapping
from contextlib import contextmanager
from copy import deepcopy
from enum import Enum, auto
import threading
from typing import Union
import warnings

import numpy as np

from iris._lazy_data import as_lazy_data
from iris.aux_factory import (
    AtmosphereSigmaFactory,
    HybridHeightFactory,
    HybridPressureFactory,
    OceanSFactory,
    OceanSg1Factory,
    OceanSg2Factory,
    OceanSigmaFactory,
    OceanSigmaZFactory,
)
import iris.config
import iris.coord_systems
import iris.coords
import iris.exceptions
import iris.fileformats.cf
from iris.fileformats.netcdf import _thread_safe_nc
from iris.fileformats.netcdf.saver import _CF_ATTRS
import iris.io
import iris.util

# Show actions activation statistics.
DEBUG = False

# Get the logger : shared logger for all in 'iris.fileformats.netcdf'.
from . import logger

# An expected part of the public loader API, but includes thread safety
#  concerns so is housed in _thread_safe_nc.
NetCDFDataProxy = _thread_safe_nc.NetCDFDataProxy

class _WarnComboIgnoringBoundsLoad(
    iris.exceptions.IrisIgnoringBoundsWarning,
    iris.exceptions.IrisLoadWarning,
):
    """One-off combination of warning classes - enhances user filtering."""

    pass

def _actions_engine():
    # Return an 'actions engine', which provides a pyke-rules-like interface to
    # the core cf translation code.
    # Deferred import to avoid circularity.
    import iris.fileformats._nc_load_rules.engine as nc_actions_engine

    engine = nc_actions_engine.Engine()
    return engine

def _assert_case_specific_facts(engine, cf, cf_group):
    # Initialise a data store for built cube elements.
    # This is used to patch element attributes *not* setup by the actions
    # process, after the actions code has run.
    engine.cube_parts["coordinates"] = []
    engine.cube_parts["cell_measures"] = []
    engine.cube_parts["ancillary_variables"] = []

    # Assert facts for CF coordinates.
    for cf_name in cf_group.coordinates.keys():
        engine.add_case_specific_fact("coordinate", (cf_name,))

    # Assert facts for CF auxiliary coordinates.
    for cf_name in cf_group.auxiliary_coordinates.keys():
        engine.add_case_specific_fact("auxiliary_coordinate", (cf_name,))

    # Assert facts for CF cell measures.
    for cf_name in cf_group.cell_measures.keys():
        engine.add_case_specific_fact("cell_measure", (cf_name,))

    # Assert facts for CF ancillary variables.
    for cf_name in cf_group.ancillary_variables.keys():
        engine.add_case_specific_fact("ancillary_variable", (cf_name,))

    # Assert facts for CF grid_mappings.
    for cf_name in cf_group.grid_mappings.keys():
        engine.add_case_specific_fact("grid_mapping", (cf_name,))

    # Assert facts for CF labels.
    for cf_name in cf_group.labels.keys():
        engine.add_case_specific_fact("label", (cf_name,))

    # Assert facts for CF formula terms associated with the cf_group
    # of the CF data variable.

    # Collect varnames of formula-root variables as we go.
    # NOTE: use dictionary keys as an 'OrderedSet'
    #   - see: https://stackoverflow.com/a/53657523/2615050
    # This is to ensure that we can handle the resulting facts in a definite
    # order, as using a 'set' led to indeterminate results.
    formula_root = {}
    for cf_var in cf.cf_group.formula_terms.values():
        for cf_root, cf_term in cf_var.cf_terms_by_root.items():
            # Only assert this fact if the formula root variable is
            # defined in the CF group of the CF data variable.
            if cf_root in cf_group:
                formula_root[cf_root] = True
                engine.add_case_specific_fact(
                    "formula_term",
                    (cf_var.cf_name, cf_root, cf_term),
                )

    for cf_root in formula_root.keys():
        engine.add_case_specific_fact("formula_root", (cf_root,))

def _actions_activation_stats(engine, cf_name):
    print("-" * 80)
    print("CF Data Variable: %r" % cf_name)

    engine.print_stats()

    print("Rules Triggered:")

    for rule in sorted(list(engine.rules_triggered)):
        print("\t%s" % rule)

    print("Case Specific Facts:")
    kb_facts = engine.get_kb()

    for key in kb_facts.entity_lists.keys():
        for arg in kb_facts.entity_lists[key].case_specific_facts:
            print("\t%s%s" % (key, arg))

def _set_attributes(attributes, key, value):
    """Set attributes dictionary, converting unicode strings appropriately."""

    if isinstance(value, str):
        try:
            attributes[str(key)] = str(value)
        except UnicodeEncodeError:
            attributes[str(key)] = value
    else:
        attributes[str(key)] = value

def _add_unused_attributes(iris_object, cf_var):
    """
    Populate the attributes of a cf element with the "unused" attributes
    from the associated CF-netCDF variable. That is, all those that aren't CF
    reserved terms.

    """

    def attribute_predicate(item):
        return item[0] not in _CF_ATTRS

    tmpvar = filter(attribute_predicate, cf_var.cf_attrs_unused())
    attrs_dict = iris_object.attributes
    if hasattr(attrs_dict, "locals"):
        # Treat cube attributes (i.e. a CubeAttrsDict) as a special case.
        # These attrs are "local" (i.e. on the variable), so record them as such.
        attrs_dict = attrs_dict.locals
    for attr_name, attr_value in tmpvar:
        _set_attributes(attrs_dict, attr_name, attr_value)

def _get_actual_dtype(cf_var):
    # Figure out what the eventual data type will be after any scale/offset
    # transforms.
    dummy_data = np.zeros(1, dtype=cf_var.dtype)
    if hasattr(cf_var, "scale_factor"):
        dummy_data = cf_var.scale_factor * dummy_data
    if hasattr(cf_var, "add_offset"):
        dummy_data = cf_var.add_offset + dummy_data
    return dummy_data.dtype

# An arbitrary variable array size, below which we will fetch real data from a variable
# rather than making a lazy array for deferred access.
# Set by experiment at roughly the point where it begins to save us memory, but actually
# mostly done for speed improvement.  See https://github.com/SciTools/iris/pull/5069
_LAZYVAR_MIN_BYTES = 5000

def _get_cf_var_data(cf_var, filename):
    """
    Get an array representing the data of a CF variable.

    This is typically a lazy array based around a NetCDFDataProxy, but if the variable
    is "sufficiently small", we instead fetch the data as a real (numpy) array.
    The latter is especially valuable for scalar coordinates, which are otherwise
    unnecessarily slow + wasteful of memory.

    """
    global CHUNK_CONTROL
    if hasattr(cf_var, "_data_array"):
        # The variable is not an actual netCDF4 file variable, but an emulating
        # object with an attached data array (either numpy or dask), which can be
        # returned immediately as-is.  This is used as a hook to translate data to/from
        # netcdf data container objects in other packages, such as xarray.
        # See https://github.com/SciTools/iris/issues/4994 "Xarray bridge".
        result = cf_var._data_array
    else:
        total_bytes = cf_var.size * cf_var.dtype.itemsize
        if total_bytes < _LAZYVAR_MIN_BYTES:
            # Don't make a lazy array, as it will cost more memory AND more time to access.
            # Instead fetch the data immediately, as a real array, and return that.
            result = cf_var[:]

        else:
            # Get lazy chunked data out of a cf variable.
            # Creates Dask wrappers around data arrays for any cube components which
            # can have lazy values, e.g. Cube, Coord, CellMeasure, AuxiliaryVariable.
            dtype = _get_actual_dtype(cf_var)

            # Make a data-proxy that mimics array access and can fetch from the file.
            fill_value = getattr(
                cf_var.cf_data,
                "_FillValue",
                _thread_safe_nc.default_fillvals[cf_var.dtype.str[1:]],
            )
            proxy = NetCDFDataProxy(
                cf_var.shape, dtype, filename, cf_var.cf_name, fill_value
            )
            # Get the chunking specified for the variable : this is either a shape, or
            # maybe the string "contiguous".
            if CHUNK_CONTROL.mode is ChunkControl.Modes.AS_DASK:
                result = as_lazy_data(proxy, chunks=None, dask_chunking=True)
            else:
                chunks = cf_var.cf_data.chunking()
                # In the "contiguous" case, pass chunks=None to 'as_lazy_data'.
                if chunks == "contiguous":
                    if (
                        CHUNK_CONTROL.mode is ChunkControl.Modes.FROM_FILE
                        and isinstance(
                            cf_var, iris.fileformats.cf.CFDataVariable
                        )
                    ):
                        raise KeyError(
                            f"{cf_var.cf_name} does not contain pre-existing chunk specifications."
                            f" Instead, you might wish to use CHUNK_CONTROL.set(), or just use default"
                            f" behaviour outside of a context manager. "
                        )
                    # Equivalent to chunks=None, but value required by chunking control
                    chunks = list(cf_var.shape)

                # Modify the chunking in the context of an active chunking control.
                # N.B. settings specific to this named var override global ('*') ones.
                dim_chunks = CHUNK_CONTROL.var_dim_chunksizes.get(
                    cf_var.cf_name
                ) or CHUNK_CONTROL.var_dim_chunksizes.get("*")
                dims = cf_var.cf_data.dimensions
                if CHUNK_CONTROL.mode is ChunkControl.Modes.FROM_FILE:
                    dims_fixed = np.ones(len(dims), dtype=bool)
                elif not dim_chunks:
                    dims_fixed = None
                else:
                    # Modify the chunks argument, and pass in a list of 'fixed' dims, for
                    # any of our dims which are controlled.
                    dims_fixed = np.zeros(len(dims), dtype=bool)
                    for i_dim, dim_name in enumerate(dims):
                        dim_chunksize = dim_chunks.get(dim_name)
                        if dim_chunksize:
                            if dim_chunksize == -1:
                                chunks[i_dim] = cf_var.shape[i_dim]
                            else:
                                chunks[i_dim] = dim_chunksize
                            dims_fixed[i_dim] = True
                if dims_fixed is None:
                    dims_fixed = [dims_fixed]
                result = as_lazy_data(
                    proxy, chunks=chunks, dims_fixed=tuple(dims_fixed)
                )
    return result

class _OrderedAddableList(list):
    """
    A custom container object for actions recording.
    Used purely in actions debugging, to accumulate a record of which actions
    were activated.

    It replaces a set, so as to preserve the ordering of operations, with
    possible repeats, and it also numbers the entries.

    The actions routines invoke an 'add' method, so this effectively replaces
    a set.add with a list.append.

    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._n_add = 0

    def add(self, msg):
        self._n_add += 1
        n_add = self._n_add
        self.append(f"#{n_add:03d} : {msg}")

def _load_cube(engine, cf, cf_var, filename):
    global CHUNK_CONTROL

    # Translate dimension chunk-settings specific to this cube (i.e. named by
    # it's data-var) into global ones, for the duration of this load.
    # Thus, by default, we will create any AuxCoords, CellMeasures et al with
    # any  per-dimension chunksizes specified for the cube.
    these_settings = CHUNK_CONTROL.var_dim_chunksizes.get(cf_var.cf_name, {})
    with CHUNK_CONTROL.set(**these_settings):
        return _load_cube_inner(engine, cf, cf_var, filename)

def _load_cube_inner(engine, cf, cf_var, filename):
    from iris.cube import Cube

    """Create the cube associated with the CF-netCDF data variable."""
    data = _get_cf_var_data(cf_var, filename)
    cube = Cube(data)

    # Reset the actions engine.
    engine.reset()

    # Initialise engine rule processing hooks.
    engine.cf_var = cf_var
    engine.cube = cube
    engine.cube_parts = {}
    engine.requires = {}
    engine.rules_triggered = _OrderedAddableList()
    engine.filename = filename

    # Assert all the case-specific facts.
    # This extracts 'facts' specific to this data-variable (aka cube), from
    # the info supplied in the CFGroup object.
    _assert_case_specific_facts(engine, cf, cf_var.cf_group)

    # Run the actions engine.
    # This creates various cube elements and attaches them to the cube.
    # It also records various other info on the engine, to be processed later.
    engine.activate()

    # Having run the rules, now add the "unused" attributes to each cf element.
    def fix_attributes_all_elements(role_name):
        elements_and_names = engine.cube_parts.get(role_name, [])

        for iris_object, cf_var_name in elements_and_names:
            _add_unused_attributes(iris_object, cf.cf_group[cf_var_name])

    # Populate the attributes of all coordinates, cell-measures and ancillary-vars.
    fix_attributes_all_elements("coordinates")
    fix_attributes_all_elements("ancillary_variables")
    fix_attributes_all_elements("cell_measures")

    # Also populate attributes of the top-level cube itself.
    _add_unused_attributes(cube, cf_var)

    # Work out reference names for all the coords.
    names = {
        coord.var_name: coord.standard_name or coord.var_name or "unknown"
        for coord in cube.coords()
    }

    # Add all the cube cell methods.
    cube.cell_methods = [
        iris.coords.CellMethod(
            method=method.method,
            intervals=method.intervals,
            comments=method.comments,
            coords=[
                names[coord_name] if coord_name in names else coord_name
                for coord_name in method.coord_names
            ],
        )
        for method in cube.cell_methods
    ]

    if DEBUG:
        # Show activation statistics for this data-var (i.e. cube).
        _actions_activation_stats(engine, cf_var.cf_name)

    return cube

def _load_aux_factory(engine, cube):
    """
    Convert any CF-netCDF dimensionless coordinate to an AuxCoordFactory.

    """
    formula_type = engine.requires.get("formula_type")
    if formula_type in [
        "atmosphere_sigma_coordinate",
        "atmosphere_hybrid_height_coordinate",
        "atmosphere_hybrid_sigma_pressure_coordinate",
        "ocean_sigma_z_coordinate",
        "ocean_sigma_coordinate",
        "ocean_s_coordinate",
        "ocean_s_coordinate_g1",
        "ocean_s_coordinate_g2",
    ]:

        def coord_from_term(term):
            # Convert term names to coordinates (via netCDF variable names).
            name = engine.requires["formula_terms"].get(term, None)
            if name is not None:
                for coord, cf_var_name in engine.cube_parts["coordinates"]:
                    if cf_var_name == name:
                        return coord
                warnings.warn(
                    "Unable to find coordinate for variable "
                    "{!r}".format(name),
                    category=iris.exceptions.IrisFactoryCoordNotFoundWarning,
                )

        if formula_type == "atmosphere_sigma_coordinate":
            pressure_at_top = coord_from_term("ptop")
            sigma = coord_from_term("sigma")
            surface_air_pressure = coord_from_term("ps")
            factory = AtmosphereSigmaFactory(
                pressure_at_top, sigma, surface_air_pressure
            )
        elif formula_type == "atmosphere_hybrid_height_coordinate":
            delta = coord_from_term("a")
            sigma = coord_from_term("b")
            orography = coord_from_term("orog")
            factory = HybridHeightFactory(delta, sigma, orography)
        elif formula_type == "atmosphere_hybrid_sigma_pressure_coordinate":
            # Hybrid pressure has two valid versions of its formula terms:
            # "p0: var1 a: var2 b: var3 ps: var4" or
            # "ap: var1 b: var2 ps: var3" where "ap = p0 * a"
            # Attempt to get the "ap" term.
            delta = coord_from_term("ap")
            if delta is None:
                # The "ap" term is unavailable, so try getting terms "p0"
                # and "a" terms in order to derive an "ap" equivalent term.
                coord_p0 = coord_from_term("p0")
                if coord_p0 is not None:
                    if coord_p0.shape != (1,):
                        msg = (
                            "Expecting {!r} to be a scalar reference "
                            "pressure coordinate, got shape {!r}".format(
                                coord_p0.var_name, coord_p0.shape
                            )
                        )
                        raise ValueError(msg)
                    if coord_p0.has_bounds():
                        msg = (
                            "Ignoring atmosphere hybrid sigma pressure "
                            "scalar coordinate {!r} bounds.".format(
                                coord_p0.name()
                            )
                        )
                        warnings.warn(
                            msg,
                            category=_WarnComboIgnoringBoundsLoad,
                        )
                    coord_a = coord_from_term("a")
                    if coord_a is not None:
                        if coord_a.units.is_unknown():
                            # Be graceful, and promote unknown to dimensionless units.
                            coord_a.units = "1"
                        delta = coord_a * coord_p0.points[0]
                        delta.units = coord_a.units * coord_p0.units
                        delta.rename("vertical pressure")
                        delta.var_name = "ap"
                        cube.add_aux_coord(delta, cube.coord_dims(coord_a))

            sigma = coord_from_term("b")
            surface_air_pressure = coord_from_term("ps")
            factory = HybridPressureFactory(delta, sigma, surface_air_pressure)
        elif formula_type == "ocean_sigma_z_coordinate":
            sigma = coord_from_term("sigma")
            eta = coord_from_term("eta")
            depth = coord_from_term("depth")
            depth_c = coord_from_term("depth_c")
            nsigma = coord_from_term("nsigma")
            zlev = coord_from_term("zlev")
            factory = OceanSigmaZFactory(
                sigma, eta, depth, depth_c, nsigma, zlev
            )
        elif formula_type == "ocean_sigma_coordinate":
            sigma = coord_from_term("sigma")
            eta = coord_from_term("eta")
            depth = coord_from_term("depth")
            factory = OceanSigmaFactory(sigma, eta, depth)
        elif formula_type == "ocean_s_coordinate":
            s = coord_from_term("s")
            eta = coord_from_term("eta")
            depth = coord_from_term("depth")
            a = coord_from_term("a")
            depth_c = coord_from_term("depth_c")
            b = coord_from_term("b")
            factory = OceanSFactory(s, eta, depth, a, b, depth_c)
        elif formula_type == "ocean_s_coordinate_g1":
            s = coord_from_term("s")
            c = coord_from_term("c")
            eta = coord_from_term("eta")
            depth = coord_from_term("depth")
            depth_c = coord_from_term("depth_c")
            factory = OceanSg1Factory(s, c, eta, depth, depth_c)
        elif formula_type == "ocean_s_coordinate_g2":
            s = coord_from_term("s")
            c = coord_from_term("c")
            eta = coord_from_term("eta")
            depth = coord_from_term("depth")
            depth_c = coord_from_term("depth_c")
            factory = OceanSg2Factory(s, c, eta, depth, depth_c)
        cube.add_aux_factory(factory)

def _translate_constraints_to_var_callback(constraints):
    """
    Translate load constraints into a simple data-var filter function, if possible.

    Returns:
         * function(cf_var:CFDataVariable): --> bool,
            or None.

    For now, ONLY handles a single NameConstraint with no 'STASH' component.

    """
    import iris._constraints

    constraints = iris._constraints.list_of_constraints(constraints)
    result = None
    if len(constraints) == 1:
        (constraint,) = constraints
        if (
            isinstance(constraint, iris._constraints.NameConstraint)
            and constraint.STASH == "none"
        ):
            # As long as it doesn't use a STASH match, then we can treat it as
            # a testing against name properties of cf_var.
            # That's just like testing against name properties of a cube, except that they may not all exist.
            def inner(cf_datavar):
                match = True
                for name in constraint._names:
                    expected = getattr(constraint, name)
                    if name != "STASH" and expected != "none":
                        attr_name = "cf_name" if name == "var_name" else name
                        # Fetch property : N.B. CFVariable caches the property values
                        # The use of a default here is the only difference from the code in NameConstraint.
                        if not hasattr(cf_datavar, attr_name):
                            continue
                        actual = getattr(cf_datavar, attr_name, "")
                        if actual != expected:
                            match = False
                            break
                return match

            result = inner
    return result

def load_cubes(file_sources, callback=None, constraints=None):
    """
    Loads cubes from a list of NetCDF filenames/OPeNDAP URLs.

    Args:

    * file_sources (string/list):
        One or more NetCDF filenames/OPeNDAP URLs to load from.
        OR open datasets.

    Kwargs:

    * callback (callable function):
        Function which can be passed on to :func:`iris.io.run_callback`.

    Returns:
        Generator of loaded NetCDF :class:`iris.cube.Cube`.

    """
    # TODO: rationalise UGRID/mesh handling once experimental.ugrid is folded
    #  into standard behaviour.
    # Deferred import to avoid circular imports.
    from iris.experimental.ugrid.cf import CFUGridReader
    from iris.experimental.ugrid.load import (
        PARSE_UGRID_ON_LOAD,
        _build_mesh_coords,
        _meshes_from_cf,
    )
    from iris.io import run_callback

    # Create a low-level data-var filter from the original load constraints, if they are suitable.
    var_callback = _translate_constraints_to_var_callback(constraints)

    # Create an actions engine.
    engine = _actions_engine()

    if isinstance(file_sources, str) or not isinstance(file_sources, Iterable):
        file_sources = [file_sources]

    for file_source in file_sources:
        # Ingest the file.  At present may be a filepath or an open netCDF4.Dataset.
        meshes = {}
        if PARSE_UGRID_ON_LOAD:
            cf_reader_class = CFUGridReader
        else:
            cf_reader_class = iris.fileformats.cf.CFReader

        with cf_reader_class(file_source) as cf:
            if PARSE_UGRID_ON_LOAD:
                meshes = _meshes_from_cf(cf)

            # Process each CF data variable.
            data_variables = list(cf.cf_group.data_variables.values()) + list(
                cf.cf_group.promoted.values()
            )
            for cf_var in data_variables:
                if var_callback and not var_callback(cf_var):
                    # Deliver only selected results.
                    continue

                # cf_var-specific mesh handling, if a mesh is present.
                # Build the mesh_coords *before* loading the cube - avoids
                # mesh-related attributes being picked up by
                # _add_unused_attributes().
                mesh_name = None
                mesh = None
                mesh_coords, mesh_dim = [], None
                if PARSE_UGRID_ON_LOAD:
                    mesh_name = getattr(cf_var, "mesh", None)
                if mesh_name is not None:
                    try:
                        mesh = meshes[mesh_name]
                    except KeyError:
                        message = (
                            f"File does not contain mesh: '{mesh_name}' - "
                            f"referenced by variable: '{cf_var.cf_name}' ."
                        )
                        logger.debug(message)
                if mesh is not None:
                    mesh_coords, mesh_dim = _build_mesh_coords(mesh, cf_var)

                cube = _load_cube(engine, cf, cf_var, cf.filename)

                # Attach the mesh (if present) to the cube.
                for mesh_coord in mesh_coords:
                    cube.add_aux_coord(mesh_coord, mesh_dim)

                # Process any associated formula terms and attach
                # the corresponding AuxCoordFactory.
                try:
                    _load_aux_factory(engine, cube)
                except ValueError as e:
                    warnings.warn(
                        "{}".format(e),
                        category=iris.exceptions.IrisLoadWarning,
                    )

                # Perform any user registered callback function.
                cube = run_callback(callback, cube, cf_var, file_source)

                # Callback mechanism may return None, which must not be yielded
                if cube is None:
                    continue

                yield cube

class ChunkControl(threading.local):
    class Modes(Enum):
        DEFAULT = auto()
        FROM_FILE = auto()
        AS_DASK = auto()

    def __init__(self, var_dim_chunksizes=None):
        """
        Provide user control of Dask chunking.

        The NetCDF loader is controlled by the single instance of this: the
        :data:`~iris.fileformats.netcdf.loader.CHUNK_CONTROL` object.

        A chunk size can be set for a specific (named) file dimension, when
        loading specific (named) variables, or for all variables.

        When a selected variable is a CF data-variable, which loads as a
        :class:`~iris.cube.Cube`, then the given dimension chunk size is *also*
        fixed for all variables which are components of that :class:`~iris.cube.Cube`,
        i.e. any :class:`~iris.coords.Coord`, :class:`~iris.coords.CellMeasure`,
        :class:`~iris.coords.AncillaryVariable` etc.
        This can be overridden, if required, by variable-specific settings.

        For this purpose, :class:`~iris.experimental.ugrid.mesh.MeshCoord` and
        :class:`~iris.experimental.ugrid.mesh.Connectivity` are not
        :class:`~iris.cube.Cube` components, and chunk control on a
        :class:`~iris.cube.Cube` data-variable will not affect them.

        """
        self.var_dim_chunksizes = var_dim_chunksizes or {}
        self.mode = self.Modes.DEFAULT

    @contextmanager
    def set(
        self,
        var_names: Union[str, Iterable[str]] = None,
        **dimension_chunksizes: Mapping[str, int],
    ) -> None:
        """
        Control the Dask chunk sizes applied to NetCDF variables during loading.

        Parameters
        ----------
        var_names : str or list of str, default=None
            apply the `dimension_chunksizes` controls only to these variables,
            or when building :class:`~iris.cube.Cube`\\ s from these data variables.
            If ``None``, settings apply to all loaded variables.
        dimension_chunksizes : dict of {str: int}
            Kwargs specifying chunksizes for dimensions of file variables.
            Each key-value pair defines a chunk size for a named file
            dimension, e.g. ``{'time': 10, 'model_levels':1}``.
            Values of ``-1`` will lock the chunk size to the full size of that
            dimension.

        Notes
        -----
        This function acts as a context manager, for use in a ``with`` block.

        >>> import iris
        >>> from iris.fileformats.netcdf.loader import CHUNK_CONTROL
        >>> with CHUNK_CONTROL.set("air_temperature", time=180, latitude=-1):
        ...     cube = iris.load(iris.sample_data_path("E1_north_america.nc"))[0]

        When `var_names` is present, the chunk size adjustments are applied
        only to the selected variables.  However, for a CF data variable, this
        extends to all components of the (raw) :class:`~iris.cube.Cube` created
        from it.

        **Un**-adjusted dimensions have chunk sizes set in the 'usual' way.
        That is, according to the normal behaviour of
        :func:`iris._lazy_data.as_lazy_data`, which is: chunk size is based on
        the file variable chunking, or full variable shape; this is scaled up
        or down by integer factors to best match the Dask default chunk size,
        i.e. the setting configured by
        ``dask.config.set({'array.chunk-size': '250MiB'})``.

        """
        old_mode = self.mode
        old_var_dim_chunksizes = deepcopy(self.var_dim_chunksizes)
        if var_names is None:
            var_names = ["*"]
        elif isinstance(var_names, str):
            var_names = [var_names]
        try:
            for var_name in var_names:
                # Note: here we simply treat '*' as another name.
                # A specific name match should override a '*' setting, but
                # that is implemented elsewhere.
                if not isinstance(var_name, str):
                    msg = (
                        "'var_names' should be an iterable of strings, "
                        f"not {var_names!r}."
                    )
                    raise ValueError(msg)
                dim_chunks = self.var_dim_chunksizes.setdefault(var_name, {})
                for dim_name, chunksize in dimension_chunksizes.items():
                    if not (
                        isinstance(dim_name, str)
                        and isinstance(chunksize, int)
                    ):
                        msg = (
                            "'dimension_chunksizes' kwargs should be a dict "
                            f"of `str: int` pairs, not {dimension_chunksizes!r}."
                        )
                        raise ValueError(msg)
                    dim_chunks[dim_name] = chunksize
            yield
        finally:
            self.var_dim_chunksizes = old_var_dim_chunksizes
            self.mode = old_mode

    @contextmanager
    def from_file(self) -> None:
        """
        Ensures the chunk sizes are loaded in from NetCDF file variables.

        Raises
        ------
        KeyError
            If any NetCDF data variables - those that become
            :class:`~iris.cube.Cube`\\ s - do not specify chunk sizes.

        Notes
        -----
        This function acts as a context manager, for use in a ``with`` block.
        """
        old_mode = self.mode
        old_var_dim_chunksizes = deepcopy(self.var_dim_chunksizes)
        try:
            self.mode = self.Modes.FROM_FILE
            yield
        finally:
            self.mode = old_mode
            self.var_dim_chunksizes = old_var_dim_chunksizes

    @contextmanager
    def as_dask(self) -> None:
        """
        Relies on Dask :external+dask:doc:`array` to control chunk sizes.

        Notes
        -----
        This function acts as a context manager, for use in a ``with`` block.
        """
        old_mode = self.mode
        old_var_dim_chunksizes = deepcopy(self.var_dim_chunksizes)
        try:
            self.mode = self.Modes.AS_DASK
            yield
        finally:
            self.mode = old_mode
            self.var_dim_chunksizes = old_var_dim_chunksizes

# Note: the CHUNK_CONTROL object controls chunk sizing in the
# :meth:`_get_cf_var_data` method.
# N.B. :meth:`_load_cube` also modifies this when loading each cube,
# introducing an additional context in which any cube-specific settings are
# 'promoted' into being global ones.

#: The global :class:`ChunkControl` object providing user-control of Dask chunking
#: when Iris loads NetCDF files.
CHUNK_CONTROL: ChunkControl = ChunkControl()


import os

import matplotlib.pyplot as plt
import numpy as np

from FileProcess.GraphClass import Graph

work_dir = '../data/Mmusculus/Full'
file_list = os.listdir(work_dir)
work_dir2 = '../data/Mmusculus/Full/Cleann'
file_list2 = os.listdir(work_dir2)
print(file_list)
print(file_list2)

n_vert_list_total = []
n_vert_list_nemo = []
n_list_profile = []
relative_frequency = []

cnt = 0
for i in range(len(file_list)):
    rel_freq = []
    filename = file_list[i]
    graph = Graph()
    curPath = work_dir + '/' + filename
    if os.path.isfile(curPath):
        with open(curPath) as f:
            first = True
            for line in f:
                if first:
                    first = False
                    continue
                v1, v2, w = line.split('\t', 2)
                id1 = v1.split('|')[0]
                id2 = v2.split('|')[0]
                graph.add_edge(id1, id2)

        verts = dict.fromkeys(graph.vertices.keys())
        gg = {}

        print(curPath)
        curPath = work_dir + '/Cleann/' + file_list2[cnt]
        print(curPath)
        with open(curPath) as f:
            for line in f:
                if not line.startswith('DIP'):
                    if line.startswith('CF') or line.startswith('CN') or line.startswith('C^') \
                            or line.startswith('C~') or line.startswith('CR') or line.startswith('Cr'):
                        rel_freq.append(float(line.split()[1].replace('%', '')))
                    continue
                else:
                    ll = list(line.split())
                    verts[ll[0]] = list(map(int, ll[1:]))
                    gg[ll[0]] = list(map(int, ll[1:]))
        print('length of nemoprofile nodes and original vertices', len(gg), len(verts))
        profile = [0 for x in range(6)]

        for v in gg:
            for j in range(len(gg[v])):
                profile[j] += gg[v][j]

        for j in range(len(profile)):
            profile[j] = profile[j] / 4
        print(profile)
        n_vert_list_total.append(len(verts))
        n_vert_list_nemo.append(len(gg))
        n_list_profile.append(profile)
        cnt += 1
        relative_frequency.append(rel_freq)

print(relative_frequency)
print(n_vert_list_total)
print(n_vert_list_nemo)
# print(n_list_profile)
# label = ['CF', 'CN', 'CR', 'C^', 'Cr', 'C~']
label = ['CF', 'CN', 'CR', 'C^', 'Cr', 'C~']
n_list_profile = np.array(n_list_profile)

n_list_profile_change = []

# print(n_list_profile)
gens = list(range(0, 10))
plt.xlabel("Generation")
plt.ylabel("Number of node")
plt.title("Node number vs Generations")
for i in range(6):
    temp_list = []
    prev = n_list_profile[:, i][0]
    for temp in n_list_profile[:, i]:
        temp_list.append(temp - prev)
    n_list_profile_change.append(temp_list)

for i in range(6):
    plt.plot(gens, n_list_profile_change[i], label=label[i])

plt.legend()
plt.show()

n_list_profile_change_per = []

for i in range(6):
    temp_list = []
    prev = n_list_profile[:, i][0]
    for temp in n_list_profile[:, i]:
        temp_list.append(100 * (temp - prev) / prev)
    n_list_profile_change_per.append(temp_list)

plt.xlabel("Generation")
plt.ylabel("Percentage of node number change")
plt.title("Percentage Change vs Generations")
for i in range(6):
    plt.plot(gens, n_list_profile_change_per[i], label=label[i])

plt.legend()
plt.show()

label = ['CF', 'CN', 'C^', 'C~', 'CR', 'Cr']

relative_frequency = np.array(relative_frequency)
rel_list = []
for i in range(6):
    temp_list = []
    for temp in relative_frequency[:, i]:
        temp_list.append(temp)
    rel_list.append(temp_list)

for i in range(6):
    plt.xlabel("Generation")
    plt.ylabel("Relative frequency")
    plt.title("Relative frequency vs Generations")
    plt.plot(gens, rel_list[i], label=label[i])
    plt.legend()
    plt.show()

plt.xlabel("Generation")
plt.ylabel("Relative frequency")
plt.title("Relative frequency vs Generations")
for i in range(6):
    plt.plot(gens, rel_list[i], label=label[i])
plt.legend()
plt.show()


from flask import Flask, request, render_template, redirect, flash
import requests, json
import sys

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secr3t'

STATUS_KEY = ['mode', 'r', 'g', 'b', 'sr', 'sg', 'sb', 'brightness', 'power', 'speed', 'wait_time', 'percentage', 'spercentage']
CONTROLLER_KEY = ['name', 'ip', 'port']

def import_controllers():
    retval = {}
    for i in get_controllers():
        retval[i[0]] = [i[1], i[2]]
    return retval

def get_controllers():
    with open("./file/controllers.txt", "r") as f:
        try:
            if f is None:
                return []
            return [l.strip().split(',') for l in f.readlines()]
        except:
            return 'failed'

def add_controller(name, ip, port):
    with open("./file/controllers.txt", "a") as f:
        try:
            f.write(f'{name},{ip},{port}\n')
            print(f'{name},{ip},{port}')
            return 'Success'
        except Exception as e:
            return 'Failed' + str(e)

def import_status(resp_status):
    listx = resp_status.split(',')
    retval = {'list': listx}
    count = 0
    for item in listx:
        retval[STATUS_KEY[count]] = item
        count = count + 1
    return retval

def get_controller_status(ip, port):
    resp = requests.get(f'http://{ip}:{port}/status')
    resp = resp.json()
    return import_status(resp['status'])

def get_all_controller_status(controllers):
    retval = {}
    for i in controllers:
        ip = i[1]
        port = i[2]
        retval[i[0]] = get_controller_status(ip, port)
    return retval

@app.route('/panel', methods=['GET', 'POST'])
def panel():
    # controllers = get_controllers()
    controllers = import_controllers()
    return redirect(f'/panel/{list(controllers.keys())[0]}')
    # cstatuses = get_all_controller_status(controllers)
    # return render_template('panel.html', controller=controllers[list(controllers.keys())[0]], controllers=list(controllers.keys()))

@app.route('/panel/<controller>', methods=['GET', 'POST'])
def panel_c(controller):
    controllers = import_controllers()
    print(controllers)
    x = controllers[controller]
    print(x)
    status = get_controller_status(x[0], x[1])
    # controllers = get_controllers()

    return render_template('panel.html', controller=controller, controllers=list(controllers.keys()), status=status)

@app.route('/add', methods=['GET', 'POST'])
def add():
    if request.method == 'POST':
        retval = add_controller(request.form['name'], request.form['ip_address'], request.form['port'])
        flash(retval)
        return redirect('/panel')
    return render_template('add.html')

@app.route('/power/<toggle>/<controller>')
def power(toggle, controller):
    print(toggle)
    return redirect(f'/panel/{controller}')

sys.stdout.write('starting flask app. \n')
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=420, debug=False)


from __future__ import (
    unicode_literals,
    print_function,
    absolute_import,
    division,
    )

import sys
import os
import optparse
import textwrap
import logging
import locale
import traceback
import glob
from itertools import chain

try:
    # Optionally import optcomplete (for auto-completion) if it's installed
    import optcomplete
except ImportError:
    optcomplete = None

# Use the user's default locale instead of C
locale.setlocale(locale.LC_ALL, '')

# Set up a console logging handler which just prints messages without any other
# adornments
_CONSOLE = logging.StreamHandler(sys.stderr)
_CONSOLE.setFormatter(logging.Formatter('%(message)s'))
_CONSOLE.setLevel(logging.DEBUG)
logging.getLogger().addHandler(_CONSOLE)

def normalize_path(path):
    """
    Eliminates symlinks, makes path absolute and normalizes case
    """
    return os.path.normcase(os.path.realpath(os.path.abspath(
        os.path.expanduser(path)
    )))

def glob_arg(arg):
    """
    Perform shell-style globbing of arguments
    """
    if set('*?[') & set(arg):
        args = glob.glob(normalize_path(arg))
        if args:
            return args
    # Return the original parameter in the case where the parameter contains no
    # wildcards or globbing returns no results
    return [arg]

def flatten(arg):
    """
    Flatten one level of nesting
    """
    return chain.from_iterable(arg)

def expand_args(args):
    """
    Expands @response files and wildcards in the command line
    """
    windows = sys.platform.startswith('win')
    result = []
    for arg in args:
        if arg.startswith('@') and len(arg) > 1:
            arg = normalize_path(arg[1:])
            try:
                with open(arg, 'rU') as resp_file:
                    for resp_arg in resp_file:
                        # Only strip the line break (whitespace is
                        # significant)
                        resp_arg = resp_arg.rstrip('\n')
                        # Only perform globbing on response file values for
                        # UNIX
                        if windows:
                            result.append(resp_arg)
                        else:
                            result.extend(glob_arg(resp_arg))
            except IOError as exc:
                raise optparse.OptionValueError(str(exc))
        else:
            result.append(arg)
    # Perform globbing on everything for Windows
    if windows:
        result = list(flatten(glob_arg(f) for f in result))
    return result

class HelpFormatter(optparse.IndentedHelpFormatter):
    """
    Customize the width of help output
    """
    def __init__(self):
        width = 75
        optparse.IndentedHelpFormatter.__init__(
                self, max_help_position=width // 3, width=width)

class OptionParser(optparse.OptionParser):
    """
    Customized OptionParser which raises an exception but doesn't terminate
    """
    def error(self, msg):
        raise optparse.OptParseError(msg)

class TerminalApplication(object):
    """
    Base class for command line applications.

    This class provides command line parsing, file globbing, response file
    handling and common logging configuration for command line utilities.
    Descendent classes should override the main() method to implement their
    main body, and __init__() if they wish to extend the command line options.
    """
    # Get the default output encoding from the default locale
    encoding = locale.getdefaultlocale()[1]

    # This class is the abstract base class for each of the command line
    # utility classes defined. It provides some basic facilities like an option
    # parser, console pretty-printing, logging and exception handling

    def __init__(self, version, usage=None, description=None):
        super(TerminalApplication, self).__init__()
        self.wrapper = textwrap.TextWrapper()
        self.wrapper.width = 75
        if usage is None:
            usage = self.__doc__.strip().split('\n')[0]
        if description is None:
            description = self.wrapper.fill('\n'.join(
                line.lstrip()
                for line in self.__doc__.strip().split('\n')[1:]
                if line.lstrip()
                ))
        self.parser = OptionParser(
            usage=usage,
            version=version,
            description=description,
            formatter=HelpFormatter()
            )
        self.parser.set_defaults(
            debug=False,
            logfile='',
            loglevel=logging.WARNING
            )
        self.parser.add_option(
            '-q', '--quiet', dest='loglevel', action='store_const',
            const=logging.ERROR, help='produce less console output')
        self.parser.add_option(
            '-v', '--verbose', dest='loglevel', action='store_const',
            const=logging.INFO, help='produce more console output')
        self.parser.add_option(
            '-l', '--log-file', dest='logfile',
            help='log messages to the specified file')
        if optcomplete:
            opt.completer = optcomplete.RegexCompleter(['.*\.log', '.*\.txt'])
        self.parser.add_option(
            '-P', '--pdb', dest='debug', action='store_true',
            help='run under PDB (debug mode)')
        self.arg_completer = None

    def __call__(self, args=None):
        sys.excepthook = self.handle
        if args is None:
            args = sys.argv[1:]
        if optcomplete:
            optcomplete.autocomplete(self.parser, self.arg_completer)
        elif 'COMP_LINE' in os.environ:
            return 0
        (options, args) = self.parser.parse_args(expand_args(args))
        _CONSOLE.setLevel(options.loglevel)
        if options.logfile:
            logfile = logging.FileHandler(options.logfile)
            logfile.setFormatter(
                logging.Formatter('%(asctime)s, %(levelname)s, %(message)s'))
            logfile.setLevel(logging.DEBUG)
            logging.getLogger().addHandler(logfile)
        if options.debug:
            logging.getLogger().setLevel(logging.DEBUG)
        else:
            logging.getLogger().setLevel(logging.INFO)
        if options.debug:
            import pdb
            return pdb.runcall(self.main, options, args)
        else:
            return self.main(options, args) or 0

    def handle(self, exc_type, exc_value, exc_trace):
        "Global application exception handler"
        if issubclass(exc_type, (SystemExit, KeyboardInterrupt)):
            # Just ignore system exit and keyboard interrupt errors (after all,
            # they're user generated)
            return 130
        elif issubclass(exc_type, (ValueError, IOError)):
            # For simple errors like IOError just output the message which
            # should be sufficient for the end user (no need to confuse them
            # with a full stack trace)
            logging.critical(str(exc_value))
            return 1
        elif issubclass(exc_type, (optparse.OptParseError,)):
            # For option parser errors output the error along with a message
            # indicating how the help page can be displayed
            logging.critical(str(exc_value))
            logging.critical('Try the --help option for more information.')
            return 2
        else:
            # Otherwise, log the stack trace and the exception into the log
            # file for debugging purposes
            for line in traceback.format_exception(exc_type, exc_value, exc_trace):
                for msg in line.rstrip().split('\n'):
                    logging.critical(msg)
            return 1

    def main(self, options, args):
        "Called as the main body of the utility"
        raise NotImplementedError

import io
import serial
from xml.etree.ElementTree import fromstring, tostring

from oxitopped import __version__
from oxitopped.bottles import Bottle
from oxitopped.logger import DataLogger, DummyLogger, LoggerError
from oxitopped.nullmodem import null_modem

class OxiTopApplication(TerminalApplication):
    """
    Base class for oxitop command line applications.

    This class enhances the base TerminalApplication class with some facilities
    common to all the oxitop command line utilities - mostly to do with
    progress notifications.
    """

    def __init__(self):
        super(OxiTopApplication, self).__init__(__version__)
        self.dummy_logger = None
        self.data_logger = None
        self.progress_visible = False
        self.parser.set_defaults(
            port='COM1' if sys.platform.startswith('win') else '/dev/ttyUSB0',
            timeout=3,
            )
        self.parser.add_option(
            '-p', '--port', dest='port', action='store',
            help='specify the port which the OxiTop Data Logger is connected '
            'to. This will be something like /dev/ttyUSB0 on Linux or COM1 '
            'on Windows. Default: %default')
        self.parser.add_option(
            '-t', '--timeout', dest='timeout', action='store',
            help='specify the number of seconds to wait for data from the '
            'serial port. Default: %default')

    def __call__(self, args=None):
        try:
            return super(OxiTopApplication, self).__call__(args)
        finally:
            if self.dummy_logger:
                self.dummy_logger.terminated = True
            if self.data_logger:
                self.data_logger.close()

    def handle(self, exc_type, exc_value, exc_trace):
        "Global application exception handler"
        if issubclass(exc_type, (serial.SerialException, LoggerError)):
            # Extend simple exception handling to serial exceptions
            logging.critical(str(exc_value))
            return 1
        else:
            return super(OxiTopApplication, self).handle(
                    exc_type, exc_value, exc_trace)

    def print_table(self, lines, header_lines=1, footer_lines=0):
        """
        Routine for pretty-printing a text table.

        `lines` : a sequence of tuples representing a row of values in the table
        `header_lines` : number of lines at the start that are headers
        `footer_lines` : number of lines at the end that are footers
        """
        lines = list(lines)
        # Calculate the maximum length of each field
        columns = max(len(line) for line in lines)
        lengths = [
            max(len(line[column])
                if column < len(line) else 0 for line in lines)
            for column in range(columns)
            ]
        # Insert separators
        if header_lines != 0:
            lines.insert(header_lines, tuple('-' * l for l in lengths))
        if footer_lines != 0:
            lines.insert(-footer_lines, tuple('-' * l for l in lengths))
        # Output the data
        for line in lines:
            print(' '.join('%-*s' % (l, s) for (l, s) in zip(lengths, line)))

    def print_form(self, lines, fmt='{field: <{width}}{value}'):
        """
        Routine for pretty-printing a form of fields.

        `lines` : a sequence of 2-tuples representing each field and its value
        `fmt` : a format string specifying how to lay out each line
        """
        lines = list(lines)
        columns = max(len(line) for line in lines)
        assert columns == 2
        # Calculate the maximum length of each field
        width = max(len(line[0]) for line in lines) + 2
        for (field, value) in lines:
            print(fmt.format(width=width, field=field, value=value))

    progress_spinner = ['\\', '|', '/' ,'-']

    def progress_start(self):
        self.progress_index = 0
        self.progress_update(erase=False)

    def progress_update(self, erase=True):
        if not self.progress_visible:
            return
        if erase:
            sys.stderr.write('\b')
        sys.stderr.write(self.progress_spinner[self.progress_index % len(self.progress_spinner)])
        sys.stderr.flush()
        self.progress_index += 1

    def progress_finish(self):
        if not self.progress_visible:
            return
        sys.stderr.write('\b')

    def main(self, options, args):
        self.progress_visible = (options.loglevel == logging.INFO)
        if options.port == 'TEST':
            data_logger_port, dummy_logger_port = null_modem(
                baudrate=9600, bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE,
                timeout=options.timeout, rtscts=True)
            with io.open(
                    os.path.join(os.path.dirname(__file__),
                        'example.xml'), 'r') as bottles_file:
                bottles_xml = fromstring(bottles_file.read())
            self.dummy_logger = DummyLogger(dummy_logger_port, [
                Bottle.from_xml(tostring(bottle))
                for bottle in bottles_xml.findall('bottle')
                ])
        else:
            data_logger_port = serial.Serial(
                options.port, baudrate=9600, bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE,
                timeout=options.timeout, rtscts=True)
        self.data_logger = DataLogger(data_logger_port, progress=(
            self.progress_start,
            self.progress_update,
            self.progress_finish,
            ))


import configparser, json, crawl, vendormanage, elasticsearch, asyncio

config = configparser.ConfigParser()
config.read('config/elastic.ini')

url = config['elastic']['url']

es = elasticsearch.ElasticSearch(url)
vendors = vendormanage.load_vendors()

index = "vendors/"

endpoint = f"{index}vendor/"

returneddata = asyncio.run(crawl.crawl(f"{url}{index}", 'PUT'))

print(json.loads(returneddata))

for vendor in vendors:
    vendor_endpoint = f"{endpoint}{vendor['uuid']}"
    vendor_returneddata = asyncio.run(es.store(vendor_endpoint, vendor))
    print(json.loads(vendor_returneddata))


# -*- coding: utf-8 -*-

import os
import itertools
import codecs
import re
import datetime
import cairocffi as cairo
import editdistance
import numpy as np
from scipy import ndimage
import pylab

from keras import backend as K
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.layers import Input, Dense, Activation
from keras.layers import Reshape, Lambda
from keras.layers.merge import add, concatenate
from keras.models import Model
from keras.layers.recurrent import GRU
from keras.optimizers import SGD
from keras.utils.data_utils import get_file
from keras.preprocessing import image
import keras.callbacks
from keras.models import load_model
from keras.preprocessing.image import load_img,img_to_array

import matplotlib.pyplot as plt

def ctc_lambda_func(args):
    y_pred, labels, input_length, label_length = args
    # the 2 is critical here since the first couple outputs of the RNN
    # tend to be garbage:
    y_pred = y_pred[:, 2:, :]
    return K.ctc_batch_cost(labels, y_pred, input_length, label_length)

# Reverse translation of numerical classes back to characters
def labels_to_text(labels):
    ret = []
    for c in labels:
        if c == len(alphabet):  # CTC Blank
            ret.append("")
        else:
            ret.append(alphabet[c])
    return "".join(ret)

alphabet = u'abcdefghijklmnopqrstuvwxyz '

absolute_max_string_len = 16
character_count = 28

weight_file = './weights24.h5'
img_w = 128
# Input Parameters
img_h = 64
words_per_epoch = 16000
val_split = 0.2
val_words = int(words_per_epoch * (val_split))

# Network parameters
conv_filters = 16
kernel_size = (3, 3)
pool_size = 2
time_dense_size = 32
rnn_size = 512
minibatch_size = 32

if K.image_data_format() == 'channels_first':
    input_shape = (1, img_w, img_h)
else:
    input_shape = (img_w, img_h, 1)

act = 'relu'
input_data = Input(name='the_input', shape=input_shape, dtype='float32')
inner = Conv2D(conv_filters, kernel_size, padding='same',
               activation=act, kernel_initializer='he_normal',
               name='conv1')(input_data)
inner = MaxPooling2D(pool_size=(pool_size, pool_size), name='max1')(inner)
inner = Conv2D(conv_filters, kernel_size, padding='same',
               activation=act, kernel_initializer='he_normal',
               name='conv2')(inner)
inner = MaxPooling2D(pool_size=(pool_size, pool_size), name='max2')(inner)

conv_to_rnn_dims = (img_w // (pool_size ** 2), (img_h // (pool_size ** 2)) * conv_filters)
inner = Reshape(target_shape=conv_to_rnn_dims, name='reshape')(inner)

# cuts down input size going into RNN:
inner = Dense(time_dense_size, activation=act, name='dense1')(inner)

# Two layers of bidirectional GRUs
# GRU seems to work as well, if not better than LSTM:
gru_1 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal', name='gru1')(inner)
gru_1b = GRU(rnn_size, return_sequences=True, go_backwards=True, kernel_initializer='he_normal', name='gru1_b')(inner)
gru1_merged = add([gru_1, gru_1b])
gru_2 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal', name='gru2')(gru1_merged)
gru_2b = GRU(rnn_size, return_sequences=True, go_backwards=True, kernel_initializer='he_normal', name='gru2_b')(gru1_merged)

# transforms RNN output to character activations:
inner = Dense(character_count, kernel_initializer='he_normal',
              name='dense2')(concatenate([gru_2, gru_2b]))
y_pred = Activation('softmax', name='softmax')(inner)
Model(inputs=input_data, outputs=y_pred).summary()

labels = Input(name='the_labels', shape=[absolute_max_string_len], dtype='float32')
input_length = Input(name='input_length', shape=[1], dtype='int64')
label_length = Input(name='label_length', shape=[1], dtype='int64')
# Keras doesn't currently support loss funcs with extra parameters
# so CTC loss is implemented in a lambda layer
loss_out = Lambda(ctc_lambda_func, output_shape=(1,), name='ctc')([y_pred, labels, input_length, label_length])

# clipnorm seems to speeds up convergence
sgd = SGD(lr=0.02, decay=1e-6, momentum=0.9, nesterov=True, clipnorm=5)

model = Model(inputs=[input_data, labels, input_length, label_length], outputs=loss_out)

# the loss calc occurs elsewhere, so use a dummy lambda func for the loss
model.compile(loss={'ctc': lambda y_true, y_pred: y_pred}, optimizer=sgd)
model.load_weights(weight_file)
# captures output of softmax so we can decode the output during visualization
#test_func = K.function([input_data], [y_pred])

model_p = Model(inputs=input_data, outputs=y_pred)

#model_p = load_model(MODEL_PATH)

def decode_predict_ctc(out, top_paths = 1):
    results = []
    beam_width = 5
    if beam_width < top_paths:
      beam_width = top_paths
    for i in range(top_paths):
      lables = K.get_value(K.ctc_decode(out, input_length=np.ones(out.shape[0])*out.shape[1],
                           greedy=False, beam_width=beam_width, top_paths=top_paths)[0][i])[0]
      text = labels_to_text(lables)
      results.append(text)
    return results

def predit_a_image(a, top_paths = 1):
  c = np.expand_dims(a.T, axis=0)
  net_out_value = model_p.predict(c)
  top_pred_texts = decode_predict_ctc(net_out_value, top_paths)
  return top_pred_texts

def paint_text(text, w, h, rotate=False, ud=False, multi_fonts=False):
    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
    with cairo.Context(surface) as context:
        context.set_source_rgb(1, 1, 1)  # White
        context.paint()
        # this font list works in CentOS 7
        if multi_fonts:
            fonts = [
                'Century Schoolbook', 'Courier', 'STIX',
                'URW Chancery L', 'FreeMono']
            context.select_font_face(
                np.random.choice(fonts),
                cairo.FONT_SLANT_NORMAL,
                np.random.choice([cairo.FONT_WEIGHT_BOLD, cairo.FONT_WEIGHT_NORMAL]))
        else:
            context.select_font_face('Courier',
                                     cairo.FONT_SLANT_NORMAL,
                                     cairo.FONT_WEIGHT_BOLD)
        context.set_font_size(25)
        box = context.text_extents(text)
        border_w_h = (4, 4)
        if box[2] > (w - 2 * border_w_h[1]) or box[3] > (h - 2 * border_w_h[0]):
            raise IOError(('Could not fit string into image.'
                           'Max char count is too large for given image width.'))

        # teach the RNN translational invariance by
        # fitting text box randomly on canvas, with some room to rotate
        max_shift_x = w - box[2] - border_w_h[0]
        max_shift_y = h - box[3] - border_w_h[1]
        top_left_x = np.random.randint(0, int(max_shift_x))
        if ud:
            top_left_y = np.random.randint(0, int(max_shift_y))
        else:
            top_left_y = h // 2
        context.move_to(top_left_x - int(box[0]), top_left_y - int(box[1]))
        context.set_source_rgb(0, 0, 0)
        context.show_text(text)

    buf = surface.get_data()
    a = np.frombuffer(buf, np.uint8)
    a.shape = (h, w, 4)
    a = a[:, :, 0]  # grab single channel
    a = a.astype(np.float32) / 255
    a = np.expand_dims(a, 0)
    if rotate:
        a = image.random_rotation(a, 3 * (w - top_left_x) / w + 1)
    #a = speckle(a)

    return a

h = 64
w = 128
a = paint_text('a game',h = h, w = w)
b = a.reshape((h, w))
#plt.imshow(b, cmap='Greys_r')
#plt.show()
print(a.shape)
c = np.expand_dims(a.T, axis=0)
#print(c)

net_out_value = model_p.predict(c)
pred_texts = decode_predict_ctc(net_out_value)
print(pred_texts)

img = load_img("test2.png", target_size=(h,w))

#plt.imshow(img, cmap='Greys_r')
#plt.show()
c = img_to_array(img)
print(c.shape)
c = c / 255.0
c = c[:, :, 0]  # grab single channel
c = np.expand_dims(c, 0)
print(c.shape)
c = np.expand_dims(c.T, axis=0)
#print(c)

net_out_value = model_p.predict(c)
pred_texts = decode_predict_ctc(net_out_value)
print(pred_texts)

#predit_a_image(a, top_paths = 3)

#plt.imshow(net_out_value[0].T, cmap='binary', interpolation='nearest')
#plt.show()

#K.get_value(K.ctc_decode(net_out_value, input_length=np.ones(net_out_value.shape[0])*net_out_value.shape[1],
#                         greedy=False, beam_width=3, top_paths=3)[0][0])

#K.ctc_decode(net_out_value, input_length=np.ones(net_out_value.shape[0])*net_out_value.shape[1],
#                         greedy=False, beam_width=5, top_paths=3)

#K.get_value(K.ctc_decode(net_out_value, input_length=np.ones(net_out_value.shape[0])*net_out_value.shape[1],
#                         greedy=False, beam_width=3, top_paths=3)[0][0])


from typing import Dict, List
from caverna import *
import ai

def play_round(game: Game, state: Game.State, controllers: Dict[Player, Controller]) -> None:
    # Take actions while any player has dwarfs
    while game.work_phase(state):
        player = game.current_player(state)
        action = controllers[player].select_action(game, state)
        print("{} selects {}".format(player, action.name))
        state = game.take(action, state)

    # Return dwarfs
    game.return_dwarfs(state)
    game.harvest(state)
    return state

def main():
    samuel = Player('Samuel')
    maria = Player('Maria')
    controllers = {samuel: ai.MinMax(samuel), maria: ai.Random(maria)}
    game = Game([samuel, maria])
    state = game.initial()
    while not game.over(state):
        print("Round {}".format(state.round))
        state = play_round(game, state, controllers)

    for player in game.players:
        name = player
        score = game.score(state, player)
        print("{name}: {score}".format(name=name, score=score))
        print(state.player_states[player].resources)
    print(controllers[samuel].total)

if __name__ == "__main__":
    main()


'''
This file contains methods to perform crud operation of sf instance
'''

# import slaesforce login credentials
import config
from simple_salesforce import Salesforce
import json

class ProductInstance:
    def __init__(self):
        try:
            self.connection = Salesforce(config.username,config.password,config.security_token)
            self.error = None
        except:
            self.connection = None
            self.error = "Invalid Credentials"

    def where(self,field,value):
        desc = self.connection.Product_Instance__c.describe()
        field_names_list = [field['name'] for field in desc['fields']]
        part = {name:None for name in field_names_list}
        field_names = ",".join(field_names_list)
        soql = "SELECT %s FROM Product_Instance__c WHERE %s = '%s'"%(field_names,field,value)
        results = self.connection.query_all(soql)
        self.count = results['totalSize']
        self.records = list()
        for record in results['records']:
            for key in field_names_list:
                part[key] = record[key]
            self.records.append(part)
            print(json.dumps(self.records))

    def getError(self):
        return self.error