import sys
from typing import List
from sklearn.metrics import mean_squared_error
import numpy as np
import cv2
from numpy.linalg import LinAlgError
import matplotlib.pyplot as plt

def myID() -> np.int:
    """
    Return my ID (not the friend's ID I copied from)
    :return: int
    """
    return 207386699

# ---------------------------------------------------------------------------
# ------------------------ Lucas Kanade optical flow ------------------------
# ---------------------------------------------------------------------------

def opticalFlowMatrix(im1: np.ndarray, im2: np.ndarray, step_size: int, win_size: int) -> (np.ndarray, np.ndarray):
    I_t = im2 - im1
    ker = np.array([[-1, 0, 1]])
    xDerivative = cv2.filter2D(im2, -1, ker, borderType=cv2.BORDER_REPLICATE)  # Derivative of X
    yDerivative = cv2.filter2D(im2, -1, ker.T, borderType=cv2.BORDER_REPLICATE)  # Derivative of Y
    maskOnes = np.ones((win_size, win_size))  # Mask of ones

    ixSquared = cv2.filter2D(xDerivative ** 2, -1, maskOnes, borderType=cv2.BORDER_REPLICATE)  # x derivative squared
    iySquared = cv2.filter2D(yDerivative ** 2, -1, maskOnes, borderType=cv2.BORDER_REPLICATE)  # y derivative squared

    ixiy = xDerivative * yDerivative
    sigmaIxiy = cv2.filter2D(ixiy, -1, maskOnes, borderType=cv2.BORDER_REPLICATE)  # Sum of window around ixiy

    ixit = xDerivative * I_t
    sigmaIxit = cv2.filter2D(ixit, -1, maskOnes, borderType=cv2.BORDER_REPLICATE)  # Sum of window around ixit

    iyit = yDerivative * I_t
    sigmaIyit = cv2.filter2D(iyit, -1, maskOnes, borderType=cv2.BORDER_REPLICATE)  # Sum of window around iyit

    v_x = np.zeros(im2.shape)  # Inint of arrays
    v_y = np.zeros(im2.shape)

    for x in range(0, im2.shape[0], step_size):
        for y in range(0, im2.shape[1], step_size):
            try:
                mat1 = np.array([[ixSquared[x][y], sigmaIxiy[x][y]],  # matrix of sums
                                 [sigmaIxiy[x][y], iySquared[x][y]]])

                eig = np.linalg.eig(mat1)  # returns eigenvalues of the matrix
                lambda1 = max(eig[0][0], eig[0][1])  # max eigenvalue
                lambda2 = min(eig[0][0], eig[0][1])  # min eigenvalue

                if lambda1 >= lambda2 > 1 and lambda1 / lambda2 < 100:
                    inverseMat1 = np.linalg.inv(mat1)  # inverse of matrix
                    mat2 = np.array([[-sigmaIxit[x][y]], [-sigmaIyit[x][y]]])
                    result = np.matmul(inverseMat1, mat2)  # matrix multiplication
                    v_x[x][y] = result[0][0]
                    v_y[x][y] = result[1][0]
                else:
                    v_x[x][y] = 0
                    v_y[x][y] = 0
            except Exception:  # If there is no inverse matrix
                v_x[x][y] = 0
                v_y[x][y] = 0
    return v_x, v_y

def opticalFlow(im1: np.ndarray, im2: np.ndarray, step_size=10, win_size=5) -> (np.ndarray, np.ndarray):
    """
    Given two images, returns the Translation from im1 to im2
    :param im1: Image 1
    :param im2: Image 2
    :param step_size: The image sample size
    :param win_size: The optical flow window size (odd number)
    :return: Original points [[x,y]...], [[dU,dV]...] for each points
    """

    if im1.ndim == 2:  # BW Image
        v_x, v_y = opticalFlowMatrix(im1, im2, step_size, win_size)
    else:  # RGB
        v_x, v_y = opticalFlowMatrix(im1[:, :, 0], im2[:, :, 0], step_size, win_size)

    retLstOriginal = []  # construct return lists
    retLstMoved = []
    for x in range(v_x.shape[0]):  # For every element in the returned matrix v_x
        for y in range(v_x.shape[1]):
            if v_x[x][y] != 0 or v_y[x][y] != 0:  # wherever the change is not 0
                retLstOriginal.append([y, x])  # add to lists
                retLstMoved.append([v_x[x][y], v_y[x][y]])

    return np.array(retLstOriginal), np.array(retLstMoved)

def opticalFlowPyrLK(img1: np.ndarray, img2: np.ndarray, k: int, stepSize: int = 10, winSize: int = 5) -> np.ndarray:
    """
    :param img1: First image
    :param img2: Second image
    :param k: Pyramid depth
    :param stepSize: The image sample size
    :param winSize: The optical flow window size (odd number)
    :return: A 3d array, with a shape of (m, n, 2),
    where the first channel holds U, and the second V.
    """

    reducedImg1 = gaussianPyr(img1, k)  # construct pyramids
    reducedImg2 = gaussianPyr(img2, k)

    newUV = np.zeros((img1.shape[0], img1.shape[1], 2))

    for ind in range(k - 1, -1, -1):  # for every level beginning with the smallest
        if img1.ndim == 2:  # BW Image
            dx, dy = opticalFlowMatrix(reducedImg1[ind], reducedImg2[ind], stepSize, winSize)
        else:  # RGB Image
            dx, dy = opticalFlowMatrix(reducedImg1[ind][:, :, 0], reducedImg2[ind][:, :, 0], stepSize, winSize)

        for i in range(dx.shape[0]):  # For every pixel in the returned matrix
            for j in range(dx.shape[1]):
                newUV[i][j][0] = dx[i][j] + (2 * newUV[i][j][0])
                newUV[i][j][1] = dy[i][j] + (2 * newUV[i][j][1])

        # Pad image with 0-es (part of gaussian expand but without the pseudo convolution with 121
        expandedImg = np.zeros((newUV.shape[0] * 2, newUV.shape[1] * 2, newUV.shape[2]))
        for k in range(newUV.shape[2]):
            for i in range(newUV.shape[0]):
                for j in range(newUV.shape[1]):
                    expandedImg[i * 2][j * 2][k] = newUV[i][j][k]  # Fill wherever there was supposed to be a value

        newUV = expandedImg

    newUV = newUV[:img2.shape[0], :img2.shape[1], :]
    return newUV

# ---------------------------------------------------------------------------
# ------------------------ Image Alignment & Warping ------------------------
# ---------------------------------------------------------------------------

def findTranslationLK(im1: np.ndarray, im2: np.ndarray) -> np.ndarray:
    """
    :param im1: image 1 in grayscale format.
    :param im2: image 1 after Translation.
    :return: Translation matrix by LK.
    """

    minMSE = sys.maxsize
    minUV = []
    points, diff = opticalFlow(im1, im2, 10, 5)  # Run LK
    for pointId in range(len(points)):  # for every return point, check the u and v for all image. take the lowest MSE value move
        t = np.array([[1, 0, diff[pointId][0]],  # temp matrix
                      [0, 1, diff[pointId][1]],
                      [0, 0, 1]], dtype=float)
        tempImg2 = cv2.warpPerspective(im1, t, im1.shape[::-1])  # warp image
        currMSE = mean_squared_error(im2, tempImg2)  # calculate MSE
        if currMSE < minMSE:  # Compare MSE
            minMSE = currMSE
            minUV = diff[pointId]
    return np.array([[1, 0, minUV[0]],
                     [0, 1, minUV[1]],
                     [0, 0, 1]], dtype=float)

def optimalAngle(im1: np.ndarray, im2: np.ndarray):
    minT = 0
    t = []
    minMSE = sys.maxsize
    for theta in range(360):  # for every possible angle
        t = np.array([[np.cos(np.deg2rad(theta)), -np.sin(np.deg2rad(theta)), 0],  # temp matrix
                      [np.sin(np.deg2rad(theta)), np.cos(np.deg2rad(theta)), 0],
                      [0, 0, 1]], dtype=float)
        tempImg2 = cv2.warpPerspective(im1, t, im1.shape[::-1])  # warp image
        currMSE = mean_squared_error(im2, tempImg2)
        if currMSE < minMSE:  # update MSE and minT
            minMSE = currMSE
            minT = theta
    im2 = cv2.warpPerspective(im1, t, im1.shape[::-1])
    return minT, im2

def findRigidLK(im1: np.ndarray, im2: np.ndarray) -> np.ndarray:
    """
    :param im1: input image 1 in grayscale format.
    :param im2: image 1 after Rigid.
    :return: Rigid matrix by LK.
    """
    minT, im2 = optimalAngle(im1, im2)  # Find optimal angle
    points, diff = opticalFlow(im1, im2, 10, 5)  # find optical flow
    minMSE = sys.maxsize
    minUV = []
    for pointId in range(len(points)):  # For every point in the return points (same as first sub-question)
        t = np.array([[np.cos(np.deg2rad(minT)), -np.sin(np.deg2rad(minT)), diff[pointId][0]],
                      [np.sin(np.deg2rad(minT)), np.cos(np.deg2rad(minT)), diff[pointId][1]],
                      [0, 0, 1]], dtype=float)
        tempImg2 = cv2.warpPerspective(im1, t, im1.shape[::-1])  # Warp image
        currMSE = mean_squared_error(im2, tempImg2)
        if currMSE < minMSE:  # update minMSE
            minMSE = currMSE
            minUV = diff[pointId]
    return np.array([[np.cos(np.deg2rad(minT)), -np.sin(np.deg2rad(minT)), minUV[0]],
                     [np.sin(np.deg2rad(minT)), np.cos(np.deg2rad(minT)), minUV[1]],
                     [0, 0, 1]], dtype=float)

def findIndents(im2: np.ndarray):
    """
    Function to find the indents of a picture from every side.
    :param im2:
    :return:
    """

    # top Indent
    i = 0
    flag = False
    while not flag:
        if np.all((im2[i] == 0)):
            i += 1
        else:
            flag = True
    topIndent = i

    # Bottom Indent
    i = 1
    flag = False
    while not flag:
        if np.all((im2[-i] == 0)):
            i += 1
        else:
            flag = True
    bottomIndent = -i + 1

    # Left Indent
    i = 0
    flag = False
    while not flag:
        if np.all((im2[:, i] == 0)):
            i += 1
        else:
            flag = True
    leftIndent = -i

    # Right Indent
    i = 1
    flag = False
    while not flag:
        if np.all((im2[:, -i] == 0)):
            i += 1
        else:
            flag = True
    rightIndent = -i + 1

    if topIndent != 0:  # finalize top and bottom indents
        topBotIndent = topIndent
    else:
        topBotIndent = bottomIndent

    if leftIndent != 0:  # finalize left and right indents
        leftRightIndent = leftIndent
    else:
        leftRightIndent = rightIndent

    return topBotIndent, leftRightIndent

def findTranslationCorr(im1: np.ndarray, im2: np.ndarray) -> np.ndarray:
    """
    :param im1: input image 1 in grayscale format.
    :param im2: image 1 after Translation.
    :return: Translation matrix by correlation.
    """

    topBotIndent, leftRightIndent = findIndents(im2)
    t = np.array([[1, 0, leftRightIndent],
                  [0, 1, topBotIndent],
                  [0, 0, 1]], dtype=float)
    return t

def findRigidCorr(im1: np.ndarray, im2: np.ndarray) -> np.ndarray:
    """
    :param im1: input image 1 in grayscale format.
    :param im2: image 1 after Rigid.
    :return: Rigid matrix by correlation.
    """

    minT, im2 = optimalAngle(im1, im2)  # Find optimal angle
    t = findTranslationCorr(im1, im2)  # find optimal translation

    t[0][0] = np.cos(np.deg2rad(minT))  # Update t with the correct angle
    t[0][1] = -np.sin(np.deg2rad(minT))
    t[1][0] = np.sin(np.deg2rad(minT))
    t[1][1] = np.cos(np.deg2rad(minT))

    return t

def warpImages(im1: np.ndarray, im2: np.ndarray, T: np.ndarray) -> np.ndarray:
    """
    :param im1: input image 1 in grayscale format.
    :param im2: input image 2 in grayscale format.
    :param T: is a 3x3 matrix such that each pixel in image 2
    is mapped under homogenous coordinates to image 1 (p2=Tp1).
    :return: warp image 2 according to T and display both image1
    and the wrapped version of the image2 in the same figure.
    """
    newIm1 = np.zeros(im2.shape)
    inverseT = np.linalg.inv(T)  # find inverse of the transformation matrix
    if inverseT[0][1] != 0:  # If there is an angle in the matrix
        deg = np.rad2deg(np.arcsin(inverseT[1][0]))
        rad = np.deg2rad(-deg)
        inverseT[0][0] = np.cos(rad)  # filling with the inverse angle
        inverseT[1][1] = np.cos(rad)
        inverseT[0][1] = -np.sin(rad)
        inverseT[1][0] = np.sin(rad)

    for x in range(im2.shape[0]):  # For every pixel in the image
        for y in range(im2.shape[1]):
            coordinates = np.matmul(inverseT, np.array([[x], [y], [1]]))  # Find ne coordinates
            coordinates = np.round(coordinates)  # Round coordinates to nearest pixel
            try:  # try to add the pixel in the correct place (if indexes don't exist, do nothing)
                if int(coordinates[0][0]) >= 0 and int(coordinates[1][0]) >= 0:
                    newIm1[int(coordinates[0][0])][int(coordinates[1][0])] = im2[x][y]
            except Exception:
                pass

    f, ax = plt.subplots(1, 3)  # Plot the results
    ax[0].imshow(im1)
    ax[0].set_title("Image 1")
    ax[1].imshow(newIm1)
    ax[1].set_title("Supposed Image 1")
    ax[2].imshow(im2)
    ax[2].set_title("Image 2")
    plt.show()

    return newIm1

# ---------------------------------------------------------------------------
# --------------------- Gaussian and Laplacian Pyramids ---------------------
# ---------------------------------------------------------------------------

def getGaussKernel(kSize: int, sigma: float = -1) -> np.ndarray:
    k = cv2.getGaussianKernel(kSize, sigma)  # Getting 1D gaussian kernel
    k /= k[0, 0]  # Restoring original form before normalization due to the original form containing 1 as the first element
    kernel = k @ k.T  # Getting the dot product of the gaussian kernel with itself transposed
    kernel /= kernel.sum()  # Divide by sum to normalize and sum to 1
    return kernel

def gaussianReduce(img: np.ndarray) -> np.ndarray:
    """
    reduce the image: blur the image and take every second pixel
    :param img:
    :return:
    """
    gaussKer = getGaussKernel(5, 1.2)
    if img.ndim == 3:  # RGB image
        ret = np.zeros((img.shape[0] // 2, img.shape[1] // 2, 3))
        for dim in range(3):  # for every channel
            blurredImg = cv2.filter2D(img[:, :, dim], -1, gaussKer, borderType=cv2.BORDER_REPLICATE)  # Blur channel
            for i in range(0, blurredImg.shape[0], 2):
                for j in range(0, blurredImg.shape[1], 2):
                    try:
                        ret[i // 2][j // 2][dim] = blurredImg[i][j]  # Fill new smaller array
                    except Exception:
                        pass

    else:  # BW Image
        blurredImg = cv2.filter2D(img, -1, gaussKer, borderType=cv2.BORDER_REPLICATE)  # Blur Image
        imgAsList = []
        for i in range(0, blurredImg.shape[0], 2):
            row = []  # init list of row
            for j in range(0, blurredImg.shape[1], 2):
                row.append(blurredImg[i][j])  # Fill new smaller array
            imgAsList.append(row)  # Add row to final list
        ret = np.array(imgAsList)  # convert list to array

    return ret

def pseudoConvolve(img: np.ndarray, paddedImg: np.ndarray) -> np.ndarray:
    """
    Helper function of Expand. When image is padded with 0-es, perform a pass over rows and columns and calculate the "in between" values
    :param img:
    :param paddedImg:
    :return:
    """
    # Pseudo-convolve with 1,2,1
    # Over rows
    expandedImg = np.zeros((img.shape[0] * 2, img.shape[1] * 2))
    for i in range(0, paddedImg.shape[0], 2):
        row = [paddedImg[i][0]]
        for j in range(1, paddedImg.shape[1] - 1):
            newVal = 0.5 * (paddedImg[i][j - 1] + (2 * paddedImg[i][j]) + paddedImg[i][j + 1])  # calc new value
            row.append(newVal)  # add value to row
        row.append(paddedImg[i][-1])
        expandedImg[i] = row  # add row to 2d list

    expandedImg = np.array(expandedImg)  # Convert list to  array
    paddedImg = expandedImg.copy()

    # Over columns
    expandedImg = np.zeros((img.shape[1] * 2, img.shape[0] * 2))
    for i in range(paddedImg.shape[1]):
        col = [paddedImg[0][i]]
        for j in range(1, paddedImg.shape[0] - 1):
            newVal = 0.5 * (paddedImg[j - 1][i] + (2 * paddedImg[j][i]) + paddedImg[j + 1][i])  # calc new value
            col.append(newVal)  # add value to col
        col.append(paddedImg[-1][i])  # add col to 2d list
        expandedImg[i] = col
    expandedImg = expandedImg.transpose()  # transpose (convert rows to cols and vice versa)

    expandedImg = np.array(expandedImg)
    expandedImg[-1] = expandedImg[-2]
    expandedImg[:, -1] = expandedImg[:, -2]
    return expandedImg

def gaussianExpand(img: np.ndarray) -> np.ndarray:
    if img.ndim == 2:  # BW Image
        paddedImg = np.zeros((img.shape[0] * 2, img.shape[1] * 2))

        # Pad image with 0-es
        for i in range(img.shape[0]):  # fill with appropriate values where needed
            row = []
            for j in range(img.shape[1]):
                row.append(img[i][j])
                row.append(0)
            paddedImg[i * 2] = row
        expandedImg = pseudoConvolve(img, paddedImg)
    else:  # RGB Image
        # Pad image with 0-es
        expandedImg = np.zeros((img.shape[0] * 2, img.shape[1] * 2, 3))
        for dim in range(3):  # fill with appropriate values where needed
            for i in range(img.shape[0]):
                for j in range(img.shape[1]):
                    expandedImg[i * 2][j * 2][dim] = img[i][j][dim]
            expandedImg[:, :, dim] = pseudoConvolve(img, expandedImg[:, :, dim])

    return expandedImg

def gaussianPyr(img: np.ndarray, levels: int = 4) -> List[np.ndarray]:
    """
    Creates a Gaussian Pyramid
    :param img: Original image
    :param levels: Pyramid depth
    :return: Gaussian pyramid (list of images)
    """
    ret = [img]
    for i in range(1, levels):  # reduce image k times and add all images to list
        ret.append(gaussianReduce(ret[-1]))
    return ret

def laplaceianReduce(img: np.ndarray, levels: int = 4) -> List[np.ndarray]:
    """
    Creates a Laplacian pyramid
    :param img: Original image
    :param levels: Pyramid depth
    :return: Laplacian Pyramid (list of images)
    """
    gaussianPyramid = gaussianPyr(img, levels)
    ret = [gaussianPyramid[-1]]
    for i in range(2, levels + 1):
        currExpand = gaussianExpand(gaussianPyramid[-i + 1])  # expand image
        try:
            laplacianImage = gaussianPyramid[-i] - currExpand  # Use the laplacian property to calculate it
        except Exception:
            temp = gaussianPyramid[-i].copy()
            gaussianPyramid[-i] = gaussianPyramid[-i][:-(temp.shape[0] - currExpand.shape[0]), :-(temp.shape[1] - currExpand.shape[1]), :]
            laplacianImage = gaussianPyramid[-i] - currExpand
        ret.append(laplacianImage)  # add to list
    ret.reverse()
    return ret

def laplaceianExpand(lap_pyr: List[np.ndarray]) -> np.ndarray:
    """
    Restores the original image from a laplacian pyramid
    :param lap_pyr: Laplacian Pyramid
    :return: Original image
    """
    img = lap_pyr[-1]
    for i in range(2, len(lap_pyr) + 1):
        gaussianExpanded = gaussianExpand(img)  # expand image
        try:
            img = gaussianExpanded + lap_pyr[-i]  # Remake the image
        except Exception:  # if gaussian image is bigger that expected
            gaussianExpanded = gaussianExpanded[:-1, :-1, :]
            img = gaussianExpanded + lap_pyr[-i]

    return img

def pyrBlend(img_1: np.ndarray, img_2: np.ndarray, mask: np.ndarray, levels: int) -> (np.ndarray, np.ndarray):
    """
    Blends two images using PyramidBlend method
    :param img_1: Image 1
    :param img_2: Image 2
    :param mask: Blend mask
    :param levels: Pyramid depth
    :return: (Naive blend, Blended Image)
    """
    mask = np.round(mask)
    laplacianImg1 = laplaceianReduce(img_1, levels)
    laplacianImg2 = laplaceianReduce(img_2, levels)
    maskGaussianPyr = gaussianPyr(mask, levels)
    for i in range(len(maskGaussianPyr)):  # For every level in the pyramid
        temp = maskGaussianPyr[i].copy()
        tempLap1 = laplacianImg1[i]
        if temp.shape[0] - tempLap1.shape[0] != 0:
            temp = temp[:-(temp.shape[0] - tempLap1.shape[0]), :, :]
        if temp.shape[1] - tempLap1.shape[1] != 0:
            temp = temp[:, :-(temp.shape[1] - tempLap1.shape[1]), :]
        maskGaussianPyr[i] = temp

    ls = []
    for la, lb, maskPyrInd in zip(laplacianImg1, laplacianImg2, maskGaussianPyr):  # blend the images at every level
        ls.append((la * maskPyrInd) + (lb * (1 - maskPyrInd)))

    expanded = laplaceianExpand(ls)

    # True blend complete

    # Naive blend: Wherever the mask is 1, insert the second picture. In all other places the first image.
    naiveBlend = np.zeros(expanded.shape)
    if img_1.ndim == 3:
        for i in range(expanded.shape[0]):  # For every pixel in the image
            for j in range(expanded.shape[1]):
                for k in range(expanded.shape[2]):
                    try:
                        if mask[i][j][k] == 1:  # Wherever the mask is 1, the new image's pixel value will be placed
                            naiveBlend[i][j][k] = img_1[i][j][k]
                        else:
                            naiveBlend[i][j][k] = img_2[i][j][k]
                    except Exception:
                        naiveBlend[i][j][k] = img_2[i][j][k]
    else:
        for i in range(expanded.shape[0]):  # For every pixel in the image
            for j in range(expanded.shape[1]):
                try:
                    if mask[i][j] == 1:  # Wherever the mask is 1, the new image's pixel value will be placed
                        naiveBlend[i][j] = img_1[i][j]
                    else:
                        naiveBlend[i][j] = img_2[i][j]
                except Exception:
                    naiveBlend[i][j] = img_2[i][j]

    return naiveBlend, expanded


from flask import request, jsonify
from ..model.ItemOrcamento import ItemOrcamento, itemOrcamento_schema, itemOrcamentos_schema
from ..model.ItemOrcamento import db

def cad_itemOrcamento():
    resp = request.get_json()
    nome = resp['nome']
    valor = resp['valor']

    item = ItemOrcamento(nome=nome, valor=valor)

    try:
        db.session.add(item)
        db.session.commit()
        return jsonify({'msg': 'Cadastrado com sucesso'}), 201
    except Exception as e:
        db.session.rollback()
        return jsonify({'msg': 'Erro ao cadastrar ItemOrcamento', 'error': str(e)}), 500

def altera_itemOrcamento(id):
    item = ItemOrcamento.query.get(id)
    if item:
        resp = request.get_json()
        try:
            item.nome = resp['nome']
            item.valor = resp['valor']
            db.session.commit()
            return jsonify({'msg': 'Alterado com sucesso', 'dados': itemOrcamento_schema.dump(item)}), 200
        except Exception as e:
            db.session.rollback()
            return jsonify({'msg': 'Não foi possível alterar', 'dados': {}, 'error': str(e)}), 500
    else:
        return jsonify({'msg': 'Item não encontrado', 'dados': {}, 'error': str(e)}), 404

def excluir_itemOrcamento(id):
    item = ItemOrcamento.query.get(id)
    if item:
        try:
            db.session.delete(item)
            db.session.commit()
            return jsonify({'msg': 'Excluido com sucesso', 'dados': itemOrcamento_schema.dump(item)}), 200
        except Exception as e:
            db.session.rollback()
            return jsonify({'msg': 'Não foi possivel excluir', 'dados': {}, 'error': str(e)}), 500
    else:
        return jsonify({'msg': 'Item não encontrado', 'dados': {}, 'error': str(e)}), 404

def buscar_todos():
    try:
        itens = ItemOrcamento.query.all()
        return jsonify({'msg': 'Busca efetuada com sucesso', 'dados': itemOrcamentos_schema.dump(itens)}), 200
    except Exception as e:
        db.session.rollback()
        return jsonify({'msg': 'Não foi possivel efetuar a busca', 'dados': {}, 'error': str(e)}), 500


import numpy as np
from scipy import signal

def filterButterBP(y, fs, division, rangef):
    """
    Pass-band Butterworth filter, 8th order, central frequencies given by "division"
    y = array to be filtered
    fs = sample rate
    division = 1 for 1/3 octave filtering and = 0 for 1/1 octave filtering
    """
    G = 2.0
    n = 3.0
    nyquistRate = fs / 2

    if division == 1:
        centerFrequency_Hz = np.array([25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315,
                                       400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150,
                                       4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000])
        if rangef:
            centerFrequency_Hz = centerFrequency_Hz[rangef[0]:rangef[1] + 1]

        # Superior and inferior limits of each central band
        factor = np.power(G, 1 / (2 * n))
        lowerCutoffFrequency_Hz = centerFrequency_Hz / factor
        upperCutoffFrequency_Hz = centerFrequency_Hz * factor
        if np.where(centerFrequency_Hz == 20000)[0]:
            upperCutoffFrequency_Hz[np.where(centerFrequency_Hz == 20000)[0]] = 22049
        filteredSignal = np.zeros((len(centerFrequency_Hz), len(y)))
    else:
        centerFrequency_Hz = np.array([31.5, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000])
        if rangef:
            centerFrequency_Hz = centerFrequency_Hz[rangef[0]:rangef[1] + 1]
        # Superior and inferior limits of each central band
        factor = np.sqrt(2)
        lowerCutoffFrequency_Hz = centerFrequency_Hz / factor
        upperCutoffFrequency_Hz = centerFrequency_Hz * factor
        if np.where(centerFrequency_Hz == 16000)[0]:
            upperCutoffFrequency_Hz[np.where(centerFrequency_Hz == 16000)[0]] = 22049
        filteredSignal = np.zeros((len(centerFrequency_Hz), len(y)))

    # Creation and application of the filter
    for lower, upper in zip(lowerCutoffFrequency_Hz, upperCutoffFrequency_Hz):  # IIR Butterworth Filter N-th order
        sos = signal.butter(N=8, Wn=np.array([lower,
                                              upper]) / nyquistRate, btype='bandpass', analog=False,
                            output='sos')

        # w, h = signal.sosfreqz(sos)  # Frequency response of filter

        #        plt.plot(w*(fs/(2*np.pi)), 20 * np.log10(abs(h)), 'b')          # Plot freq response

        index = np.where(lowerCutoffFrequency_Hz == lower)  # Finds index of iteration to store signal
        index = index[0]

        filteredSignal[index, :] = signal.sosfiltfilt(sos, y)  # Filters the input "y" signal

    return filteredSignal, centerFrequency_Hz


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

import json

from pyload.core.utils import parse

from ..base.multi_downloader import MultiDownloader

class LeechThreeHundreedSixtyCom(MultiDownloader):
    __name__ = "LeechThreeHundreedSixtyCom"
    __type__ = "downloader"
    __version__ = "0.01"
    __status__ = "testing"

    __pattern__ = r"^unmatchable$"
    __config__ = [
        ("enabled", "bool", "Activated", True),
        ("use_premium", "bool", "Use premium account if available", True),
        ("fallback", "bool", "Fallback to free download if premium fails", False),
        ("chk_filesize", "bool", "Check file size", True),
        ("max_wait", "int", "Reconnect if waiting time is greater than minutes", 10),
        ("revert_failed", "bool", "Revert to standard download if fails", True),
    ]

    __description__ = """Leech360.com multi-downloader plugin"""
    __license__ = "GPLv3"
    __authors__ = [("GammaC0de", "nitzo2001[AT]yahoo[DOT]com")]

    def handle_premium(self, pyfile):
        json_data = self.load(
            "https://leech360.com/generate",
            get={"token": self.account.info["data"]["token"], "link": pyfile.url},
        )
        api_data = json.loads(json_data)

        if api_data["error"]:
            self.fail(api_data["error_message"])

        pyfile.name = api_data.get("filename", "") or pyfile.name
        pyfile.size = parse.bytesize(api_data.get("message", "0"))
        self.link = api_data["download_url"]


import torch
import torch.nn as nn
import torch.nn.functional as F

from ..common.blocks import Conv2dReLU
from ..base.model import Model
from ..encoders.scse import SCse

class TransposeX2(nn.Module):

    def __init__(self, in_channels, out_channels, use_batchnorm=True, **batchnorm_params):
        super().__init__()
        layers = []
        layers.append(nn.ConvTranspose2d(in_channels, out_channels, kernel_size=4, stride=2, padding=1))
        if use_batchnorm:
            layers.append(nn.BatchNorm2d(out_channels, **batchnorm_params))
        layers.append(nn.ReLU(inplace=True))

        self.block = nn.Sequential(*layers)

    def forward(self, x):
        return self.block(x)

class DecoderBlock(nn.Module):
    def __init__(self, in_channels, out_channels, use_batchnorm=True, se_module=False):
        super().__init__()
        if se_module:
            self.block = nn.Sequential(
                Conv2dReLU(in_channels, in_channels // 4, kernel_size=1, use_batchnorm=use_batchnorm),
                TransposeX2(in_channels // 4, in_channels // 4, use_batchnorm=use_batchnorm),
                Conv2dReLU(in_channels // 4, out_channels, kernel_size=1, use_batchnorm=use_batchnorm),
                SCse(out_channels)
            )
        else:
            self.block = nn.Sequential(
                Conv2dReLU(in_channels, in_channels // 4, kernel_size=1, use_batchnorm=use_batchnorm),
                TransposeX2(in_channels // 4, in_channels // 4, use_batchnorm=use_batchnorm),
                Conv2dReLU(in_channels // 4, out_channels, kernel_size=1, use_batchnorm=use_batchnorm),
            )

    def forward(self, x):
        x, skip = x
        x = self.block(x)
        if skip is not None:
            x = x + skip
        return x

class LinknetDecoder(Model):

    def __init__(
            self,
            encoder_channels,
            prefinal_channels=32,
            final_channels=1,
            use_batchnorm=True,
            se_module=False,
            h_columns=False
    ):
        super().__init__()

        in_channels = encoder_channels
        self.h_columns = h_columns

        self.layer1 = DecoderBlock(in_channels[0], in_channels[1], use_batchnorm=use_batchnorm, se_module=se_module)
        self.layer2 = DecoderBlock(in_channels[1], in_channels[2], use_batchnorm=use_batchnorm, se_module=se_module)
        self.layer3 = DecoderBlock(in_channels[2], in_channels[3], use_batchnorm=use_batchnorm, se_module=se_module)
        self.layer4 = DecoderBlock(in_channels[3], in_channels[4], use_batchnorm=use_batchnorm, se_module=se_module)
        self.layer5 = DecoderBlock(in_channels[4], prefinal_channels, use_batchnorm=use_batchnorm, se_module=se_module)

        if self.h_columns:
            self.layer1_h = nn.Conv2d(in_channels[1], prefinal_channels, kernel_size=(1, 1))
            self.layer2_h = nn.Conv2d(in_channels[2], prefinal_channels, kernel_size=(1, 1))
            self.layer3_h = nn.Conv2d(in_channels[3], prefinal_channels, kernel_size=(1, 1))
            self.layer4_h = nn.Conv2d(in_channels[4], prefinal_channels, kernel_size=(1, 1))
            self.final_conv = nn.Sequential(nn.Conv2d(int(prefinal_channels * 5), 64, kernel_size=3, padding=1),
                                            nn.ELU(True),
                                            nn.Conv2d(64, 1, kernel_size=1, bias=False))
        else:
            self.final_conv = nn.Conv2d(prefinal_channels, final_channels, kernel_size=(1, 1))

        self.initialize()

    def forward(self, x):
        encoder_head = x[0]
        skips = x[1:]

        if self.h_columns:
            d5 = self.layer1([encoder_head, skips[0]])
            d4 = self.layer2([d5, skips[1]])
            d3 = self.layer3([d4, skips[2]])
            d2 = self.layer4([d3, skips[3]])
            d1 = self.layer5([d2, None])
            x = torch.cat((d1,
                           self.layer4_h(F.upsample(d2, scale_factor=2, mode='bilinear', align_corners=True)),
                           self.layer3_h(F.upsample(d3, scale_factor=4, mode='bilinear', align_corners=True)),
                           self.layer2_h(F.upsample(d4, scale_factor=8, mode='bilinear', align_corners=True)),
                           self.layer1_h(F.upsample(d5, scale_factor=16, mode='bilinear', align_corners=True))), 1)
        else:
            x = self.layer1([encoder_head, skips[0]])
            x = self.layer2([x, skips[1]])
            x = self.layer3([x, skips[2]])
            x = self.layer4([x, skips[3]])
            x = self.layer5([x, None])
        x = self.final_conv(x)

        return x


import time
from node_bfs_class import node
import heapq as hq
from PIL import Image
import imageio
import numpy as np
import os

class graph:
    """a container structure for nodes"""

    def __init__(self, map, watch_evolution=True):
        self.map = [x.copy() for x in map]
        self.pixel_array = [[(0,0,0) if val == 'X' else (255,255,255) for val in row] for row in map]
        self.start_node_coords = self.get_start_node_coords()
        self.goal_node_coords = self.get_goal_node_coords()
        self.nodes = {}
        self.active_nodes = {}
        self.watch_evolution = watch_evolution
        self.populate_nodes()

    def get_start_node_coords(self):
        """gets the coordinates of the start node"""
        i = ['S' in row for row in self.map].index(True)
        j = [x == 'S' for x in self.map[i]].index(True)
        return (i, j)

    def get_goal_node_coords(self):
        """gets the coordinates of the goal node"""
        i = ['G' in row for row in self.map].index(True)
        j = [x == 'G' for x in self.map[i]].index(True)
        return (i, j)

    def populate_nodes(self):
        self.nodes = {(i, j): node(self.map,
                      (i, j),
                      (i, j) == self.goal_node_coords,
                      (i, j) == self.start_node_coords)
                      for i in range(len(self.map))
                      for j in range(len(self.map[i]))
                      if self.map[i][j] != 'X'}

    def get_path(self, end):
        ret = []
        n = end
        while self.nodes[n].parent != self.start_node_coords:
            ret.append(n)
            n = self.nodes[n].parent

        ret.append(n)

        return ret[::-1]

    def a_star_search(self):
        """ different approach to A* """
        queue = []
        hq.heappush(queue, (0, self.start_node_coords))

        self.nodes[self.start_node_coords].set_distance_to_goal(self.goal_node_coords)
        self.nodes[self.start_node_coords].set_path_length(0.)

        while queue:
            tup = hq.heappop(queue)
            n = self.nodes[tup[1]]

            if n.loc == self.goal_node_coords:
                self.draw_path(self.get_path(n.loc))
                #self.draw_path_to_image(self.get_path(n.loc), final_path=True)
                return True

            if not n.is_active:
                n.activate()
                n.set_distance_to_start(self.start_node_coords)
                n.set_distance_to_goal(self.goal_node_coords)

            for neighbor in n.neighbors:
                dist = n.path_length + n.distance_to_node(neighbor)
                if dist < self.nodes[neighbor].path_length:
                    self.nodes[neighbor].set_parent(n.loc)
                    self.nodes[neighbor].set_distance_to_goal(self.goal_node_coords)
                    self.nodes[neighbor].set_path_length(dist)
                    if (self.nodes[neighbor].cost, neighbor) not in queue:
                        hq.heappush(queue, (self.nodes[neighbor].cost, neighbor))

            if self.watch_evolution and queue:
                self.draw_path([x[1] for x in queue], reset=True, symb='Q')
                time.sleep(0.005)

            #if queue and len(queue) > 1:
            #    self.draw_path_to_image(queue, final_path=False)

        return False

    def draw_path(self, path, reset=False, symb="P"):
        """draws a path, and resets the board if necessary"""
        changed_coords = []
        snc = self.start_node_coords
        gnc = self.goal_node_coords
        self.map[snc[0]][snc[1]] = "\033[93mS\033[0m"
        self.map[gnc[0]][gnc[1]] = "\033[93mG\033[0m"

        for coord in path:
            if coord != snc and coord != gnc:
                self.map[coord[0]][coord[1]] = "\033[93m"+symb+"\033[0m"
                changed_coords.append(coord)

        if reset:
            print('\n'.join([''.join(row) for row in self.map]))
            print(flush=True)
            for coord in changed_coords:
                if coord != snc and coord != gnc:
                    self.map[coord[0]][coord[1]] = '.'

    def draw_path_to_image(self, path, final_path=False):
        """ creates images pixel by pixel """
        if final_path:
            adjusted_priorities = [ self.nodes[x].path_length * 255 / self.nodes[self.goal_node_coords].path_length for x in path]
            for i,x in enumerate(path):
                self.pixel_array[x[0]][x[1]] = (0 + adjusted_priorities[i], 0, 255 - adjusted_priorities[i])
        else:
            min_priority = min([x[0] for x in path])
            adjusted_priorities = [x[0]-(min_priority+1) for x in path]
            adjusted_priorities = [x * 255 / max(adjusted_priorities) for x in adjusted_priorities]
            for i,x in enumerate(path):
                self.pixel_array[x[1][0]][x[1][1]] = (255-adjusted_priorities[i], 0, 0+adjusted_priorities[i])

        snc = self.start_node_coords
        gnc = self.goal_node_coords
        self.pixel_array[snc[0]][snc[1]] = (0, 0, 255)
        self.pixel_array[gnc[0]][gnc[1]] = (255, 0, 0)

        pixel_array = np.array(self.pixel_array, np.uint8)
        new_image = Image.fromarray(pixel_array)
        new_image.save(f'example/queue_status_{time.time()}.png')

        if not final_path:
            for i,x in enumerate(path):
                self.pixel_array[x[1][0]][x[1][1]] = (255, 255, 255)

    def create_gif(self):
        """ creates a gif of all the most recently created pngs """
        filenames = [x for x in os.listdir('example') if x.endswith('.png')]
        filenames.sort()
        filenames = filenames[::20] + [filenames[-1] for i in range(240)] #drop frames
        images = list(map(lambda f: imageio.imread('example/'+f), filenames))
        imageio.mimsave(os.path.join('example/a_star_search.gif'), images, format='GIF', fps=120)
        #os.


#https://leetcode.com/problems/spiral-matrix/
#Given an m x n matrix, return all elements of the matrix in spiral order.

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        if not matrix:
            return []
        m,n = len(matrix),len(matrix[0])
        res = []
        left,right,top,bottom = 0,n-1,0,m-1
        while left<=right and top<=bottom:
            for i in range(left,right+1):
                res.append(matrix[top][i])
            for i in range(top+1,bottom+1):
                res.append(matrix[i][right])
            if left<right and top<bottom:
                for i in range(right-1,left,-1):
                    res.append(matrix[bottom][i])
                for i in range(bottom,top,-1):
                    res.append(matrix[i][left])
            left,right,top,bottom = left+1,right-1,top+1,bottom-1
        return res


import arxiv
# max_results = float('inf'),
search = arxiv.Search(
  query = "Word Embedding in Bangla",
  id_list = [],
  max_results = 10,
  sort_by = arxiv.SortCriterion.Relevance,
  sort_order = arxiv.SortOrder.Descending
)
for result in search.results():
    print(result.title)
    result.download_pdf()


import os,sys
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

import pickle
from baselines import logger
import ppo
import gym
from train_ppo import *

import globals
from time import *
from utils.plotting_performance import *
from utils.utils import *

from brs_engine.DubinsCar_brs_engine import *
from brs_engine.PlanarQuad_brs_engine import *

# ranges: [(1,10), (20,30), (40,50), ..]
def gen_plots(args, ranges):
    if args['loadpath'] == None:
        raise ValueError("Loading path is None!!")
    if args['gym_env'] == "PlanarQuadEnv-v0":
        lo = [-5, 0, -np.pi]
        hi = [5, 10, np.pi]
        bins = [100, 100, 100]
    elif args['gym_env'] == "DubinsCarEnv-v0":
        lo = [-5, -5, 0]
        hi = [5, 5, np.pi * 2]
        bins = [100, 100, 100]
    else:
        raise ValueError("env's name is invalid!!")

    full_dir = args['loadpath']+'/heatmap'
    num_files = len([x for x in os.listdir(full_dir)])
    # print("num files:", num_files)

    # fig = plt.figure()

    num_fig = len(ranges)
    fig, axes = plt.subplots(2, num_fig)
    print(axes)
    for rg in ranges:
        if len(rg) != 2 or rg[0] > rg[1] or rg[0] > num_files or rg[1] > num_files:
            raise ValueError("Please provide valid range series!!")
        else:
            rg_lo = rg[0]
            rg_hi = rg[1]
            all_obs_cur_rg = []
            all_dones_cur_rg = []
            for id in range(rg_lo, rg_hi):
                with open(full_dir + '/iter_' + str(id) + '.pkl', 'rb') as f:
                    seg_info = pickle.load(f)
                # obs: np.array([[_,_,_],
                #                [_,_,_]])
                all_obs_cur_rg.append(seg_info['ob'])
            all_obs_cur_rg_concat = np.concatenate(all_obs_cur_rg, axis=0)
            if args['gym_env'] == "PlanarQuadEnv-v0":
                x = all_obs_cur_rg_concat[:, 0]
                z = all_obs_cur_rg_concat[:, 2]
                theta = all_obs_cur_rg_concat[:,4]
                # ax = fig.add_subplot(1,num_fig,1+ranges.index(rg), adjustable='box', aspect=1.0)

                # ax.set_xlabel("X position")
                # ax.set_ylabel("Z position")
                # ax.hist2d(x, z, bins=min(bins[:2]), range=[[-5, 5], [0, 10]])
                # ax.hist2d(x, z, bins=min(bins[:2]))

                # theta as 1d hist, xz as 2d hist
                axes[0, ranges.index(rg)].set(adjustable='box', aspect='equal')
                axes[0, ranges.index(rg)].hist(theta, bins=bins[2], density=True, facecolor='g', alpha=0.75, log=True)
                axes[1, ranges.index(rg)].set(adjustable='box', aspect='equal')
                axes[1, ranges.index(rg)].hist2d(x, z, range=[[-5,5],[0,10]], bins=min(bins[:2]), cmap='tab20')

                # x as 1d hist, z&theta as 2d hist
                # axes[0, ranges.index(rg)].set(adjustable='box', aspect='equal')
                # axes[0, ranges.index(rg)].hist(x, bins=bins[0], density=True, facecolor='g', alpha=0.75, log=True)
                # axes[1, ranges.index(rg)].set(adjustable='box', aspect='equal')
                # axes[1, ranges.index(rg)].hist2d(z,theta, range=[[0,10],[-np.pi,np.pi]], bins=min(bins[1:]), cmap='tab20')

                # z as 1d hist, x&theta as 2d hist
                # axes[0, ranges.index(rg)].set(adjustable='box', aspect='equal')
                # axes[0, ranges.index(rg)].hist(z, bins=bins[1], density=True, facecolor='g', alpha=0.75, log=True)
                # axes[1, ranges.index(rg)].set(adjustable='box', aspect='equal')
                # axes[1, ranges.index(rg)].hist2d(x, theta, range=[[-5, 5], [-np.pi, np.pi]], bins=bins[0],
                #                                  cmap='tab20')
            elif args['gym_env'] == "DubinsCarEnv-v0":
                xy = all_obs_cur_rg_concat[:, :2]
                ax = fig.add_subplot(1,num_fig,1+ranges.index(rg))
                ax.set_xlabel('X position')
                ax.set_ylabel('Y position')
                ax.hist2d(xy[:, 0], xy[:, 1], bins=min(bins[:2]))
            else:
                ValueError("env's name is invalid!!")
    # for ax in axes:
    #     ax.set_xlabel('Common x-label')
    #     ax.set_ylabel('Common y-label')
    # fig.add_subplot(111, frameon=False)
    # plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
    # plt.xlabel('Common x-label')
    # plt.ylabel('Common z-label')
    fig.tight_layout()
    plt.show()

    plt.savefig(args['loadpath'] + '/test3_xy.png')
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--gym_env", help="which gym environment to use.", type=str, default='PlanarQuadEnv-v0')
    parser.add_argument("--loadpath", help="the path you load for generating heatmaps", type=str, default=None)
    args = parser.parse_args()
    args = vars(args)

    # gen_plots(args, [(1,20), (20,40), (40,60), (60,80), (80,100), (100,120)])
    gen_plots(args, [(1,20), (20,40), (40,60)])

# -----------  this part below is for generating obs during training for building heatmaps later ---------------------
# globals.init()
# def gen_iter_obs(args):
#     assert args['gym_env'] == 'DubinsCarEnv-v0' or args['gym_env'] == 'PlanarQuadEnv-v0'
#     logger.configure(dir=args['RUN_DIR'])
#     globals.g_hm_dirpath = args['HEATMAP_DIR']
#     globals.g_iter_id = 0
#
#     env = gym.make(args['gym_env'])
#     env.reward_type = args['reward_type']
#     env.set_additional_goal = args['set_additional_goal']
#     logger.record_tabular("algo", args['algo'])
#     logger.record_tabular("env", args['gym_env'])
#     logger.record_tabular("env.set_additional_goal", args['set_additional_goal'])
#     logger.record_tabular("env.reward_type", args['reward_type'])
#
#     logger.dump_tabular()
#
#
#     # Initialize brs engine. You also have to call reset_variables() after instance initialization
#     if args['reward_type'] == 'ttr':
#         if args['gym_env'] == 'DubinsCarEnv-v0':
#             brsEngine = DubinsCar_brs_engine()
#             brsEngine.reset_variables()
#         elif args['gym_env'] == 'PlanarQuadEnv-v0':
#             brsEngine = Quadrotor_brs_engine()
#             brsEngine.reset_variables()
#         else:
#             raise ValueError("invalid environment name for ttr reward!")
#         # You have to assign the engine
#         env.brsEngine = brsEngine
#     elif args['reward_type']  in ['hand_craft', 'distance', 'distance_lambda_10', 'distance_lambda_1',
#                               'distance_lambda_0.1']:
#         pass
#     else:
#         raise ValueError("wrong type of reward")
#     ppo_params_json = os.environ['PROJ_HOME'] + '/ppo_params.json'
#
#     # Start to train the policy
#     trained_policy = train(env=env, algorithm=ppo, params=ppo_params_json, args=args, save_obs=True)
#     trained_policy.save_model(args['MODEL_DIR'])
#
#     return True
#
# if __name__ == "__main__":
#     with tf.device('/gpu:1'):
#         # ----- path setting ------
#         parser = argparse.ArgumentParser()
#         parser.add_argument("--gym_env", help="which gym environment to use.", type=str, default='PlanarQuadEnv-v0')
#         parser.add_argument("--reward_type", help="which type of reward to use.", type=str, default='ttr')
#         parser.add_argument("--algo", help="which type of algorithm to use.", type=str, default='ppo')
#         parser.add_argument("--set_additional_goal", type=str, default="angle")
#         args = parser.parse_args()
#         args = vars(args)
#
#         RUN_DIR = os.path.join(os.getcwd(), 'heatmaps_icra',
#                                args['gym_env']+ '_' + args['reward_type'] + '_' + args['algo'] + '_' + strftime(
#                                    '%d-%b-%Y_%H-%M-%S'))
#         MODEL_DIR = os.path.join(RUN_DIR, 'model')
#         FIGURE_DIR = os.path.join(RUN_DIR, 'figure')
#         RESULT_DIR = os.path.join(RUN_DIR, 'result')
#         HEATMAP_DIR = os.path.join(RUN_DIR, 'heatmap')
#
#         maybe_mkdir(RUN_DIR)
#         maybe_mkdir(MODEL_DIR)
#         maybe_mkdir(FIGURE_DIR)
#         maybe_mkdir(RESULT_DIR)
#         maybe_mkdir(HEATMAP_DIR)
#
#         args['RUN_DIR'] = RUN_DIR
#         args['MODEL_DIR'] = MODEL_DIR
#         args['FIGURE_DIR'] = FIGURE_DIR
#         args['RESULT_DIR'] = RESULT_DIR
#         args['HEATMAP_DIR'] = HEATMAP_DIR
#         gen_iter_obs(args)


from PySide.QtGui import QImage
import numpy as np

def qimage_to_numpy(qimage):
    """ Returns an RGB array given any image. """

    # Each row in a bitmap is stored in the size of multiples of 4
    # If there are less number of bits in a row, it is padded with 0s
    # a 32 bit format ensures that we can get away with dealing with padding
    qimage = qimage.convertToFormat(QImage.Format_ARGB32)
    w, h = qimage.width(), qimage.height()
    string = qimage.bits()

    arr = np.fromstring(bytes(string), dtype=np.uint8)
    arr = arr.reshape(h, w, 4)
    arr = arr[..., 0:3]

    c0 = arr[..., 0].copy()
    c2 = arr[..., 2].copy()

    arr[..., 0] = c2
    arr[..., 2] = c0
    return arr

def numpy_to_qimage(array):
    """ Returns QImage from an RGB array ."""

    rows, cols, channels = array.shape
    array4 = np.zeros((rows, cols, 4), dtype=np.uint8)
    array4[..., 0:3] = array
    array4[..., 3] = 255

    c0 = array[..., 0].copy()
    c2 = array[..., 2].copy()

    array4[..., 0] = c2
    array4[..., 2] = c0

    string = array4.tostring()

    img = QImage(string, cols, rows, QImage.Format_ARGB32)
    # On windows, img `img` holds a reference to `string` and behaves wrongly
    # when the string goes out of scope.
    return img.copy()

def rgb_to_gray(arr):
    """ Converts an RGB numpy array to grayscale. """

    r = arr[:, :, 0].astype(np.float)
    g = arr[:, :, 1].astype(np.float)
    b = arr[:, :, 2].astype(np.float)

    garr = 0.299*r + 0.587*g + 0.114*b
    return garr.astype(np.uint8)

def gray_to_rgb(arr):
    """ Converts intensity array to RGB. """

    return np.dstack([arr, arr, arr])

def rgb_to_yuv(arr):
    "Converts RGB array to YUV."

    r = arr[:, :, 0].astype(np.float)
    g = arr[:, :, 1].astype(np.float)
    b = arr[:, :, 2].astype(np.float)

    y = r*.299000 + g*.587000 + b*.114000
    u = r*-.168736 + g*-.331264 + b*.500000 + 128
    v = r*.500000 + g*-.418688 + b*-.081312 + 128

    ret = np.dstack([y, u, v])
    ret = np.clip(ret, 0, 255)
    return ret.astype(np.uint8)

def yuv_to_rgb(arr):
    "Converts YUV array to RGB"

    y = arr[:, :, 0].astype(np.float)
    u = arr[:, :, 1].astype(np.float)
    v = arr[:, :, 2].astype(np.float)

    r = y + 1.4075 * (v - 128)
    g = y - 0.3455 * (u - 128) - (0.7169 * (v - 128))
    b = y + 1.7790 * (u - 128)

    ret = np.dstack([r, g, b])
    ret = np.clip(ret, 0, 255)
    return ret.astype(np.uint8)

def fft_to_qimage(arr):
    """Converts frquency spectrum magnitude image to displayable qimage.

    To make the image visually conceivable, we take the log of the array.
    Otherwise, there is just too much difference between the maxima and minima.
    """

    magnitutde_log = np.log(1 + arr)  # To avoid computing log(0).
    mn = magnitutde_log.min()
    mx = magnitutde_log.max()
    norm_img = 255*(magnitutde_log - mn)/(mx - mn)
    norm_img = norm_img.astype(np.uint8)
    rgb_image = gray_to_rgb(norm_img)
    return numpy_to_qimage(rgb_image)


import pathlib
from typing import List

import cv2
import h5py
import numpy as np
import typer

def split_text(txt: List[np.bytes_]) -> List[List[str]]:
    """テキストを文字単位に分割

    Args:
        txt (List[np.bytes_]): テキスト文字列の配列

    Returns:
        List[List[str]]: テキスト文字列の文字単位に分割した配列
    """

    txt = [i.decode('utf-8') for i in txt]

    splitted_text = []

    for words in txt:
        for word in words.split('\n'):
            splitted_char = []
            for char in list(word):
                if char != ' ':
                    splitted_char.append(char)
            splitted_text.append(splitted_char)

    return splitted_text

def convert_bbox(imgaug_bboxes):
    all_bbox = []

    for i in imgaug_bboxes.bounding_boxes:
        bbox = i.to_keypoints()
        all_bbox.append(np.array([
            [bbox[0].x, bbox[0].y],
            [bbox[1].x, bbox[1].y],
            [bbox[2].x, bbox[2].y],
            [bbox[3].x, bbox[3].y]
        ]))

    return np.stack(all_bbox)

def main(file_path: str = 'src/dataset/SynthText.h5', saved_path: str = 'src/dataset/synthtext'):

    pathlib.Path(saved_path).mkdir(exist_ok=True)
    pathlib.Path(f'{saved_path}/char_bbox').mkdir(exist_ok=True)
    pathlib.Path(f'{saved_path}/image').mkdir(exist_ok=True)
    pathlib.Path(f'{saved_path}/text').mkdir(exist_ok=True)

    with h5py.File(file_path, 'r') as db:

        dataset = sorted(db['data'].keys())

        for image_name in dataset:

            image = db['data'][image_name][...]
            char_bbox = db['data'][image_name].attrs['charBB']
            txt = db['data'][image_name].attrs['txt']

            splitted_text = split_text(txt)

            char_bbox = char_bbox.transpose(2, 1, 0)

            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

            cv2.imwrite(f"{saved_path}/image/{image_name}", image)
            np.save(f"{saved_path}/char_bbox/{image_name.replace('.jpg', '')}.npy", char_bbox)
            with open(f"{saved_path}/text/{image_name.replace('.jpg', '')}.txt", 'w') as f:
                for x in splitted_text:
                    f.write(str(x) + "\n")

if __name__ == "__main__":
    typer.run(main)


"""Download Anime"""

# For USERGE-X
# (C) 2021 All Rights Reserved
# Idea by: [TG: @Lostb053]
# Author: (github.com/code-rgb) [TG: @DeletedUser420]

import asyncio
from urllib.parse import quote

from bs4 import BeautifulSoup as soup
from pyrogram import filters
from pyrogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup

from userge import Config, Message, userge
from userge.utils import check_owner, get_response, rand_key

GOGO = "https://gogoanime.so"
GOGO_DB = {}

@userge.on_cmd("gogo", about={"header": "Gogo Plugin Help"})
async def gogo_h(message: Message):
    if not userge.has_bot:
        await message.err("You Need to create a bot via Bot Father", del_in=5)
        return
    link_ = "https://t.me/{}?start=inline".format((await userge.bot.get_me()).username)
    if message.client.is_bot:
        await userge.bot.send_message(
            message.chat.id,
            text="To know how to use this plugin,\nClick the button below",
            reply_markup=InlineKeyboardMarkup(
                [[InlineKeyboardButton("See Help for Anime", url=link_)]]
            ),
        )
        await message.delete()
    else:
        await message.edit(
            "[**See Help for Anime**]({})".format(link_), disable_web_page_preview=True
        )

class Anime:
    @staticmethod
    async def _get_html(link: str, add_pre: bool = True):
        if add_pre:
            link = GOGO + link
        content = await get_response.text(link)
        return soup(content, "lxml")

    @staticmethod
    async def search(query: str):
        page = await Anime._get_html("/search.html?keyword=" + quote(query))
        out = []
        for i in page.find("ul", {"class": "items"}).findAll("li"):
            result_ = i.find("p", {"class": "name"})
            title = result_.a.text.strip()
            release = i.find("p", {"class": "released"}).text.strip()
            result_url = GOGO + result_.a.get("href")
            image = i.div.a.img.get("src")
            k_ = image.rsplit("/", 1)
            if len(k_) == 2:
                k_[1] = quote(k_[1])
            key_ = rand_key()
            body_ = f"[\u200c]({'/'.join(k_)})**{title}**\n{release}"
            GOGO_DB[key_] = {"url": result_url, "body": body_}
            out.append(
                {
                    "key": key_,
                    "title": title,
                    "release": release,
                    "image": "/".join(k_),
                }
            )
        return out

    @staticmethod
    async def get_eps(link: str):
        page = await Anime._get_html(link, add_pre=False)
        end_ = page.find("ul", {"id": "episode_page"}).findAll("li")[-1].a.get("ep_end")
        return end_

    @staticmethod
    def _get_name(link: str):
        name_ = "/" + (link.rsplit("/", 1))[1]
        return name_

    @staticmethod
    async def get_quality(url: str, episode: int, key_: str):
        endpoint = f"{Anime._get_name(url)}-episode-{episode}"
        page_ = await Anime._get_html(endpoint)
        link_ = page_.find("li", {"class": "dowloads"}).a.get("href")
        # get qualities from download page
        page = await Anime._get_html(link_, add_pre=False)
        btn_, row_ = [], []
        for i in page.findAll("div", {"class": "dowload"}):
            qual = i.a
            if qual.get("target") != "_blank":
                name = qual.text.replace("Download", "").strip()
                btn_.append(InlineKeyboardButton(name, url=qual.get("href")))
                if len(btn_) == 2:
                    row_.append(btn_)
                    btn_ = []
        if len(btn_) != 0:
            row_.append(btn_)
        row_.append(
            [InlineKeyboardButton("Back", callback_data=f"get_currentpg{key_}")]
        )
        return InlineKeyboardMarkup(row_)

if userge.has_bot:

    @userge.bot.on_callback_query(filters.regex(pattern="get_eps(.*)"))
    @check_owner
    async def get_eps_from_key(c_q: CallbackQuery):
        key_ = c_q.matches[0].group(1)
        url_ = GOGO_DB.get(key_)
        if not url_:
            return await c_q.answer("Not Found")
        url_ = url_.get("url")
        await c_q.answer()
        res = await Anime.get_eps(url_)
        btn_, row_, paginate = [], [], []
        for i in range(1, int(res) + 1):
            btn_.append(
                InlineKeyboardButton(
                    "EP " + str(i), callback_data=f"gogo_get_qual{key_}_{i}"
                )
            )
            if len(btn_) == 4:
                row_.append(btn_)
                btn_ = []
            if len(row_) == 7:
                paginate.append(row_)
                row_ = []
        if len(btn_) != 0:
            row_.append(btn_)
        if len(row_) != 0:
            paginate.append(row_)
        GOGO_DB[key_]["page"] = paginate
        p_len = len(paginate)
        if p_len > 1:
            paginate[0].append(
                [
                    InlineKeyboardButton(
                        "1 / " + str(p_len), callback_data=f"gogo_page{key_}_0"
                    ),
                    InlineKeyboardButton("Next", callback_data=f"gogo_next{key_}_0"),
                ]
            )
        GOGO_DB[key_]["current_pg"] = paginate[0]
        await c_q.edit_message_reply_markup(
            reply_markup=InlineKeyboardMarkup(paginate[0])
        )

    @userge.bot.on_callback_query(
        filters.regex(pattern=r"gogo_get_qual([a-z0-9]+)_([\d]+)")
    )
    @check_owner
    async def get_qual_from_eps(c_q: CallbackQuery):
        key_ = c_q.matches[0].group(1)
        episode = int(c_q.matches[0].group(2))
        key_data = GOGO_DB.get(key_)
        if not key_data:
            return await c_q.answer("Not Found")
        url_ = key_data.get("url")
        await c_q.answer()
        await c_q.edit_message_text(
            text=f"{key_data.get('body')}\n**[  Episode: {episode}  ]**\n\n📹 __Choose the desired video quality from below.__\n**Note:** for uploading to TG:\n`{Config.CMD_TRIGGER}upload [link] | [filename].mp4`",
            reply_markup=(
                await Anime.get_quality(url=url_, episode=episode, key_=key_)
            ),
        )

    @userge.bot.on_callback_query(
        filters.regex(pattern=r"gogo_(page|next|back)([a-z0-9]+)_([\d]+)")
    )
    @check_owner
    async def gogo_paginate(c_q: CallbackQuery):
        direction = c_q.matches[0].group(1)
        key_ = c_q.matches[0].group(2)
        pos = int(c_q.matches[0].group(3))
        key_data = GOGO_DB.get(key_)
        if not key_data:
            return await c_q.answer("Not Found")
        await c_q.answer()
        pages = key_data.get("page")
        p_len = len(pages)
        del_back, del_next = False, False
        if direction == "next":
            page = pos + 1
            del_next = (page + 1) == p_len
        elif direction == "back":
            del_back = pos == 1
            page = pos - 1
        else:
            return
        button_base = [
            InlineKeyboardButton("❮  Back", callback_data=f"gogo_back{key_}_{page}"),
            InlineKeyboardButton(
                f"{page + 1} / {p_len}",
                callback_data=f"gogo_page{key_}_{page}",
            ),
            InlineKeyboardButton("Next  ❯", callback_data=f"gogo_next{key_}_{page}"),
        ]
        if del_back:
            button_base.pop(0)
        if del_next:
            button_base.pop()
        # Work Around for multiple nav buttons
        # idk why "pages" is acting as a global variable
        # So safe to check if nav buttons already exists
        if "gogo_get_qual" in pages[page][-1][-1].callback_data:
            pages[page].append(button_base)
        else:
            pages[page][-1] = button_base
        GOGO_DB[key_]["current_pg"] = pages[page]
        await c_q.edit_message_reply_markup(
            reply_markup=InlineKeyboardMarkup(pages[page])
        )
        await asyncio.sleep(0.3)

    @userge.bot.on_callback_query(filters.regex(pattern=r"get_currentpg(.*)"))
    @check_owner
    async def get_current_pg(c_q: CallbackQuery):
        key_ = c_q.matches[0].group(1)
        data_ = GOGO_DB.get(key_)
        if not data_:
            return await c_q.answer("Not Found")
        await c_q.answer()
        mrkp = data_.get("current_pg")
        body_ = data_.get("body")
        await c_q.edit_message_text(text=body_, reply_markup=InlineKeyboardMarkup(mrkp))


from abc import ABC, abstractmethod

# Base class for GNSS data sources
class GNSSDataSource(ABC):
    @abstractmethod
    def read_data(self):
        pass

# Derived class for GNSS data from UART
class UARTDataSource(GNSSDataSource):
    def read_data(self):
        # Reading data from UART
        data = []
        return data

# Derived class for GNSS data from USB
class USBDataSource(GNSSDataSource):
    def read_data(self):
        # Reading data from USB
        data = []
        return data

# Derived class for GNSS data from a File
class FileDataSource(GNSSDataSource):
    def __init__(self, filename):
        self.filename = filename

    def read_data(self):
        try:
            with open(self.filename, 'r') as file:
                data = []
                for line in file:
                    # Parse and add data from the file
                    values = line.strip().split(',')
                    if len(values) == 5:
                        data.append([float(value) for value in values])
                return data
        except FileNotFoundError:
            print("File not found.")
            return []

# Factory class for creating GNSS data sources
class GNSSDataSourceFactory:
    @staticmethod
    def create_data_source(source_type, source_parameter=None):
        if source_type == "UART":
            return UARTDataSource()
        elif source_type == "USB":
            return USBDataSource()
        elif source_type == "File" and source_parameter is not None:
            return FileDataSource(source_parameter)
        else:
            raise ValueError("Invalid data source type")

# Example usage:
if __name__ == "__main__":
    data_source_type = "File"  # Change this to "UART" or "USB" for other source types
    file_path = "gnss_data.txt"  # Specify the file path if using "File" source

    try:
        data_source = GNSSDataSourceFactory.create_data_source(data_source_type, file_path)
        gnss_data = data_source.read_data()
        if not gnss_data:
            print("No GNSS data available.")
        else:
            print("GNSS data:", gnss_data)
    except ValueError as e:
        print(e)


from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains

PATH = r"C:\Program Files (x86)\chromedriver.exe"  # Path of the selenium driver
s = Service(executable_path=PATH)
driver = webdriver.Chrome(service=s)  # Every selenium file needs above lines (For my computer :D)

driver.get("https://orteil.dashnet.org/cookieclicker/")  # URL of the game

driver.implicitly_wait(5)

the_cookie = driver.find_element(By.ID, "bigCookie")
cookie_counter = driver.find_element(By.ID, "cookies")

items_to_buy = [driver.find_element(By.ID, "productPrice1"), driver.find_element(By.ID, "productPrice0")]  # Items can be modified

for i in range(1000):
    actions = ActionChains(driver)
    actions.click(the_cookie)
    actions.perform()

    count = int(cookie_counter.text.split(" ")[0])

    for item_to_buy in items_to_buy:
        price = int(item_to_buy.text)

        buy_action = ActionChains(driver)  # Action buffer
        buy_action.click(item_to_buy)  # click to buy

        if count >= price:
            buy_action.perform()  # Buy the upgrade!


class Solution:
  def reverseString(self, s) -> None:
    """
    Do not return anything, modify s in-place instead.
    """
    temp = s[::-1]
    for i in range(len(s)):
      s[i] = temp[i]

example = Solution()
example.reverseString(["h", "e", "l", "l", "o"])


import unittest
from tests.testing_utils import mocked_requests_post
from transcript_manager import FD_RTT
from unittest import mock

# Our test case class
class TranscriptManager(unittest.TestCase):

    def setUp(self):
        self.fd_rtt = FD_RTT({
              "url": "https://www.youtube.com/watch?v=KWMqeJiIiMo&ab_channel=EpicEconomist",
              "exit_on_video": False,
            }, {})

    @mock.patch('requests.post', side_effect=mocked_requests_post)
    def test_get_youtube_meta_data(self, mock_send_discord_msg):
        self.fd_rtt.send_metadata_embed({"id": "KWMqeJiIiMo"}, {"format_id": "mp4", "url": "https://www.youtube.com/watch?v=KWMqeJiIiMo&ab_channel=EpicEconomist", "is_livestream": False})
        mock_send_discord_msg.assert_called_once()

    # process video can be done with test transcribe
    # @mock.patch('requests.post', side_effect=mocked_requests_post)
    # def test_process_video(self, mock_request):
    #     self.fd_rtt.process_video("https://www.youtube.com/watch?v=u88EmaRrIlU&ab_channel=BNNBloomberg")
    #     self.assertTrue(True)

    @mock.patch('requests.post', side_effect=mocked_requests_post)
    def test_get_channel(self, mock_request):
        channelName = self.fd_rtt.get_channel_from_name()
        self.assertEqual(channelName, "Epic Economist")

if __name__ == '__main__':
    unittest.main()


#!/usr/bin/env python

# This work is licensed under the terms of the MIT license.
# For a copy, see <https://opensource.org/licenses/MIT>.

"""
Module used to parse all the route and scenario configuration parameters.
"""

import json
import math
import xml.etree.ElementTree as ET
import sys

import carla
from agents.navigation.local_planner import RoadOption
from srunner.scenarioconfigs.route_scenario_configuration import RouteScenarioConfiguration

# TODO  check this threshold, it could be a bit larger but not so large that we cluster scenarios.
TRIGGER_THRESHOLD = 2.0  # Threshold to say if a trigger position is new or repeated, works for matching positions
TRIGGER_ANGLE_THRESHOLD = 10  # Threshold to say if two angles can be considering matching when matching transforms.

class RouteParser(object):

    """
    Pure static class used to parse all the route and scenario configuration parameters.
    """

    @staticmethod
    def parse_annotations_file(annotation_filename):  # world_annotations = RouteParser.parse_annotations_file(config.scenario_file)  # scenario file is the JSON file # return final_dict  # the file has a current maps name that is an one element vec
        """
        Return the annotations of which positions where the scenarios are going to happen.
        :param annotation_filename: the filename for the anotations file
        :return:
        """

        with open(annotation_filename, 'r') as f:  # The JSON file -> scenario_file -> srunner/data/all_towns_traffic_scenarios1_3_4.json
            annotation_dict = json.loads(f.read())
            #print(annotation_filename)  # srunner/data/all_towns_traffic_scenarios1_3_4.json

        final_dict = {}

        for town_dict in annotation_dict['available_scenarios']:
            final_dict.update(town_dict)

        #print(final_dict)

        #print(next(iter(final_dict)))  # prints Town01

        #for keys in final_dict.keys():
            #print(keys)
            '''
            prints
            Town01
            Town02
            Town03
            Town04
            Town05
            Town06
            '''
        '''
        for key, value in final_dict.items():
            print(key)  # All town numbers printed before all the array values of each town were printed.
            print(f"{key}-value are as {value}")  #Town01-value are as, then all the values of that object array, i.e., te available event configurations, transforms, other vehicle transforms, left right front transforms.
        '''

        return final_dict  # the file has a current maps name that is an one element vec

    @staticmethod
    def parse_routes_file(route_filename, scenario_file, single_route=None):
        """
        Returns a list of route elements.
        :param route_filename: the path to a set of routes.
        :param single_route: If set, only this route shall be returned
        :return: List of dicts containing the waypoints, id and town of the routes
        """

        list_route_descriptions = []
        tree = ET.parse(route_filename)  # srunner/data/routes_debug.xml
        for route in tree.iter("route"):  # <route id="0" town="Town02">

            route_id = route.attrib['id']  # <route id="0" town="Town02">
            if single_route and route_id != single_route:  #  single_route = 0, route_id = 0.
                continue

            # For route id = 0 we doing this below: <route id="0" town="Town02">

            new_config = RouteScenarioConfiguration()  # RouteScenarioConfiguration/scenario config object
            new_config.town = route.attrib['town']  # s/o ScenarioConfiguration
            new_config.name = "RouteScenario_{}".format(route_id)  # RouteScenario_0
            new_config.weather = RouteParser.parse_weather(route)  # route -> <route id="0" town="Town02">
            new_config.scenario_file = scenario_file  # The JSON file -> scenario_file -> srunner/data/all_towns_traffic_scenarios1_3_4.json

            waypoint_list = []  # the list of waypoints that can be found on this route  # NAAAAAAAAICCCCCCCCCCCCEEEEEEEEEE
             # For route id = 0 we doing this below: <route id="0" town="Town02">
            for waypoint in route.iter('waypoint'):   # For route id = 0 we doing this below: <route id="0" town="Town02"> ,
            # and now iterating through the route for waypoints!!! and adding them in the order they come, I can even draw them here :3
            # go through all the points in the route!!!!!!!!!!

                waypoint_list.append(carla.Location(x=float(waypoint.attrib['x']),  # converting the waypoints to a valid cala location here and appending that carla location to the waypoint list variable!
                                                    y=float(waypoint.attrib['y']),
                                                    z=float(waypoint.attrib['z'])))

            new_config.trajectory = waypoint_list  # added them (all the waypoint carla locations) to the trajectory variable of the scenario/route configuration object!

            list_route_descriptions.append(new_config)  # only one route cx of one route ID. This list has only one object hence.

        return list_route_descriptions

    @staticmethod
    def parse_weather(route):  # set weather, might NEED it later!
        """
        Returns a carla.WeatherParameters with the corresponding weather for that route. If the route
        has no weather attribute, the default one is triggered.
        """

        route_weather = route.find("weather")
        if route_weather is None:

            weather = carla.WeatherParameters(sun_altitude_angle=70)

        else:
            weather = carla.WeatherParameters()
            for weather_attrib in route.iter("weather"):

                if 'cloudiness' in weather_attrib.attrib:  # child.tag, child.attrib
                    weather.cloudiness = float(weather_attrib.attrib['cloudiness'])  # pass those decimal values
                if 'precipitation' in weather_attrib.attrib:
                    weather.precipitation = float(weather_attrib.attrib['precipitation'])
                if 'precipitation_deposits' in weather_attrib.attrib:
                    weather.precipitation_deposits = float(weather_attrib.attrib['precipitation_deposits'])
                if 'wind_intensity' in weather_attrib.attrib:
                    weather.wind_intensity = float(weather_attrib.attrib['wind_intensity'])
                if 'sun_azimuth_angle' in weather_attrib.attrib:
                    weather.sun_azimuth_angle = float(weather_attrib.attrib['sun_azimuth_angle'])
                if 'sun_altitude_angle' in weather_attrib.attrib:
                    weather.sun_altitude_angle = float(weather_attrib.attrib['sun_altitude_angle'])
                if 'wetness' in weather_attrib.attrib:
                    weather.wetness = float(weather_attrib.attrib['wetness'])
                if 'fog_distance' in weather_attrib.attrib:
                    weather.fog_distance = float(weather_attrib.attrib['fog_distance'])
                if 'fog_density' in weather_attrib.attrib:
                    weather.fog_density = float(weather_attrib.attrib['fog_density'])

                    '''
                    <route id="0" town="Town01">
                       <weather
                          cloudiness="0"
                          precipitation="0"
                          precipitation_deposits="0"
                          wind_intensity="0"
                          sun_azimuth_angle="0"
                          sun_altitude_angle="70"
                          fog_density="0"
                          fog_distance="0"
                          wetness="0"
                       />

                    '''

        return weather

    @staticmethod
    def check_trigger_position(new_trigger, existing_triggers):  # trigger_id = RouteParser.check_trigger_position(waypoint, existent_triggers)  # waypoint is the trigger transform  that has matched with a waypoint inside our route traj  # existent_triggers is empty for our first iteration
        """
        Check if this trigger position already exists or if it is a new one.
        :param new_trigger:
        :param existing_triggers:
        :return:
        """

        for trigger_id in existing_triggers.keys():
            trigger = existing_triggers[trigger_id]
            dx = trigger['x'] - new_trigger['x']
            dy = trigger['y'] - new_trigger['y']
            distance = math.sqrt(dx * dx + dy * dy)

            dyaw = (trigger['yaw'] - new_trigger['yaw']) % 360

            # TRIGGER_THRESHOLD = 2.0
            # TRIGGER_ANGLE_THRESHOLD = 10

            if distance < TRIGGER_THRESHOLD \
                    and (dyaw < TRIGGER_ANGLE_THRESHOLD or dyaw > (360 - TRIGGER_ANGLE_THRESHOLD)):
                return trigger_id  # if the triger is already in the existing_triggers, it will return the trigger_id else it will return none i.e., implying that there are none matches xP

        return None

    @staticmethod
    def convert_waypoint_float(waypoint):
        """
        Convert waypoint values to float
        """
        waypoint['x'] = float(waypoint['x'])
        waypoint['y'] = float(waypoint['y'])
        waypoint['z'] = float(waypoint['z'])
        waypoint['yaw'] = float(waypoint['yaw'])

    @staticmethod
    def match_world_location_to_route(world_location, route_description):  # (waypoint, trajectory)  # waypoint is the trigger transform and traj is our route
        """
        We match this location to a given route.
            world_location:
            route_description:
        """
        def match_waypoints(waypoint1, wtransform):
            """
            Check if waypoint1 and wtransform are similar
            """

            #print("shiz")  # shiz
            #sys.exit("off")

            dx = float(waypoint1['x']) - wtransform.location.x  # transform x - route x
            dy = float(waypoint1['y']) - wtransform.location.y
            dz = float(waypoint1['z']) - wtransform.location.z
            dpos = math.sqrt(dx * dx + dy * dy + dz * dz)

            dyaw = (float(waypoint1['yaw']) - wtransform.rotation.yaw) % 360

            # TRIGGER_THRESHOLD = 2.0
            # TRIGGER_ANGLE_THRESHOLD = 10

            return dpos < TRIGGER_THRESHOLD \
                and (dyaw < TRIGGER_ANGLE_THRESHOLD or dyaw > (360 - TRIGGER_ANGLE_THRESHOLD))

        # *********** outer function starts here merroooo ***************

        match_position = 0
        # TODO this function can be optimized to run on Log(N) time
        for route_waypoint in route_description:  # in route
            if match_waypoints(world_location, route_waypoint[0]):  # trigger point and route waypoint

                # If above statement is true, it means our trigger point is on a certain waypoint inside our defined densly populated route
                # and the trigger yaw is the same as that particular waypoint's yaw within certain thresholds

                return match_position  # this match position is the index of the waypoint inside our route array for which our particular transform has matched with.
            match_position += 1

        return None

    @staticmethod
    def get_scenario_type(scenario, match_position, trajectory):  #scenario_subtype = RouteParser.get_scenario_type(scenario_name, match_position, trajectory)  # # "scenario_type": "Scenario1", i.e., all scenarios  # this match position is the index of the waypoint inside our route array  # traj is our densly populated route

        """
        Some scenarios have different types depending on the route.
        :param scenario: the scenario name
        :param match_position: the matching position for the scenarion
        :param trajectory: the route trajectory the ego is following
        :return: tag representing this subtype

        Also used to check which are not viable (Such as an scenario
        that triggers when turning but the route doesnt')
        WARNING: These tags are used at:
            - VehicleTurningRoute
            - SignalJunctionCrossingRoute
        and changes to these tags will affect them
        """

        def check_this_waypoint(tuple_wp_turn):
            """
            Decides whether or not the waypoint will define the scenario behavior
            """
            if RoadOption.LANEFOLLOW == tuple_wp_turn[1]:
                return False
            elif RoadOption.CHANGELANELEFT == tuple_wp_turn[1]:
                return False
            elif RoadOption.CHANGELANERIGHT == tuple_wp_turn[1]:
                return False
            return True

        '''

        VOID = -1
        LEFT = 1
        RIGHT = 2
        STRAIGHT = 3
        LANEFOLLOW = 4
        CHANGELANELEFT = 5
        CHANGELANERIGHT = 6

        '''

        # Unused tag for the rest of scenarios,
        # can't be None as they are still valid scenarios
        subtype = 'valid'

        # We have till "scenario_type": "Scenario4" in Town02 in the all towns 1 3 4 JSON file and other actors only in scenario 4 and I guess after scenario 4
        # and I noticed we have scenario 1, 3, 4 in all towns 1 3 4 JSON file, go figure.

        if scenario == 'Scenario4':
            for tuple_wp_turn in trajectory[match_position:]:  # the trigger point that matched with our route at the match position
                if check_this_waypoint(tuple_wp_turn):  # check if the RoadOption of that matched waypoint is a left or right, tag them as S4left, S4right, if neither then tag them as none.
                    if RoadOption.LEFT == tuple_wp_turn[1]:
                        subtype = 'S4left'
                    elif RoadOption.RIGHT == tuple_wp_turn[1]:
                        subtype = 'S4right'
                    else:
                        subtype = None
                    break  # Avoid checking all of them
                subtype = None

        if scenario == 'Scenario7':  # for scenario 7 we have the RoadOption STRAIGHT also available. Tag it as S7opposite
            for tuple_wp_turn in trajectory[match_position:]:
                if check_this_waypoint(tuple_wp_turn):
                    if RoadOption.LEFT == tuple_wp_turn[1]:
                        subtype = 'S7left'
                    elif RoadOption.RIGHT == tuple_wp_turn[1]:
                        subtype = 'S7right'
                    elif RoadOption.STRAIGHT == tuple_wp_turn[1]:
                        subtype = 'S7opposite'
                    else:
                        subtype = None
                    break  # Avoid checking all of them
                subtype = None

        if scenario == 'Scenario8':  # for S8 we only have left
            for tuple_wp_turn in trajectory[match_position:]:
                if check_this_waypoint(tuple_wp_turn):
                    if RoadOption.LEFT == tuple_wp_turn[1]:
                        subtype = 'S8left'
                    else:
                        subtype = None
                    break  # Avoid checking all of them
                subtype = None

        if scenario == 'Scenario9':  # for S9 we only have right
            for tuple_wp_turn in trajectory[match_position:]:
                if check_this_waypoint(tuple_wp_turn):
                    if RoadOption.RIGHT == tuple_wp_turn[1]:
                        subtype = 'S9right'
                    else:
                        subtype = None
                    break  # Avoid checking all of them
                subtype = None

        return subtype

    @staticmethod
    def scan_route_for_scenarios(route_name, trajectory, world_annotations):  # potential_scenarios_definitions, _ = RouteParser.scan_route_for_scenarios(config.town, route, world_annotations)  # Town02  # route as defined in the method above  # world_annotations is the dictionary of the JSON file with town names as keys for each scenario and each key returns the annotations of which positions where the scenarios are going to happen.
        """
        Just returns a plain list of possible scenarios that can happen in this route by matching
        the locations from the scenario into the route description

        :return:  A list of scenario definitions with their correspondent parameters
        """

        # the triggers dictionaries:
        existent_triggers = {}
        # We have a table of IDs and trigger positions associated  # ID meaning -> "scenario_type": "Scenario1"??
        possible_scenarios = {}

        # Keep track of the trigger ids being added
        latest_trigger_id = 0

        for town_name in world_annotations.keys():
            if town_name != route_name:
                continue
                # for the selected town

            scenarios = world_annotations[town_name]  # many possible scenarios mentioned like "scenario_type": "Scenario1"??

            #print(scenarios)  # all scenarios with all their transforms and everything was printed. The Dynamic Language.
            #sys.exit("heff")

            for scenario in scenarios:  # For each existent scenario
                if "scenario_type" not in scenario:  # if a block of code does not have "scenario_type": "Scenario1 or X"
                # if the JSON file does not have a scenario_type object key I guess
                    break  # heff this loop then
                scenario_name = scenario["scenario_type"]  # "scenario_type": "Scenario1"

                #print(scenario_name)  # Scenario1
                #sys.exit("heff")

                for event in scenario["available_event_configurations"]:  # for each scenario there is "available_event_configurations": [ in which there are numerous transforms and other vehicles and front and side and back transforms etc
                    waypoint = event['transform']  # trigger point of this scenario  # just noticed in some JSON available_event_configurations we have other vehicles with left or right sub fields which have front left &/or right locs and a transform underneath them as a trigger point I presume
                    RouteParser.convert_waypoint_float(waypoint)
                    # We match trigger point to the  route, now we need to check if the route affects
                    match_position = RouteParser.match_world_location_to_route(
                        waypoint, trajectory)  # waypoint is the trigger transform and traj is our route

                    # return match_position  # this match position is the index of the waypoint inside our route array for which our particular transform has matched with.

                    if match_position is not None:
                        # We match a location for this scenario, create a scenario object so this scenario
                        # can be instantiated later

                        if 'other_actors' in event:
                            other_vehicles = event['other_actors']  # all the other_actors with front, left, right locations in this
                        else:
                            other_vehicles = None
                        scenario_subtype = RouteParser.get_scenario_type(scenario_name, match_position,  # # "scenario_type": "Scenario1"  # this match position is the index of the waypoint inside our route array  # traj is our densly populated route
                                                                         trajectory)

                        # return subtype i.e., S4left, S4right, S8left, S9right, S7opposite (RoadOption = straight) etc

                        if scenario_subtype is None:
                            continue  # heff it
                        scenario_description = {
                            'name': scenario_name,  # all the scenarios we are parsing
                            'other_actors': other_vehicles,  # all the transforms inside the other vehicles i.e., left right front
                            'trigger_position': waypoint,  # waypoint is the trigger transform  that has matched with a waypoint inside our route traj
                            'scenario_type': scenario_subtype,  # scenario_name = scenario["scenario_type"]  # "scenario_type": "Scenario1"  # some scenarios have route dependent configs, S4left, etc
                        }

                        trigger_id = RouteParser.check_trigger_position(waypoint, existent_triggers)  # waypoint is the trigger transform  that has matched with a waypoint inside our route traj  # existent_triggers is empty for our first iteration

                        #return trigger_id  # if the triger is already in the existing_triggers, it will return the trigger_id else it will return none i.e., implying that there are none matches xP

                        if trigger_id is None:
                            # This trigger does not exist create a new reference on existent triggers
                            existent_triggers.update({latest_trigger_id: waypoint})  # latest_trigger_id = 0 initially
                            # Update a reference for this trigger on the possible scenarios

                            possible_scenarios.update({latest_trigger_id: []})  # latest_trigger_id = 0 initially

                            trigger_id = latest_trigger_id  # so for first trigger that was not already in the dictionary existent_triggers, is initially give an id of 0 for the initial very first one

                            # Increment the latest trigger
                            latest_trigger_id += 1  # until we catch the next trigger id cx it is not included in our existent_triggers dictionary yet

                        possible_scenarios[trigger_id].append(scenario_description)  # so we have a whole scenario for that particular trigger location as defined above and we do these for all the trigger points defined to create all these scenarios

                        # so now for the trigger id on which we caught the trigger not being matched, we append the possible scenarios dictionary on that particular id wiht the whole scenario description dictionary

                        '''
                        scenario_description = {
                            'name': scenario_name,  # all the scenarios we are parsing
                            'other_actors': other_vehicles,  # all the transforms inside the other vehicles i.e., left right front
                            'trigger_position': waypoint,  # waypoint is the trigger transform  that has matched with a waypoint inside our route traj
                            'scenario_type': scenario_subtype,  # scenario_name = scenario["scenario_type"]  # "scenario_type": "Scenario1"  # some scenarios have route dependent configs, S4left, etc
                        }
                        '''

        return possible_scenarios, existent_triggers  # existent_triggers have all the trigger points inside the, and possible scenarios have the complete description of those scenarios from all towns 1 3 4 JSOn file compared to our route