import math
import pygame
from math import pi,sin,cos,tan,sqrt,atan,degrees,radians
from time import sleep
from _momentum_sliders import *
def momentum_run():
    #-=-=-=-=Impulse and momnetum =-=-=-=-#
    '''
    Do not type anything for the variable we need to solve for

    ##Example:

    >>> acceleration:
    >>> Force1: 2
    >>> Force2: 2
    >>> Mass: 1.3

    3.08 is the acceleration
    '''
    WIDTH, HEIGHT = 900, 650
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
    GREY = (200, 200, 200)      #----------------------------------------------------------------------------------------------# Colors
    ORANGE = (200, 100, 50)
    RED = (255,0,0)
    GREEN = (0,255,0)
    TRANS = (1, 1, 1)
    WHITE = (255,255,255)

    center=(int(WIDTH/2),int(HEIGHT/2))
    full_screen_radius=sqrt((WIDTH/2)**2+(HEIGHT/2)**2)#---------------------------------------------------------------# Screen setting

    font = pygame.font.SysFont("Arial",int(16))
    font2 = pygame.font.SysFont("Arial",int(21))#--------------------------------------------------------------------------# Font setting

    choose_image = pygame.image.load('_momentum_choose_image.png')
    twoD_click = pygame.image.load('_momentum_2d_click.png')
    oneD_click = pygame.image.load('_momentum_1d_click.png')
    up=pygame.transform.scale(pygame.image.load("_momentum_arrow_up.png").convert_alpha(),(35,35))
    down=pygame.image.load("_momentum_arrow_down.png")
    down=down.convert_alpha()
    down=pygame.transform.scale(down,(35,35))#-----------------------------------------------------------------------# Images imported
    right=pygame.image.load("_momentum_arrow_right.png")
    right=right.convert_alpha()
    right=pygame.transform.scale(right,(35,35))
    left=pygame.image.load("_momentum_arrow_left.png")
    left=left.convert_alpha()
    left=pygame.transform.scale(left,(35,35))

    def ratio_conversion(lst):#----------------------------------------------------------------------------------------------# Function that change the speed of the ball to have noticeable velocity difference
        v_max=max([abs(i) for i in lst if i!=0])
        v_min=min([abs(i) for i in lst if i!=0])                                                                                # Velocity maximum and minimum are chosen from a list of variables
        if v_max>5:
            ratio=4/v_max                                                                                # If the max velocity if bigger than 5, then find the ratio between the value and 5
            for i in range(len(lst)):
                lst[i]= lst[i]*ratio
        elif v_min<0.7:
            ratio=0.7/v_min                                                                                # If the min velocity is smaller than 0.7, then find the ration between the two values
            for i in range(len(lst)):
                lst[i]= lst[i]*ratio                                                                                # Apply the obtained ratio and apply it to all variables in the list imported
        return lst

    def initial_pos(v1x,v1y,v2x,v2y,ang1,ang2):#------------------------------------------------------------------------# Function that always center the location of collision while inputted variables are reasonable
        ang1=radians(ang1)+pi
        ang2=radians(ang2)+pi                                                                                # The angle formed with the horizontal at which the balls will collide in the center
        if v1y==0 and v1x!=0:
            if v1x>0:
                ang1=2*pi+pi
            elif v1x<0:
                ang1=2*pi
        if v2y==0 and v2x!=0:                                                                                # Six if statements to check special cases which can cause multiple possibilities
            if v2x>0:
                ang2=2*pi+pi
            elif v2x<0:
                ang2=2*pi

        if v1x==0 and v1y!=0:
            if v1y>0:
                ang1=3*pi/2+pi
            elif v1y<0:                                                                                # Explanation: if any vector has 0, meaning the combined vector is only going vertical or horizontal
                ang1=pi/2+pi                                                                                # Since trignomitry is used, multiple answer can be abstracted vertical or horizontal angles
        if v2x== 0 and v2y!=0:                                                                                # Therefore, each case must be studied separately
            if v2y>0:
                ang2=3*pi/2+pi
            elif v2y<0:
                ang2=pi/2+pi

        if v1y==0 and v1x==0:
            ang1=0
        if v2y==0 and v2x==0:
            ang2=0

        v1=sqrt(v1x**2+v1y**2)                                                                                # Find the combined vector
        v2=sqrt(v2x**2+v2y**2)

        ratio=v1/v2                                                                                # Find the ratio between the combined vectors
        d1=full_screen_radius*ratio                                                                                # A distance defined by using the corner of screen and the screen center, and the ratio of two velocities
        d2=full_screen_radius
        d1x,d1y=d1*cos(ang1),d1*sin(ang1)                                                                                # separate the component into x and y so it can be used in drawing functions
        d2x,d2y=d2*cos(ang2),d2*sin(ang2)
        return d1x,d1y,d2x,d2y

    def legend(screen,variables):#------------------------------------------------------------------------------------# Function that print message on pygame screen
        myfont = pygame.font.SysFont('Arial', 19)
        font = pygame.font.SysFont('Arial', 23)
        m1_text = myfont.render('Mass 1: '+str(round(variables['mass1'],2))+'kg', False, WHITE)
        screen.blit(m1_text,(45, 10))
        v1_text = myfont.render('Velocity 1: '+str(round(variables['vel1'],2))+'m/s', False, WHITE)
        screen.blit(v1_text,(45, 30))
        m2_text = myfont.render('Mass 2: '+str(round(variables['mass2'],2))+'kg', False, WHITE)
        screen.blit(m2_text,(45, 50))
        v2_text = myfont.render('Velocity 2: '+str(round(variables['vel2'],2))+'m/s', False, WHITE)
        screen.blit(v2_text,(45, 70))
        if int(variables['collision_type'])==1 or int(variables['collision_type'])==2:
            v1f_text = myfont.render('Velocity 1 prime: '+str(round(variables['vel1f'],2))+'m/s', False, WHITE) # WIth different type of collision, different message will be printed
            screen.blit(v1f_text,(45, 90))
            v2f_text = myfont.render('Velocity 2 prime: '+str(round(variables['vel2f'],2))+'m/s', False, WHITE)
            screen.blit(v2f_text,(45, 110))
        else:
            vf_text = myfont.render('Velocity prime: '+str(round(variables['velf'],2))+'m/s', False, WHITE)
            screen.blit(vf_text,(45, 90))
                                                                                # If a velocity value is much more than the others, message will be shown
        if abs(variables['vel1'])>abs(10*variables['vel2']) or abs(variables['vel2'])>abs(10*variables['vel1']):
            print(variables['vel1'],variables['vel2'])
            DIfference_txt = font.render('Difference between veolcities are too big', False, RED)
            screen.blit(DIfference_txt,(300, 400))

    def distance(x,y,x2,y2):#-------------------------------------------------------------------------------------------# Distance function
        return (round(sqrt((x2-x)**2+(y2-y)**2)))

    #Momentum in one dimention
    def ask_for_input(lst,collision_type):#-----------------------------------------------------------------------------# Function that collect inputted data , rearrange data and return the data along with the variable to be solved
        collision_type=collision_type
        vel1= lst[0]
        mass1 =  lst[1]                                                                                # Values are abstracted from the imported list
        vel2= lst[2]
        mass2 = lst[3]
        if int(collision_type) ==1 or int(collision_type) ==2:                                                                      # Different type of collision can lead to different final values
            vel1f= lst[4]
            vel2f= lst[5]
            variables={'collision_type':collision_type,'mass1':mass1,'vel1':vel1,'mass2':mass2,'vel2':vel2,'vel1f':vel1f,'vel2f':vel2f}
        if int(collision_type)==3:
            velf = lst[4]
            variables={'collision_type':collision_type,'mass1':mass1,'vel1':vel1,'mass2':mass2,'vel2':vel2,'velf':velf}# All the values are arranged into a dictionary
        count_of_empty=0
        for i in variables:
            if len(variables.get(i))==0:                                                                                # If any value of dictionary keys is empty string, then it will become the one to be solved
                solve=i
                count_of_empty+=1
            else:                                                                                # At the same time, all string values are change into float to allow calculations
                variables[i]=float(variables.get(i))
        if count_of_empty>1 and collision_type!=1:
            return 'ERROR: Too many empty variables'
        if collision_type==1 and count_of_empty>2:                                                                                # If there are too many missing variables or none, then a message will be returned
            return 'ERROR: Too many empty variables'
        if count_of_empty==0:
            return 'EXCEPT: All variables found'
        return variables,solve

    def find_missing_momentum1D(variables,solve):#--------------------------------------------------------------# Function that take the values and what is to be solved, to calculate the missing value
        conservation_violated=False
        negative_mass=False                                                                                # Two unrealistic cases: negative mass and unconserved energy, two boolean values to be modified
        collision_type=variables['collision_type']
        if collision_type==1:  # elastic and unelastic                                                                              Elastic collision, where kinetic energy and momentum are all conserved
            mass1,vel1,mass2,vel2,vel1f,vel2f= variables['mass1'],variables['vel1'],variables['mass2'],variables['vel2'],variables['vel1f'],variables['vel2f']
            if solve == 'mass1':
                variables['mass1']=round((mass2*(vel1f+vel1)-2*mass2*vel2)/(vel1-vel1f),2)
                mass1=variables['mass1']
                variables['vel2f']=round(vel2+(mass1*(vel1-vel1f)/mass2),2)
            if solve == 'vel1':
                variables['vel1']=( round((vel1f*(mass1+mass2)-2*mass2*vel2)/(mass1-mass2),2))  #needed to be modified
                vel1=variables['vel1']
                variables['vel2f']=round(vel2+(mass1*(vel1-vel1f)/mass2),2)
            if solve =='mass2':
                variables['mass2']=( round(mass1*(vel1f-vel1)/(2*vel2-vel1f-vel1),2))
                mass2=variables['mass2']
                variables['vel2f']=round(vel2+(mass1*(vel1-vel1f)/mass2),2)
            if solve == 'vel2':
                variables['vel2']=( round((mass1*(vel1f-vel1)+mass2*(vel1f+vel1))/2*mass2,2))                                                                                # Equation rearranged in all cases
                vel2=variables['vel2']
                variables['vel2f']=round(vel2+(mass1*(vel1-vel1f)/mass2),2)
            if solve == 'vel1f':
                variables['vel1f']=( round(vel1*(mass1-mass2)/(mass1+mass2)+vel2*2*mass2/(mass1+mass2),2))
                vel1f=variables['vel1f']
                variables['vel2f']=round(vel2+(mass1*(vel1-vel1f)/mass2),2)
            if solve == 'vel2f':
                variables['vel2f']=( round(vel2*(mass2-mass1)/(mass1+mass2)+vel1*2*mass1/(mass1+mass2),2))
                vel2f=variables['vel2f']
                variables['vel1f']=round(vel1+(mass2*(vel2-vel2f)/mass1),2)
            mass1,vel1,mass2,vel2,vel1f,vel2f= variables['mass1'],variables['vel1'],variables['mass2'],variables['vel2'],variables['vel1f'],variables['vel2f']

        if collision_type==2:  #                                                                                Inelastic, kinetic energy not conserved, momentum conserved
            mass1,vel1,mass2,vel2,vel1f,vel2f= variables['mass1'],variables['vel1'],variables['mass2'],variables['vel2'],variables['vel1f'],variables['vel2f']
            if solve == 'mass1':
                variables['mass1']=round(mass2*(vel2f-vel2)/(vel1-vel1f),2)
            if solve == 'vel1':
                variables['vel1']=round(vel1f+(mass2*(vel2f-vel2)/mass1),2)
            if solve =='mass2':
                variables['mass2']=round(mass1*(vel1-vel1f)/(vel2f-vel2),2)
            if solve == 'vel2':
                variables['vel2']=round(vel2f+(mass1*(vel1f-vel1)/mass2),2)
            if solve == 'vel1f':
                variables['vel1f']=round(vel1+(mass2*(vel2-vel2f)/mass1),2)
            if solve == 'vel2f':
                variables['vel2f']=round(vel2+(mass1*(vel1-vel1f)/mass2),2)
            mass1,vel1,mass2,vel2,vel1f,vel2f= variables['mass1'],variables['vel1'],variables['mass2'],variables['vel2'],variables['vel1f'],variables['vel2f']
            if (1/2*((mass1*vel1**2+mass2*vel2**2)-(mass1*vel1f**2+mass2*vel2f**2)))<0 and 'component_cal' not in variables:
                conservation_violated=True

        if collision_type== 3: # completely unelastic                                                                                Inelastic, kinetic energy not conserved, momentum conserved
            mass1,vel1,mass2,vel2,velf= variables['mass1'],variables['vel1'],variables['mass2'],variables['vel2'],variables['velf']
            if solve == 'mass1':
                variables['mass1']=( round((mass2*(velf-vel2))/(vel1-velf),2))
            if solve == 'vel1':
                variables['vel1']=( round((mass2*(velf-vel2))/mass1+velf,2))
            if solve == 'mass2':
                variables['mass2']=( round((mass1*(vel1-velf))/(velf-vel2),2))
            if solve == 'vel2':
                variables['vel2']=( round((velf-mass1*(vel1-velf))/mass2,2))
            if solve == 'velf':
                variables['velf']=(round((mass1*vel1+mass2*vel2)/(mass1+mass2),2))
        if variables['mass1']<0 or variables['mass2']<0:
            negative_mass=True
        if conservation_violated==True or negative_mass==True:
            if conservation_violated==True:                                                                                # When law of physics is violated, no true value will be returned, instead it will be None
                variables=None
                print("Re-enter values, total kinetic energy is not conserved")
            else:
                print("Re-enter values, unrealistic mass occurred")
                variables=None

        return (variables)
    def redraw_1d(variables):#--------------------------------------------------------------------------------------------# Function to draw animation
        pygame.init()
        screen=pygame.display.set_mode((WIDTH, HEIGHT))
        if len(variables)==7:                                                                                # Check the number of keys in a dictionary, to determine how many variables are needed to be defined
            m1,m2,v1,v2,v1f,v2f= variables['mass1'],variables['mass2'],variables['vel1'],variables['vel2'],variables['vel1f'],variables['vel2f']
        else:
            m1,m2,v1,v2,v1f,v2f= variables['mass1'],variables['mass2'],variables['vel1'],variables['vel2'],variables['velf'],variables['velf']
        lst_values=[v1,v2,v1f,v2f]
        display=True
        post_col=False                                                                                # A boolean variable that determine when the collision happen
        press=0
        if v1>=0:
            ang1=0                                                                                # SInce it is 1d, no angles are inputted by user, but the negative/positive sign determine the direction of the velocity
        else:
            ang1=pi
        if v2>=0:
            ang2=0                                                                                # When velocity is bigger than 0, direction becomes 0 degree; when it is smaller than 0, direction becomes 180 degree

        else:
            ang2=pi
        d1x,d1y,d2x,d2y=initial_pos(v1,0,v2,0,ang1,ang2)                                                                            # initial_pos() is called to determine the initial position of of two ball so that they collide at the very center
        x1=WIDTH/2+d1x
        x2=WIDTH/2+d2x                                                                                # Values returned by the function is added to the center coordinate and initial position determined
        y=HEIGHT/2
        lst=ratio_conversion(lst_values)                                                                                # ratio_conversion() is called to recalculate relative velocity
        v1,v2,v1f,v2f=lst[0],lst[1],lst[2],lst[3]

        while display:                                                                                # While loop called to display and redraw the animation
            screen.fill(BLACK)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:                                                                                # When space is pressed, slow down the animation
                    pygame.quit()
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        press+=3
                    if event.key == pygame.K_r:                                                                                # When r is pressed, the program returns to the value selection
                        fade(WIDTH,HEIGHT)
                        main()

            if distance(x1,y,x2,y)<20:                                                                                # If the object did not touch, they will have the initial velocities
                post_col=True
            if post_col==True:                                                                                # When they collide, the pre-set boolean variable becomes False

                act_vel1=v1f
                act_vel2=v2f
            else:
                act_vel1=v1
                act_vel2=v2

            x1+=act_vel1
            x2+=act_vel2
            indication(x1,y,WHITE)
            indication(x2,y,GREY)
            if m1>m2:
                pygame.draw.circle(screen,WHITE, (int(x1),int(y)), int(12),0)
                pygame.draw.circle(screen,GREY, (int(x2),int(y)), int(8),0)
            if m1<m2:
                pygame.draw.circle(screen,WHITE, (int(x1),int(y)), int(8),0)
                pygame.draw.circle(screen,GREY, (int(x2),int(y)), int(12),0)
            legend(screen,variables)                                                                                # Legend function is called to update the velocity and mass
            pygame.display.update()
            pygame.time.delay(press)

    print('-'*30)

    def find_missing_momentum2D(lst,collision_type):#-----------------------------------------------------------------# The main function that collects data and calculates the variables for 2d collisions
        collision_type= collision_type
        print("All angles are mandatory to be input")
        vel1= lst[0]
        angle1= lst[1]
        mass1 = lst[2]
        vel2= lst[3]                                                                                # Variables inside of the function is defined using the imported list of values
        angle2=lst[4]
        mass2 = lst[5]
        if collision_type =='1' or  collision_type =='2':
            vel1f= lst[6]
            angle1f=lst[7]                                                                                # Separate the cases of elastic, inelastic and completely inelastic
            vel2f= lst[8]
            angle2f=lst[9]
            variables={'collision_type':collision_type,'mass1':mass1,'vel1':vel1,'angle1':angle1,'mass2':mass2,'vel2':vel2,'angle2':angle2,'vel1f':vel1f,'angle1f':angle1f,'vel2f':vel2f,'angle2f':angle2f}
        if collision_type=='3':
            velf =lst[6]
            anglef=lst[7]
            variables={'collision_type':collision_type,'mass1':mass1,'vel1':vel1,'angle1':angle1,'mass2':mass2,'vel2':vel2,'angle2':angle2,'velf':velf,'anglef':anglef}

        count_of_empty=0
        vel_variables={}
        ang=[]
        for i in variables:
            if len(variables.get(i))==0:                                                                                # Run through the similar procedure to check the unknown variable
                solve=i                                                                                # if the value is empty string then it becomes the unknown to be solved
                count_of_empty+=1
            else:                                                                                # Otherwise it will be convert into float
                variables[i]=float(variables[i])

            if 'v' in i :                                                                                # Make copies of all velocity variables of the dictionary that consist of only velocities
                vel_variables[i]=variables.get(i)
            if 'g' in i :
                ang.append(variables.get(i))                                                                                # Makes copies of all angles variables of the dictionary that consist of only velocities
        for i in vel_variables:
            for p in reversed(ang):
                vel_variables[i]=(variables.get(i),p)                                                                                # Create a dictionary that consists of tuples containing velocities and their corresponding angles
            ang.remove(p)
        if count_of_empty>2:
            return 'ERROR: Too many empty variables'
        if count_of_empty==0:                                                                                # If too much missing values or none, print message
            return 'EXCEPT: All variables found'

        velx_variables={}
        vely_variables={}                                                                                # Create two empty dictionary reserved for velocities of different axis
        xy_axis=(velx_variables,vely_variables,variables)
        for i in vel_variables:
            if i!=solve:

                velx_variables[i]=round((vel_variables[i][0])*cos(vel_variables[i][1]*pi/180),2)                                # Velocities of each axis is being calculated using the angle assigned and being added into the two dictionary
                vely_variables[i]=round((vel_variables[i][0])*sin(vel_variables[i][1]*pi/180),2)
            else:
                velx_variables[i]=''
                vely_variables[i]=''                                                                                # If the any variable is solve, a empty string is assgned as the value of the corresponding key
        for i in xy_axis:
            i['collision_type']=variables['collision_type']                                                                                # Two new keys assigned to indicate that it is calculate a component
            i['component_cal']=True
        for i in xy_axis:
            for p in variables:
                if 'mass' in p:
                    i[p]=variables[p]
        if find_missing_momentum1D(vely_variables,solve)!=None or find_missing_momentum1D(velx_variables,solve)!= None:# If mass variable becomes negative or conservation of energy is false as calculated previously
            vely_variables[solve]=find_missing_momentum1D(vely_variables,solve)[solve]
            velx_variables[solve]=find_missing_momentum1D(velx_variables,solve)[solve]                                                      # No real values is returned if one of the two conditions is met
            if 'mass' not in solve:
                variables[solve]=sqrt(vely_variables[solve]**2+velx_variables[solve]**2)
            if 'mass' in solve:
                variables[solve]=vely_variables[solve]                                                                                # None s will be returned to re-ask the user to input values
            return(xy_axis)
        else:
            xy_axis=(None,None,None)
            return xy_axis

    def ratio_conversion2d(lstx,lsty):#------------------------------------------------------------------------------------------------------# Ratio conversion of 2d version , takes two parameter instead of one
        v_max=max([abs(i) for i in lstx+lsty if i!=0])
        v_min=min([abs(i) for i in lstx+lsty if i!=0])
        if v_max>1.3:
            ratio=1.3/v_max
            for i in range(len(lstx)):
                lstx[i]= lstx[i]*ratio
                lsty[i]= lsty[i]*ratio                                                                                # Go through the same procedure as the 1d ratio conversion, see previous code to relate
        elif v_min<0.1:
            ratio=0.1/v_min
            for i in range(len(lstx)):
                lstx[i]= lstx[i]*ratio
                lsty[i]= lsty[i]*ratio
        return (lstx,lsty)

    def redraw_2d(variable_x,variable_y,variables):                                                                                # The function that update screen and show animation
        if None in (variable_x,variable_y,variables):
            pass
        else:
            xy=[variable_x,variable_y]
            pygame.init()
            screen=pygame.display.set_mode((WIDTH, HEIGHT))
            display=True                                                                                # The boolean value to set the while loop to keep the game going
            post_col=False                                                                                # Boolean value that check if it's before or after the collision
            press=0
            m1,m2=variable_x['mass1'],variable_x['mass2']
            if len(variable_x)==8:
                v1x,v2x,v1fx,v2fx= variable_x['vel1'],variable_x['vel2'],variable_x['vel1f'],variable_x['vel2f']
            else:
                v1x,v2x,v1fx,v2fx= variable_x['vel1'],variable_x['vel2'],variable_x['velf'],variable_x['velf']                              # Variables will be different if the type of collision is different
            if len(variable_y)==8:
                v1y,v2y,v1fy,v2fy= variable_y['vel1'],variable_y['vel2'],variable_y['vel1f'],variable_y['vel2f']
            else:
                v1y,v2y,v1fy,v2fy= variable_y['vel1'],variable_y['vel2'],variable_y['velf'],variable_y['velf']
            d1x,d1y,d2x,d2y=initial_pos(v1x,v1y,v2x,v2y,variables['angle1'],variables['angle2'])                                            # Use initial_pos function to determine the location to start so they can meet at the center
            print(d1x,d1y,d2x,d2y)
            x1=WIDTH/2+d1x
            y1=HEIGHT/2-d1y
            x2=WIDTH/2+d2x                                                                                # the distance is applied to the initial x, y values of the balls
            y2=HEIGHT/2-d2y
            print('x1:',x1,'y1',y1,'x2',x2,'y2',y2)
            lstx=[v1x,v2x,v1fx,v2fx]
            lsty=[v1y,v2y,v1fy,v2fy]
            ratio_conversion2d(lstx,lsty)                                                                                # Velocity ratio conversion function is applied so that the balls travels at the observable speed
            nlx,nly=ratio_conversion2d(lstx,lsty)
            v1x,v2x,v1fx,v2fx=nlx[0],nlx[1],nlx[2],nlx[3]
            v1y,v2y,v1fy,v2fy=nly[0],nly[1],nly[2],nly[3]

            while display:
                screen.fill(BLACK)
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        pygame.quit()                                                                                # If quit hit, quit the screen
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_SPACE:
                            press+=3                                                                                # If space pressed, the display time becomes slower
                        if event.key == pygame.K_r:
                            fade(WIDTH,HEIGHT)                                                                                # If r pressed, use returns to the variables input screen
                            main()

                if distance(x1,y1,x2,y2)<20:
                    post_col=True
                if post_col==True:
                    velx1,vely1,velx2,vely2=v1fx,v1fy,v2fx,v2fy                                                                                # Before or after collision is detected so new velocities can be assigned

                else:
                    velx1,vely1,velx2,vely2=v1x,v1y,v2x,v2y
        ##        print( velx1,vely1,velx2,vely2)
                x1+=velx1
                x2+=velx2
                y1-=vely1
                y2-=vely2
                indication(x1,y1,WHITE)
                indication(x2,y2,GREY)                                                                                # Indicator are called here
                if m1>m2:
                    pygame.draw.circle(screen,WHITE, (int(x1),int(y1)), int(12),0)
                    pygame.draw.circle(screen,GREY, (int(x2),int(y2)), int(8),0)
                if m1<m2:
                    pygame.draw.circle(screen,WHITE, (int(x1),int(y1)), int(8),0)
                    pygame.draw.circle(screen,GREY, (int(x2),int(y2)), int(12),0)
                pygame.draw.circle(screen,RED, center, 4,0)
                legend(screen,variables)
                pygame.display.update()                                                                                #
                pygame.time.delay(press)

    def indication(x,y,color):
        if x>WIDTH:
            ratio=abs(WIDTH/x)
            ind=right
            indicator=ind.get_rect(center=(WIDTH-18,int(y)))                                                                                # Indicator function helps to indicate the position of the ball when they are outside of the screen
            pygame.draw.circle(screen,color, (WIDTH-48,int(y)), int(7*ratio),0)
            screen.blit(ind,indicator)
        if x<0:
            ratio=(WIDTH/(WIDTH+abs(x)))                                                                                # General concept: When the x or y of ball is bigger or smaller than the limit of the screen, a triangle indicator will be shown ,and a ball will be shown with a changing size that depends on the how far away the ball is from the screen
            ind=left
            indicator=ind.get_rect(center=(18,int(y)))
            pygame.draw.circle(screen,color, (48,int(y)), int(7*ratio),0)
            screen.blit(ind,indicator)
        if y>HEIGHT:
            ratio=abs(HEIGHT/y)
            ind=down
            indicator=ind.get_rect(center=(int(x),(HEIGHT-18)))
            pygame.draw.circle(screen,color, (int(x),HEIGHT-48), int(7*ratio),0)
            screen.blit(ind,indicator)
        if y<0:
            ratio=(HEIGHT/(HEIGHT+abs(Y)))
            ind=up
            indicator=ind.get_rect(center=(int(x),(18)))
            pygame.draw.circle(screen,color, (int(x),48), int(7*ratio),0)
            screen.blit(ind,indicator)

    def choose_component():#-----------------------------------------------------------------------------------------------------------# A function that sets the screen for the user to be abl eto choose which dimension of animation is to be shown

        while True:
            event = pygame.event.poll()
            mousex, mousey = pygame.mouse.get_pos()
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            twoD_width=233                                                                                # Dimension of each button is defined here
            twoD_x=239
            oneD_width=233
            oneD_x=497
            h=351
            y=137
            if oneD_x <= mousex <= oneD_x+oneD_width and y <= mousey <= y+h and event.type == pygame.MOUSEBUTTONDOWN:# If the mouse is clicked with a certain location, the type of visualization will be shown
                return '1d'
            elif twoD_x <= mousex <= twoD_x+twoD_width and y <= mousey < y+h and event.type == pygame.MOUSEBUTTONDOWN:
                return '2d'

            screen.blit(choose_image,(0,0))
            if oneD_x <= mousex <= oneD_x+oneD_width and y <= mousey <= y+h :                                                           # In order to make the visual effect better, when mouse is positioned over a certain area, it will increase the visual attribute of the product
                screen.blit(oneD_click,(0,0))
            elif twoD_x <= mousex <= twoD_x+twoD_width and y <= mousey < y+h:
                screen.blit(twoD_click,(0,0))

            pygame.display.update()
    def choose_collision():
        myfont = pygame.font.SysFont('Arial', 36)
        ELASTIC_txt = myfont.render('ELASTIC', False, WHITE)

        INELASTIC_txt = myfont.render('INELASTIC', False, ORANGE)                                                                                # Indication to be shown on each section of the screen

        CE_txt = myfont.render('COMPLETELY', False, BLACK)
        CE_txt2=myfont.render('INELASTIC', False, BLACK)
        while True:
            event = pygame.event.poll()
            mousex, mousey = pygame.mouse.get_pos()
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

            elastic_width=WIDTH/3
            elastic_x=0
            inelatic_width=WIDTH/3                                                                                # The three sections of the screen on which if clicked, will lead to different type of collision
            inelastic_x=WIDTH/3
            CE_width=WIDTH/3
            CE_x=2*WIDTH/3
            h=HEIGHT
            y=0
            if elastic_x <= mousex <=elastic_width and y <= mousey <= y+h and event.type == pygame.MOUSEBUTTONDOWN:
                return '1'
            elif inelastic_x <= mousex <= inelastic_x+inelatic_width and y <= mousey < y+h and event.type == pygame.MOUSEBUTTONDOWN:# If mouse is clicked  within a certain area, the type of collision will be chosen
                return '2'
            elif CE_x <= mousex <= CE_x+CE_width and y <= mousey < y+h and event.type == pygame.MOUSEBUTTONDOWN:
                return '3'

            screen.fill(BLACK)
            if elastic_x <= mousex <=elastic_width and y <= mousey <= y+h:
                pygame.draw.rect(screen, ORANGE, (elastic_x,y,elastic_width,HEIGHT), 0)
                screen.blit(ELASTIC_txt,(WIDTH/6-75, h/2))
            elif inelastic_x <= mousex <= inelastic_x+inelatic_width and y <= mousey < y+h:
                pygame.draw.rect(screen, GREY, (inelastic_x,y,inelatic_width,HEIGHT), 0)                                                                        # Visual attributes of the button, when mouse is positioned on a certain are, color will be changed
                screen.blit(INELASTIC_txt,(3*WIDTH/6-75, h/2))
            elif CE_x <= mousex <= CE_x+CE_width and y <= mousey < y+h:
                pygame.draw.rect(screen, WHITE, (CE_x,y,CE_width,HEIGHT), 0)
                screen.blit(CE_txt,(5*WIDTH/6-75, h/2))
                screen.blit(CE_txt2,(5*WIDTH/6-75, h/2+50))

            pygame.display.update()
    def fade(width,height):                                                                                # Function that will make the screen to fade into a certain color
        fade=pygame.Surface((width,height))
        fade.fill(BLACK)
        for alpha in range(0,300):
            fade.set_alpha(alpha)
            screen.blit(fade,(0,0))
            pygame.display.update()
            pygame.time.delay(3)

    def Rfade(width,height):                                                                                # Function that will make the screen to inversely fade into a certain color

        fade=pygame.Surface((width,height))
        fade.fill(WHITE)
        for alpha in reversed(range(0,300)):
            fade.set_alpha(alpha)
            screen.blit(fade,(0,0))
            pygame.display.update()
            pygame.time.delay(3)

    def main(case='2d',collision='1'):                                                                                # Main function that contained all the little functions
        case=choose_component()
        collision_type=choose_collision()
        a,b=slider_set(case,collision_type)
        fade(WIDTH,HEIGHT)                                                                                ### Firstly, the choose cases functions are called to determine the dimension and the type of the collision
        if case=='1d':                                                                                ### Determine the type values b using sliders
            a,b=ask_for_input(a,b)
            print(a)                                                                                ### The sliders value are inputted into functions to rearrange and calculate the missing data to finalize the visualization
            print(find_missing_momentum1D(a,b))
            while None == find_missing_momentum1D(a,b):
                a,b=slider_set(case,collision_type)
                print(a)                                                                                ### If special cases are met, re ask the using input more valid values, and run through the process again
                a,b=ask_for_input(a,b)
                find_missing_momentum1D(a,b)

        if case=='2d':
            a,b,c=find_missing_momentum2D(a,b)
            print(a,b,c)
            while None in find_missing_momentum1D(a,b):                                                         # Same scenario applies for 2d collision
                print('fuck meeee')
                a,b=slider_set(case,collision_type)
                a,b,c=find_missing_momentum1D(a,b)

        if case=='1d':

            print(redraw_1d(find_missing_momentum1D(a,b)))                                                  # Draw the 1d scenario
        if case=='2d':
            redraw_2d(a,b,c)                                                                                # Draw the 2d scenario
    main()
    unit=''                                                                                # Main functions calling


import cv2
import matplotlib.pyplot as plt
img = cv2.imread("img1.jpg")

# Gray Scale çevirme
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

plt.figure()
plt.axis('off')
plt.imshow(img,cmap = "gray") #color map ile griye çevirdik
plt.show()

#Thresholding
# 60-255 arasındakini eşikleri beyaz yap demek
_,thresh_img = cv2.threshold(img,thresh=60,maxval=255,type=cv2.THRESH_BINARY)
#THRESH_BINARY_INV inverse deseydik o zaman 60-255 arası siyah olacaktı diğer yerler beyaz olacaktı

plt.figure()
plt.axis('off')
plt.imshow(thresh_img,cmap = "gray") #color map ile griye çevirdik
plt.show()

# Adapting Thresholding

thresh_img2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,9,8)
# C sayısı ortalamadan veya ağırlıklı ortalamadan çıkartılabilecke değer = 8 dedik
# 11 block size yani siyah noktaların kalınlığı gibi düşün

plt.figure()
plt.axis('off')
plt.imshow(thresh_img2,cmap = "gray") #color map ile griye çevirdik
plt.show()


#!/usr/bin/env python

from __future__ import print_function

import logging

from Libs.ManageAllGrafts import ManageAllGrafts

def display_menu(header, menu_list):
    """
    Display a menu and return the selection
    :param str header: The header to print at top
    :param list menu_list:
        A list of tuples of format
            (thing to display, thing to return when selected)
    :returns: One of the selected things :-)
    """
    while True:
        print()
        print(header)
        for ind, menu_item in enumerate(menu_list):
            print("{}: {}".format(ind, menu_item[0]))

        user_input = raw_input("Enter a selection index: ")
        # Handle the case where the user input something non-integer
        try:
            user_input_int = int(user_input)
        except:
            print("ERROR: input an integer")
            continue

        # Handle the case where the user input something out of range
        if user_input_int not in range(len(menu_list)):
            print("ERROR: select something on the menu")
            continue

        # Handle the case where the user did something else invalid
        try:
            selected_entry = menu_list[user_input_int]
        except:
            print("ERROR: bad selection, you bad mother")
            continue

        # This might break if the programmer doesn't provide proper input
        # Let it break - how else would you handle programmer stupidity?
        return selected_entry[1]

def add_graft(manager):
    print()
    addr = raw_input("Address (blank to cancel): ")
    if addr == "":
        print("CANCELLED")
        return

    port = raw_input("Port (blank to cancel): ")
    if port == "":
        print("CANCELLED")
        return
    try:
        port = int(port)
    except:
        print("ERROR: invalid port - cancelling - only integers accepted")
        return

    if port < 0 or port > 65535:
        print("ERROR: invalid port - cancelling")
        return

    """ TODO: Maybe later
    architecture = raw_input("Architecture of graft to add (default: X64): ")
    if architecture == "":
        architecture = "X64"
    """

    new_ind = manager.add_graft(addr, port)

    print("Added graft index: {}".format(new_ind))

def list_grafts(manager):
    print()
    print("Available grafts:")
    for index, addr, port in manager.list_grafts():
        print("{}: {} port {}".format(index, addr, port))

def select_graft(manager, menu_title):
    """
    Display a list of grafts, and return the integer selected
    :return int or None: None if the user selected "back"
    """
    graft_list = manager.list_grafts()

    graft_menu = [("{} port {}".format(addr, port), index)
            for index, addr, port in graft_list]
    graft_menu.append(("Back", None))

    return display_menu(menu_title, graft_menu)

def command_graft(manager):
    selected = select_graft(manager, "Send Command to Graft: ")
    if selected is None:
        print("CANCELLED")
        return

    command = raw_input("Command (blank to cancel): ")
    if command == "":
        print("CANCELLED")
        return

    manager.command_graft(selected, command)
    print("Command sent - response will be received in background")

def show_command_responses(manager):
    selected = select_graft(manager, "Show Response From Graft: ")
    if selected is None:
        print("CANCELLED")
        return

    print()
    for c_and_r in manager.list_graft_responses(selected):
        print("{}: {}".format(c_and_r.command, c_and_r.response))

def shutdown(manager):
    manager.close()
    quit(0)

def mainloop():
    main_menu = [
            ("Add graft", add_graft),
            ("List grafts", list_grafts),
            ("Command graft", command_graft),
            ("Show command responses", show_command_responses),
            ("Quit", shutdown),
            ]

    manager = ManageAllGrafts()

    while True:
        selected_action = display_menu("Main Menu", main_menu)
        selected_action(manager)

if __name__ == "__main__":
    logging.basicConfig(filename="Logs/cli_log",
            format="%(asctime)s %(message)s", level=logging.INFO)

    mainloop()


import pygit2

import os
import re
import sys

from argparse import ArgumentParser

cli = ArgumentParser()
subparsers = cli.add_subparsers(dest="subcommand")

def argument(*name_or_flags, **kwargs):
    """Convenience function to properly format arguments to pass to the
    subcommand decorator.
    """
    return (list(name_or_flags), kwargs)

def subcommand(args=[], parent=subparsers):
    """Decorator to define a new subcommand in a sanity-preserving way.
    The function will be stored in the ``func`` variable when the parser
    parses arguments so that it can be called directly like so::
        args = cli.parse_args()
        args.func(args)
    Usage example::
        @subcommand([argument("-d", help="Enable debug mode", action="store_true")])
        def subcommand(args):
            print(args)
    Then on the command line::
        $ python cli.py subcommand -d
    """
    def decorator(func):
        parser = parent.add_parser(func.__name__, description=func.__doc__)
        for arg in args:
            parser.add_argument(*arg[0], **arg[1])
        parser.set_defaults(func=func)
    return decorator

BITCOIN_CHAIN = ["testnet","mainnet", "regtest"]

def get_branches():
    return pygit2.Repository(path=os.getenv('GITDIR')).branches.local

@subcommand([argument("branches", help="Name",action="store_true")])
def branches(args):
    print(list(get_branches()))

@subcommand([argument("context", help="Context")])
def context(args):
    if "::" in args.context:
        cmd = []
        context = args.context.split("::")
        if context[0] in BITCOIN_CHAIN:
            cmd.append(f"export BITCOIN_CHAIN={context[0]};")
        else:
            print(f"echo {context[0]} is an 'Invalid chain'")

        filtered_branches = [branch for branch in get_branches() if context[1] in branch]
        if len(filtered_branches) > 1:
            print(f"echo Found more than one branch matching {context[1]}")

        else:
            cmd.append(f"export BITCOIN_GIT_BRANCH={filtered_branches[0]};")

        print("".join(cmd))

if __name__ == "__main__":
    args = cli.parse_args()
    if args.subcommand is None:
        cli.print_help()
    else:
        args.func(args)


from functools import cmp_to_key
import subprocess
import sys
import logging
import re
import threading
import time
import os
from urllib.error import HTTPError
import requests
from requests.exceptions import ConnectionError
from urllib3.exceptions import NewConnectionError

from cryptoadvance.specter.specter_error import SpecterError

logger = logging.getLogger(__name__)

class VersionChecker:
    def __init__(self, name="cryptoadvance.specter", specter=None):
        self.name = name

        self.current = self._get_current_version()
        self.latest = "unknown"
        self.upgrade = False
        self.running = False
        self.specter = specter

    def start(self):
        if not self.running:
            self.running = True
            self.thread = threading.Thread(target=self.loop)
            self.thread.daemon = True
            self.thread.start()

    def stop(self):
        logger.info("version checker stopped.")
        self.running = False

    @property
    def info(self):
        info = {"current": self.current, "latest": self.latest, "upgrade": self.upgrade}
        return info

    @property
    def installation_type(self):
        """app or pip"""
        if getattr(sys, "frozen", False):
            return "app"
        else:
            return "pip"

    def loop(self, dt=3600):
        """Checks for updates once per hour"""
        while self.running:
            self.current, self.latest, self.upgrade = self.get_version_info()
            logger.info(
                f"version checked, install_type {self.installation_type} curr: {self.current} latest: {self.latest} ==> upgrade: {self.upgrade}"
            )
            time.sleep(dt)

    def _get_binary_version(self):
        """
        Get binary version: current, latest.
        Fails if version.txt is not present.
        Returns latest = "unknown" if fetch failed.
        """
        current = self._get_current_version()
        latest = "unknown"
        if self.name != "cryptoadvance.specter":
            logger.warning(
                "We're checking here for a different binary than specter-desktop. We're hopefully in a pytest"
            )
        latest = self._get_latest_version_from_github()
        return current, latest

    def _get_pip_version(self):
        """
        returns current, latest
        """
        current = self._get_current_version()
        latest = "unknown"
        try:
            if self.specter:
                requests_session = self.specter.requests_session(force_tor=False)
            else:
                requests_session = requests.Session()

            releases = (
                requests_session.get(f"https://pypi.org/pypi/{self.name}/json")
                .json()["releases"]
                .keys()
            )
            releases = list(releases)
            for i in range(-1, 0 - len(releases), -1):
                # for some stupid reason, rc-versions are BEFORE the real versions
                if not releases[i][:-1].endswith("rc"):
                    latest = releases[i]
                    break
        except (
            HTTPError,
            ConnectionError,
            ConnectionRefusedError,
            NewConnectionError,
        ) as e:
            logger.error(f"{e} while checking for new pypi version")
        except Exception as e:
            logger.exception(e)
            latest = "unknown"
        return current, latest

    def get_version_info(self):
        """
        Returns a triple of the current version
        of the pip-package cryptoadvance.specter and
        the latest version and whether you should upgrade.
        """
        # check if we have version.txt file
        # this is the case for binaries
        current = "unknown"
        latest = "unknown"
        # check binary version
        if self.installation_type == "app":
            current, latest = self._get_binary_version()
        else:
            current, latest = self._get_pip_version()

        # check that both current and latest versions match the pattern
        # vA.B.C or just A.B.C
        # For vA.B.C-preX or something like that we don't show notification
        if re.search(r"v?([\d+]).([\d+]).([\d+])$", current):
            if re.search(r"v?([\d+]).([\d+]).([\d+])$", latest):
                return (
                    current,
                    latest,
                    # check without leading v so v1.2.3 = 1.2.3
                    latest.replace("v", "") != current.replace("v", ""),
                )
        # if current version is not A.B.C - stop periodic checks
        else:
            self.stop()
        return current, latest, False

    def _get_latest_version_from_github(self):
        try:
            if self.specter:
                requests_session = self.specter.requests_session(force_tor=False)
            else:
                requests_session = requests.Session()
            releases = (
                requests_session.get(f"https://pypi.org/pypi/{self.name}/json")
                .json()["releases"]
                .keys()
            )
            latest = sorted(releases, key=cmp_to_key(compare))[0]
        except Exception as e:
            logger.exception(e)
            latest = "unknown"
        return latest

    @classmethod
    def _get_current_version(self):
        """Returns the version found in cryptoadvance.specter._version or "unknown"
        if that doesn't exist
        """
        try:
            from cryptoadvance.specter._version import version
        except ModuleNotFoundError:
            return "unknown"
        return "v" + version

def compare(version1: str, version2: str) -> int:
    """Compares two version strings like v1.5.1 and v1.6.0 and returns
    * 1 : version2 is bigger that version1
    * -1 : version1 is bigger than version2
    * 0 : both are the same
    This is not supporting semver and it doesn't take any postfix (-pre5)
    into account and is therefore a naive implementation
    """
    version1 = _parse_version(version1)
    version2 = _parse_version(version2)

    if version1["major"] > version2["major"]:
        return -1
    elif version1["major"] < version2["major"]:
        return 1
    if version1["minor"] > version2["minor"]:
        return -1
    elif version1["minor"] < version2["minor"]:
        return 1
    if version1["patch"] > version2["patch"]:
        return -1
    elif version1["patch"] < version2["patch"]:
        return 1
    if version1["postfix"] == "" and version2["postfix"] == "":
        return 0
    if version1["postfix"] == "" and version2["postfix"] != "":
        return -1
    if version1["postfix"] != "" and version2["postfix"] == "":
        return 1
    version1["postfix"] = (
        version1["postfix"].replace("-", "").replace("rc", "").replace("pre", "")
    )
    version2["postfix"] = (
        version2["postfix"].replace("-", "").replace("rc", "").replace("pre", "")
    )
    if version1["postfix"] < version2["postfix"]:
        return 1
    if version1["postfix"] > version2["postfix"]:
        return -1
    return 0

def _parse_version(version: str) -> dict:
    """Parses version-strings like v1.5.6-pre5 and returns a dict
    This also parses something like:
    2.0.0rc20.dev0+ga99ede2a.d20230215
    but ignores the stuff behind the postfix (which is good enough for our use cases)
    see also: https://github.com/pypa/setuptools_scm/#default-versioning-scheme
    """
    if version.startswith("0.1.dev") or version.startswith("v0.1.dev"):
        # setuptools_scm creates weird versions if you're somewhere where no tags are available
        # on the .git
        # This is the case in testing-scenarios. I couldn't figure out how to convince
        # setuptools_scm to at least return 0.0.1dev or something like that.
        # Anyway, let's return something, it's not relevant anyway.
        # And the alternative would be yet another dependency like e.g. packaging
        return {
            "major": 0,
            "minor": 1,
            "patch": 0,
            "postfix": "",
        }
    try:

        if version[0] == "v":
            version = version[1:]
        version = version.replace("rc", "-pre")
        version_ar = version.split(".")
        if len(version_ar) == 5 or len(version_ar) == 4:
            version_ar = version_ar[0:3]
        if len(version_ar) != 3:
            raise SpecterError(
                f"version {version} does not have 3 separated digits but {len(version_ar)}"
            )
        postfix = ""
        if "-" in version_ar[2]:
            postfix = version_ar[2].split("-")[1]
            version_ar[2] = version_ar[2].split("-")[0]
        return {
            "major": int(version_ar[0]),
            "minor": int(version_ar[1]),
            "patch": int(version_ar[2]),
            "postfix": postfix,
        }
    except Exception as e:
        logger.error(f"{str(e)} parsing version {version} ")
        raise e


import asyncio
from functools import wraps

import pytest

from pnq._itertools import AsyncMap, DebugPath, Lazy, Map, Sleep
from pnq._itertools.core import IterType, Query, QueryNormal, QuerySyncToAsync

def async_test(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        async def main():
            return await func(*args, **kwargs)

        return asyncio.run(main())

    return wrapper

async def aiter(iterable=[]):
    for i in iterable:
        yield i

async def create_aiter():
    yield 4
    yield 5
    yield 6

async def list_async(aiter):
    return [x async for x in aiter]

def test_iter_type():
    assert not IterType.IMPOSSIBLE
    assert IterType.NORMAL
    assert IterType.ASYNC
    assert IterType.BOTH

    assert (IterType.NORMAL & IterType.BOTH) == IterType.NORMAL
    assert (IterType.ASYNC & IterType.BOTH) == IterType.ASYNC
    assert (IterType.BOTH & IterType.BOTH) == IterType.BOTH

    assert (IterType.NORMAL & IterType.ASYNC) == IterType.IMPOSSIBLE
    assert (IterType.ASYNC & IterType.ASYNC) == IterType.ASYNC
    assert (IterType.BOTH & IterType.ASYNC) == IterType.ASYNC

    assert (IterType.NORMAL & IterType.NORMAL) == IterType.NORMAL
    assert (IterType.ASYNC & IterType.NORMAL) == IterType.IMPOSSIBLE
    assert (IterType.BOTH & IterType.NORMAL) == IterType.NORMAL

    assert (IterType.NORMAL & IterType.IMPOSSIBLE) == IterType.IMPOSSIBLE
    assert (IterType.ASYNC & IterType.IMPOSSIBLE) == IterType.IMPOSSIBLE
    assert (IterType.BOTH & IterType.IMPOSSIBLE) == IterType.IMPOSSIBLE

class Test010_Async:
    @async_test
    async def test_async(self):
        q1 = Query([1, 2, 3])
        assert list(q1) == [1, 2, 3]

        with pytest.raises(NotImplementedError):
            [x async for x in q1]

        q2 = Query(create_aiter())
        assert [x async for x in q2] == [4, 5, 6]

        with pytest.raises(NotImplementedError):
            list(q2)

    @async_test
    async def test_chain(self):
        q1 = QueryNormal([1, 2, 3])
        assert list(q1) == [1, 2, 3]
        assert [x async for x in q1] == [1, 2, 3]

        q2 = Query(q1)
        assert list(q2) == [1, 2, 3]
        assert [x async for x in q2] == [1, 2, 3]

        q3 = QuerySyncToAsync(q2)
        with pytest.raises(NotImplementedError):
            assert list(q3) == [1, 2, 3]

        assert [x async for x in q3] == [1, 2, 3]

        q4 = Map(q1, lambda x: x * 2)
        assert list(q4) == [2, 4, 6]
        assert [x async for x in q4] == [2, 4, 6]

        q5 = Map(q2, lambda x: x * 2)
        assert list(q5) == [2, 4, 6]
        assert [x async for x in q5] == [2, 4, 6]

        q6 = Map(q3, lambda x: x * 2)
        with pytest.raises(NotImplementedError):
            assert list(q6) == [2, 4, 6]
        assert [x async for x in q6] == [2, 4, 6]

        q7 = Map(q6, lambda x: x * 2)
        with pytest.raises(NotImplementedError):
            assert list(q7) == [4, 8, 12]
        assert [x async for x in q7] == [4, 8, 12]

        async def mul_2(x):
            return x * 2

        q8 = AsyncMap(q7, mul_2)
        with pytest.raises(NotImplementedError):
            assert list(q8) == [8, 16, 24]
        assert [x async for x in q8] == [8, 16, 24]

        q9 = Map(q8, lambda x: x * 2)
        with pytest.raises(NotImplementedError):
            assert list(q9) == [16, 32, 48]
        assert [x async for x in q9] == [16, 32, 48]

    @async_test
    async def test_lazy(self):
        assert Lazy(Query([1, 2, 3]), list)() == [1, 2, 3]
        assert await Lazy(Query(create_aiter()), list_async) == [4, 5, 6]

    @async_test
    async def test_map_debug(self):
        map_five = lambda x: 5  # noqa
        map_ten = lambda x: 10  # noqa
        assert Lazy(DebugPath(Query([1, 2, 3]), map_five, map_ten), list)() == [5, 5, 5]
        assert await Lazy(
            DebugPath(Query(create_aiter()), map_five, map_ten), list_async
        ) == [10, 10, 10]

    @async_test
    async def test_sleep(self):
        assert list(Sleep(Query([1, 2, 3]), 0)) == [1, 2, 3]
        assert [x async for x in Sleep(Query(aiter([1, 2, 3])), 0)] == [1, 2, 3]

    def test_builder(self):
        from pnq._itertools.builder import Builder

        assert list(Builder.query([1, 2, 3])) == [1, 2, 3]
        assert list(Builder.query({1: "a", 2: "b"})) == [(1, "a"), (2, "b")]

    @pytest.mark.xfail
    def test_builder_run(self):  # FIXME: テストが通らない
        import tempfile

        from pnq._itertools.builder import Builder
        from pnq.aio import CancelToken

        async def func_1():
            return 100

        assert Builder.run(func_1) == 100

        with tempfile.TemporaryFile() as f:
            # 引数なしで関数が実行できること
            async def write_1():
                f.write(b"test1")
                f.flush()

            Builder.run(write_1)
            f.seek(0)
            assert f.read() == b"test1"

        with tempfile.TemporaryFile() as f:
            # tokenを受け取ってファイルを書き込めること
            async def write_2(token):
                f.write(b"test2")
                f.flush()
                assert isinstance(token, CancelToken)
                assert token.is_running()

            Builder.run(write_2)
            f.seek(0)
            assert f.read() == b"test2"

    @pytest.mark.skipif(True, reason="CI/CDで失敗するためスキップ")
    @pytest.mark.slow
    def test_builder_run_signal(self):
        import signal
        import subprocess
        import time
        from functools import partial
        from pathlib import Path

        sigtest = Path(__file__).absolute().parent / "__signal_test"

        popen = partial(
            subprocess.Popen, stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )

        p = popen(["python", sigtest, "0"])
        outs, errs = p.communicate()
        assert b"" in outs
        assert b"" in outs
        assert p.returncode == 0

        p = popen(["python", sigtest, "1"])
        outs, errs = p.communicate()
        assert b"" in outs
        assert b"" in outs
        assert p.returncode == 0

        # SIGINTを送信した時、強制キャンセルされることを確認
        p = popen(["python", sigtest, "0"])
        time.sleep(0.1)
        p.send_signal(signal.SIGINT)
        outs, errs = p.communicate()
        assert b"SIGINT" in outs
        assert b"forcibly" in outs
        assert b"Task was destroyed" in errs
        assert p.returncode == 1

        # SIGINTを送信した時、強制キャンセルされないことを確認
        p = popen(["python", sigtest, "1"])
        time.sleep(0.1)
        p.send_signal(signal.SIGINT)
        outs, errs = p.communicate()
        assert b"SIGINT" in outs
        assert b"safely shut down" in outs
        assert b"" in errs
        assert p.returncode == 0


import pandas as pd
import cv2
import torch
from torchvision import transforms
from models.resnet import Resnet
from models.efficientnet import EfficientNetModel

def preprocess_csv(df):
    df = df.drop(['width', 'height', 'xmin', 'ymin', 'xmax', 'ymax'], axis=1)
    df['class'] = df['class'].replace(['military tank', 'civilian car', 'military helicopter', 'military aircraft',
                                       'military truck', 'civilian aircraft'], ['0', '1', '2', '3', '4', '5'])
    return df

def get_model(model_name, num_classes):
    model = None
    if 'resnet' in model_name:
        model = Resnet(model_name, num_classes)
    elif 'efficientnet' in model_name:
        model = EfficientNetModel(model_name, num_classes)
    else:
        raise ValueError(f'Undefined model name: {model_name}')
    return model

def get_mean_and_std(dataloader):
    channels_sum, channels_squared_sum, num_batches = 0, 0, 0
    for data, _ in dataloader:
        convert_tensor = transforms.ToTensor()
        data = convert_tensor(data)
        data = data.unsqueeze(0)
        channels_sum += torch.mean(data, dim=[0, 2, 3])
        channels_squared_sum += torch.mean(data ** 2, dim=[0, 2, 3])
        num_batches += 1

    mean = channels_sum / num_batches
    std = (channels_squared_sum / num_batches - mean ** 2) ** 0.5

    return mean, std

def balance_dataframe(data):
    data = data.groupby('class')
    data = pd.DataFrame(data.apply(lambda x: x.sample(data.size().min()).reset_index(drop=True)))

    return data

def get_dataset_counts_info(X_train, X_test):
    print(f'Length of X_train: {len(X_train)}')
    print(X_train['class'].value_counts())
    print('--------------------')
    print(f'Length of X_test: {len(X_test)}')
    print(X_test['class'].value_counts())

def image_resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    dim = None
    (h, w) = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None:
        r = height / float(h)
        dim = (int(w * r), height)
    else:
        r = width / float(w)
        dim = (width, int(h * r))

    resized = cv2.resize(image, dim, interpolation=inter)
    return resized


from tinydb import Query, TinyDB

from models.match import Match
from models.round import Round
from models.db_manager_players import Db_manager_player
from utils.transform_date import date_iso2fr

class Db_manager_tournament:
    """Manager for tournament table on database"""

    def __init__(self):
        db = TinyDB('chess_tournament')
        self.tournaments_table = db.table("tournaments")

    def get_all(self):
        """Get all tournament on db only base info

        Return:
            list[Tournament]: list of tournament obj

        """
        from models.tournament import Tournament
        tournaments_obj_list: list[Tournament] = []
        for document in self.tournaments_table:
            tournament_id = document.doc_id
            tournament_obj = self.get_one(tournament_id)
            if tournament_obj:
                tournaments_obj_list.append(tournament_obj)
        return tournaments_obj_list

    def get_one(self, tournament_id):
        """Get one tournament on db only base info

        Args:
            int: tournament id

        Return:
            Tournament: tournament obj

        """

        from models.tournament import Tournament
        document = self.tournaments_table.get(doc_id=tournament_id)
        if document:
            tournament_obj = Tournament(
                document["name"],
                document["place"],
                document["date"],
                document["time_ctrl"],
                document["description"],
                document["round"]
            )
            tournament_obj.set_id(document.doc_id)
            tournament_obj.set_status(document["status"])
            return tournament_obj
        return False

    def _get_players_by_tournament_id(self, tournament_id: int) -> dict:
        """Get players of one tournament

        Args:
            int: tournament id

        Return:
            dict: key = id of player, value = points of the player

        """
        tournament_document = self.tournaments_table.get(doc_id=tournament_id)
        player_dict = {}
        if tournament_document:
            player_dict = tournament_document.get('players')
            if player_dict:
                return player_dict
        return {}

    def get_players_all_tournaments(self) -> set[int]:
        """Get players of all tournaments

        Return:
            set[int]: id of players

        """

        tournaments_obj_list = self.get_all()
        players_set: set[int] = set()
        for tournament_obj in tournaments_obj_list:
            if tournament_obj.get_id:
                players_dict = self._get_players_by_tournament_id(
                    tournament_obj.get_id)
                player_id_list = [int(key) for key in players_dict]
                players_set.update(player_id_list)
        return players_set

    def get_one_from_db(self, tournament_id: int):
        """Get complete obj tournament from db

        Args:
            int: tournament id

        Return:
            Tournament: base info + players + rounds + matchs
        """
        tournament_document = self.tournaments_table.get(doc_id=tournament_id)
        if tournament_document:
            tournament_obj = self.get_one(tournament_id)
            if tournament_obj:
                # Create Players list
                self._create_players_obj_list(tournament_id)
                tournament_players_dict = tournament_document["players"]
                # Extract players_id from key
                players_id: list[int] = [key for key in tournament_players_dict]
                # Extract player_ point with key
                players_points: list[int] = [
                    tournament_players_dict[key] for key in tournament_players_dict
                ]
                for player_id, player_points in zip(players_id, players_points):
                    tournament_obj.add_player_from_db(player_id, player_points)
                # Create rounds and matchs
                round_obj_list = self.get_rounds_by_tournament_id(tournament_id)
                for round_obj in round_obj_list:
                    tournament_obj.add_round_from_db(round_obj)
                return tournament_obj
        return False

    def get_rounds_by_tournament_id(self, tournament_id: int) -> list[Round] | list:
        """Get rounds of a tournament

        Args:
            int: tournament id

        Return:
            list[Round]: list of round obj with matchs

        """
        tournament_document = self.tournaments_table.get(doc_id=tournament_id)
        if tournament_document:
            rounds_db_list = tournament_document["rounds"]
            players_obj_list = self._create_players_obj_list(tournament_id)
            round_obj_list: list[Round] = []
            for round_item in rounds_db_list:
                # Create one round
                # Round is close ?
                if round_item["date_end"] is not None:
                    round_obj = Round.add_round_from_db(
                        round_item["name"],
                        round_item["date_begin"],
                        round_item["date_end"]
                    )
                else:
                    # Round is not close
                    round_obj = Round.add_round_from_db(
                        round_item["name"],
                        round_item["date_begin"]
                    )
                # Create matchs
                for match_db in round_item["matchs"]:
                    birth_date_iso_player_1 = match_db["player_1"]["birth_date"]
                    birth_date_iso_player_2 = match_db["player_2"]["birth_date"]
                    birth_date_fr_player_1 = date_iso2fr(birth_date_iso_player_1)
                    birth_date_fr_player_2 = date_iso2fr(birth_date_iso_player_2)
                    match_db["player_1"]["birth_date"] = birth_date_fr_player_1
                    match_db["player_2"]["birth_date"] = birth_date_fr_player_2
                    # Find player1 in list
                    player_1 = [
                        player for player in players_obj_list
                        if ((
                            player.get_player["name"]
                        ) == (
                            match_db["player_1"]["name"]
                        )
                        ) and ((
                            player.get_player["surname"]
                        ) == (
                            match_db["player_1"]["surname"]
                        )
                        ) and ((
                            player.get_player["birth_date"]
                        ) == (
                            match_db["player_1"]["birth_date"]
                        ))]
                    score_player_1 = match_db["score_player_1"]
                    player_1 = player_1[0]
                    # Find player2 in list
                    player_2 = [
                        player for player in players_obj_list
                        if ((
                            player.get_player["name"]
                        ) == (
                            match_db["player_2"]["name"]
                        )
                        ) and ((
                            player.get_player["surname"]
                        ) == (
                            match_db["player_2"]["surname"]
                        )
                        ) and ((
                            player.get_player["birth_date"]
                        ) == (
                            match_db["player_2"]["birth_date"]
                        ))]
                    score_player_2 = match_db["score_player_2"]
                    player_2 = player_2[0]
                    # Create one match
                    match_obj = Match(player_1,
                                      player_2,
                                      score_player_1,
                                      score_player_2
                                      )
                    round_obj.add_match(match_obj)
                round_obj_list.append(round_obj)
            return round_obj_list
        return []

    def update_players_by_tournament_id(self, tournament):
        """Update players dict of one tournament on db

        Args:
            str: name of tournament
            str: date of tournament
            dict: key = id of player, value = points of the player

        """

        from models.tournament import Tournament
        tournament_obj: Tournament = tournament
        tournament_id = tournament_obj.get_id
        tournament_players = tournament_obj.get_players
        if tournament_id:
            self.tournaments_table.update(
                {"players": tournament_players}, doc_ids=[tournament_id])

    def update_round_quantity_by_tournament_id(self, tournament):
        """Update round quantity  by tournament id

        Args:
            tournament_obj(Tournament): Current tournament

        """
        from models.tournament import Tournament
        tournament_obj: Tournament = tournament
        tournament_id = tournament_obj.get_id
        tournament_round_quantity = tournament_obj.get_round
        if tournament_id:
            self.tournaments_table.update(
                {"round": tournament_round_quantity}, doc_ids=[tournament_id])

    def update_status_by_tournament_id(self, tournament):
        """Update tournament status  by tournament id

        Args:
            tournament_obj(Tournament): Current tournament

        """
        from models.tournament import Tournament
        tournament_obj: Tournament = tournament
        tournament_id = tournament_obj.get_id
        tournament_status = tournament_obj.get_status
        if tournament_id:
            self.tournaments_table.update(
                {"status": tournament_status}, doc_ids=[tournament_id])

    def add_one(self, tournament) -> bool | int:
        """Add a tournament base info on db

        Args:
            Tournament: tournament obj

        Return:
            bool | int: True if succes, tournament id if already exist

        """

        from models.tournament import Tournament
        tournament_obj: Tournament = tournament
        tournament_dict = tournament_obj.get_tournament
        tournament = Query()
        # tournament exist ?
        if not self.tournaments_table.search(
                (
                    tournament.name == tournament_dict["name"]
                ) & (
                    tournament.date == tournament_dict["date"]
                )):
            # Create tournament in db and get id
            tournament_id = self.tournaments_table.insert(tournament_dict)
            tournament_obj.set_id(tournament_id)
            return True
        else:
            # Tournament already exist, find is id and return it
            tournament_document = self.tournaments_table.get(
                (
                    tournament.name == tournament_dict["name"]
                ) & (
                    tournament.date == tournament_dict["date"]
                )
            )
            if tournament_document:
                tournament_id = tournament_document.doc_id
                return tournament_id
        # This never append
        return False

    def update_rounds_by_tournament_id(self, tournament):
        """Update rounds of one tournament on db

        Args:
            str: name of tournament
            str: date of tournament
            list[Round]: list of round obj to update

        """
        from models.tournament import Tournament
        tournament_obj: Tournament = tournament
        tournament_id = tournament_obj.get_id
        if tournament_id:
            tournament_rounds = tournament_obj.get_rounds
            # Build rounds infos
            rounds_list = []
            for round in tournament_rounds:
                round_to_add = {"name": round.get_name}
                round_to_add["date_begin"] = round.get_begin
                if round.get_end is not None:
                    round_to_add["date_end"] = round.get_end
                # Build matchs infos
                matchs = []
                for match in round.get_matchs:
                    match_to_add = {
                        "player_1": match.get_players[0].get_player,
                        "player_2": match.get_players[1].get_player,
                        "score_player_1": match.get_scores[0],
                        "score_player_2": match.get_scores[1]}
                    matchs.append(match_to_add)
                round_to_add.update({"matchs": matchs})
                rounds_list.append(round_to_add)
            # Update database
            self.tournaments_table.update(
                {"rounds": rounds_list}, doc_ids=[tournament_id])

    def _create_players_obj_list(self, tournament_id):
        """Create a list of player obj of a tournament

        Args:
            int: tournament id

        Return:
            list[Player]: list of player obj

        """
        from models.player import Player
        manager_player_obj = Db_manager_player()
        players_obj_list: list[Player] = []
        tournament_document = self.tournaments_table.get(doc_id=tournament_id)
        if tournament_document:
            tournament_players_dict = tournament_document["players"]
            # Extract players_id from key
            players_id: list[int] = [key for key in tournament_players_dict]
            for player_id in players_id:
                # Find data of one player
                player_obj = manager_player_obj.get_by_id(player_id)
                if player_obj:
                    player_dict = player_obj.get_player
                    # Create one player
                    player_obj_to_add: Player = Player.add_player_from_db(
                        player_dict["name"],
                        player_dict["surname"],
                        player_dict["birth_date"],
                        player_dict["gender"],
                        player_dict["classification"],
                        player_id)
                    # Add Player to Players for this tournament
                    players_obj_list.append(player_obj_to_add)
            return players_obj_list
        return[]


import glob
import os
import pickle
from collections import defaultdict
from multiprocessing import Pool

import torch as torch
from tqdm import tqdm
from rouge_utils import _get_word_ngrams, cal_rouge
import dill

def _cal_rouge(sents, abstract):

    evaluated_1grams = [_get_word_ngrams(1, [sent.split()]) for sent in sents]
    reference_1grams = _get_word_ngrams(1, [abstract[0].split()])
    evaluated_2grams = [_get_word_ngrams(2, [sent.split()]) for sent in sents]
    reference_2grams = _get_word_ngrams(2,  [abstract[0].split()])

    candidates_1 = [evaluated_1grams[idx] for idx in range(len(evaluated_1grams))]
    candidates_1 = set.union(*map(set, candidates_1))
    candidates_2 = [evaluated_2grams[idx] for idx in range(len(evaluated_2grams))]
    candidates_2 = set.union(*map(set, candidates_2))

    rouge_1 = cal_rouge(candidates_1, reference_1grams)['f']
    rouge_2 = cal_rouge(candidates_2, reference_2grams)['f']

    return (rouge_1 + rouge_2) / 2

def _mp_rouge(params):
    i, j ,p_id, src_sent_1, src_sent_2, tgt = params
    rg_sent_1 = _cal_rouge([src_sent_1], [tgt])
    rg_sent_2 = _cal_rouge([src_sent_1] + [src_sent_2], [tgt])
    return {
        'index': (i, j),
        'rg_diff': rg_sent_2 - rg_sent_1,
        'paper_id': p_id
    }

if __name__ == '__main__':
    BASE_DIR = os.getenv('BERT_DIR')

    if not os.path.exists(BASE_DIR[:-1] + '-graph'):
        os.makedirs(BASE_DIR[:-1] + '-graph')

    BASE_DIR_WR = BASE_DIR[:-1] + '-graph'

    for se in ['train', 'val', 'test']:
    # for se in ['train']:
        prepare_array = []
        print('populating the ROUGE list...')

        for f in tqdm(glob.glob(BASE_DIR + '/' + se + '*.pt'), total=len(glob.glob(BASE_DIR + '/' + se + '*.pt'))):
            datasets = torch.load(f)

            for d in datasets:
                src_sents = d['src_tokens']
                if len(src_sents) == len(d['src_sent_labels']):
                    src_sents = [' '.join(s) for s in src_sents]
                    tgt = d['tgt_txt']
                    p_id = d['paper_id']

                    for i, src_sent_1 in enumerate(src_sents):
                        for j, src_sent_2 in enumerate(src_sents):
                            if j > i:
                                prepare_array.append((i, j, p_id, src_sent_1, src_sent_2, tgt))
                            # _mp_rouge(prepare_array[-1])
                else:
                    import pdb;pdb.set_trace()

        paper_id_edges =  defaultdict(lambda: defaultdict(dict))
        pool = Pool(15)

        # compute pair-wise ROUGE

        for out in tqdm(pool.imap_unordered(_mp_rouge, prepare_array), total=len(prepare_array)):
            paper_id_edges[out['paper_id']][out['index'][0]][out['index'][1]] = out['rg_diff']

        pool.close()
        pool.join()

        print(f'Now saving {se} graph...')

        torch.save(
            obj=paper_id_edges,
            f=f'/disk1/sajad/datasets/sci/arxivL/rouge_edge/{se}.pkl',
            pickle_module=dill
        )

        print(f'Now re-saving {se} torch files...')

        for f in tqdm(glob.glob(BASE_DIR + '/' + se + '*.pt'), total=len(glob.glob(BASE_DIR + '/' + se + '*.pt'))):
            datasets = torch.load(f)
            new_datasets = []

            for d in datasets:
                edge_weights = []
                src_sents = d['src_tokens']
                p_id = d['paper_id']

                for i, src_sent_1 in enumerate(src_sents):
                    for j, src_sent_2 in enumerate(src_sents):
                        if i == j:
                            edge_weights.append(0.0)

                        elif i > j:
                            edge_weight = paper_id_edges[p_id][j][i]
                            edge_weights.append(edge_weight)

                        elif i < j:
                            edge_weight = paper_id_edges[p_id][i][j]
                            edge_weights.append(edge_weight)

                d['edge_weights'] = edge_weights
                new_datasets.append(d)

            torch.save(new_datasets, f.replace('new', 'new-graph'))


# beer_app/update/views.py
from io import TextIOWrapper
import csv
from flask import render_template, url_for, request, redirect, Blueprint
from flask_login import current_user, login_required
from beer_app import db
from beer_app.models import Item, Location, Zone
from beer_app.update.forms import ItemForm, LocationForm, ZoneForm

update = Blueprint('update', __name__)

@update.route('/add_zone_form', methods=['GET', 'POST'])
@login_required
def add_zone_form():
    form = ZoneForm()

    if form.validate_on_submit():
        zone = Zone(name=form.name.data)
        db.session.add(zone)
        db.session.commit()
        return redirect(url_for('update.add_zone_form'))

    return render_template('add_zone_form.html', form=form)

@update.route('/add_zone_csv', methods=['GET', 'POST'])
@login_required
def add_zone_csv():

    if request.method == 'POST':
        csv_reader = upload_csv()
        for row in csv_reader:
            zone = Zone(name=row[1])
            db.session.add(zone)
            db.session.commit()
        return redirect(url_for('update.add_zone_form'))

    return render_template('add_zone_csv.html')

@update.route('/add_location_form', methods=['GET', 'POST'])
@login_required
def add_location_form():
    form = LocationForm()
    form.zone_id.choices = [(0,'--Select a Neighborhood--')]
    form.zone_id.choices += [
                                                (zone.id, zone.name)
                                                for zone
                                                in Zone.query.order_by('name')
                                            ]

    if form.validate_on_submit():
        location = Location(name=form.name.data,
                            address=form.address.data,
                            phone=form.address.data,
                            website=form.address.data,
                            zone_id=form.zone_id.data
                            )
        db.session.add(location)
        db.session.commit()
        return redirect(url_for('update.add_location_form'))

    return render_template('add_location_form.html', form=form)

@update.route('/add_location_csv', methods=['GET', 'POST'])
@login_required
def add_location_csv():
    if request.method == 'POST':
        csv_reader = upload_csv()
        for row in csv_reader:
            location = Location(name=row[1],
                                address=row[2],
                                phone=row[3],
                                website=row[4],
                                zone_id=row[5]
                                )
            db.session.add(location)
            db.session.commit()
        return redirect(url_for('update.add_location_form'))
    return render_template('add_location_csv.html')

@update.route('/add_item_form', methods=['GET', 'POST'])
@login_required
def add_item_form():
    form = ItemForm()
    form.location.choices = [(0, '--Select a Brewery--')]
    form.location.choices += [
                                                (loc.id, loc.name)
                                                for loc
                                                in Location.query.order_by('name')
                                              ]

    if form.validate_on_submit():
        item = Item(location_id=form.location.data,
                    name=form.name.data,
                    style=form.style.data,
                    abv=form.abv.data,
                    ibu=form.ibu.data)
        db.session.add(item)
        db.session.commit()
        return redirect(url_for('update.add_item_form'))

    return render_template('add_item_form.html', form=form)

@update.route('/add_item_csv', methods=['GET','POST'])
@login_required
def add_item_csv():
    if request.method == 'POST':
        csv_reader = upload_csv()
        for row in csv_reader:
            item = Item(name=row[1],
                        style=row[2],
                        abv=row[3],
                        ibu=row[4],
                        location_id=row[5]
                        )
            db.session.add(item)
            db.session.commit()
        return redirect(url_for('update.add_item_form'))

    return render_template('add_item_csv.html')

def upload_csv():
    csv_file = request.files['file']
    csv_file = TextIOWrapper(csv_file)
    return csv.reader(csv_file, delimiter=',')


import threading

from datetime import timedelta

from cachetools import LRUCache
from .rabbit_queue import RMQueue

queue_cache = {}

def get_cached_queue(
        name,
        route,
        cloud=None,
        config=None,
        logger=None,
        close_after=timedelta(),
        verbose=10):
    # if queue_cache.get(name, None) is None:
    #    queue_cache[name] = RMQueue(
    #        queue=name,
    #        route=route,
    #        config=config,
    #        logger=logger,
    #        verbose=verbose)
    #
    # return queue_cache[name]
    q = RMQueue(
        queue=name,
        route=route,
        config=config,
        logger=logger,
        verbose=verbose)
    if close_after is not None and close_after.total_seconds() > 0:
        thr = threading.Timer(
            interval=close_after.total_seconds(),
            function=purge_rmq,
            kwargs={
                "q": q,
                "logger": logger})
        thr.setDaemon(True)
        thr.start()

    return q

def purge_rmq(q, logger, **kwargs):
    if q is None:
        return

    try:
        q.stop()
    except BaseException as e:
        logger.warning(e)
        return
    return