#!/usr/bin/python3

#The simulation is performed on top of the casper simulation codes available from the Stanford project. (https://github.com/omoindrot/simple_casper)

import os
import numpy as np

from parameters import *
from block import Block
from utils import exponential_latency
from network import Network
from validator import VoteValidator
#from plot_graph import plot_node_blockchains
from collections import Counter
from tqdm import tqdm
from pandas import DataFrame
import random
import networkx as nx

def fraction_justified_and_finalized(validator):
    """Compute the fraction of justified and finalized checkpoints in the main chain.

    From the genesis block to the highest justified checkpoint, count the fraction of checkpoints
    in each state.
    """
    # Get the main chain
    checkpoint = validator.highest_justified_checkpoint

    count_justified = 0
    count_finalized = 0
    count_total = 0
    while checkpoint is not None:
        count_total += 1
        if checkpoint.hash in validator.justified:
            count_justified += 1
        if checkpoint.hash in validator.finalized:
            count_finalized += 1
        checkpoint = validator.get_checkpoint_parent(checkpoint)

    fraction_justified = float(count_justified) / float(count_total)
    fraction_finalized = float(count_finalized) / float(count_total)
    count_forked_justified = len(validator.justified) - count_justified
    fraction_forked_justified = float(count_forked_justified) / float(count_total)
    return fraction_justified, fraction_finalized, fraction_forked_justified

def delay_throughput(network):
    delay = {}
    Edelay = 0.0
    E2delay = 0.0
    delay_array = np.array([])
    for block_hash in network.global_finalized_time_absolute :
        delay[block_hash] = network.global_finalized_time_absolute[block_hash]  - network.first_proposal_time[block_hash]
    for block_hash in delay :
        delay_array = np.append(delay_array,delay[block_hash])
        Edelay += delay[block_hash]
        E2delay += delay[block_hash]**2
    if len(delay)>0:
        Edelay = Edelay/len(delay)
        E2delay = E2delay/len(delay)
    else:
        Edelay = Edelay
        E2delay = E2delay
    min_time = BLOCK_PROPOSAL_TIME*EPOCH_SIZE*NUM_EPOCH
    max_time = 1
    max_epoch=0
    for block_hash in network.global_finalized_time_absolute:
        if(network.global_finalized_time_absolute[block_hash]>=max_time):
            max_time = network.global_finalized_time_absolute[block_hash]
    for block_hash in network.global_finalized_time_absolute:
        if(network.first_proposal_time[block_hash]<=min_time):
            min_time = network.first_proposal_time[block_hash]
    for block_hash in network.global_finalized_time_absolute:
        if((network.processed[block_hash].height)/EPOCH_SIZE>=max_epoch):
            max_epoch = (network.processed[block_hash].height)/EPOCH_SIZE

    if len(network.global_finalized_time)>0:
        throughput = max_epoch/(max_time)       #Removed min_time
    else:
        throughput = 0  #max_time - min_time

    return Edelay, E2delay, throughput, len(network.global_finalized_time), delay_array

def timing_chain(network):
    timing = {}
    sum_timing = [0,0,0,0,0,0,0,0]
    timing_count = [0,0,0,0,0,0,0,0]
    for blockhash in network.global_finalized_time:
        timing[blockhash] = [network.first_justification_time.get(blockhash,-1), network.global_justified_time.get(blockhash,-1), \
        network.first_justified_time.get(blockhash,-1), network.last_justified_time.get(blockhash,-1), network.first_finalization_time.get(blockhash,-1), \
        network.global_finalized_time.get(blockhash,-1), network.first_finalized_time.get(blockhash,-1), network.last_finalized_time.get(blockhash,-1)  ]
    for blockhash in timing:
        for i in range(len(timing[blockhash])):
            if timing[blockhash][i] == -1:
                pass
            else:
                sum_timing[i] = sum_timing[i] + timing[blockhash][i]
                timing_count[i] = timing_count[i] + 1

    return np.array(sum_timing), np.array(timing_count)

'''

def depth_calculator(validator):
    depth_finalized = 0
    num_depth_finalized =0
    main_chain_size = validator.main_chain_size
    max_epoch=0
    for blockhash in validator.finalized:
        if validator.processed[blockhash].height > max_epoch:
            max_epoch = validator.processed[blockhash].height
        num_depth_finalized = 1
    depth_finalized = (main_chain_size - max_epoch)/EPOCH_SIZE
    return depth_finalized, num_depth_finalized

'''

def finalization_quartiles(network):
    sumquartiles = [0.0,0.0,0.0,0.0]
    sumsquarequartiles = [0.0,0.0,0.0,0.0]
    len_valid_sums = [0.0,0.0,0.0,0.0]

    Equartiles = [0.0,0.0,0.0,0.0]
    E2quartiles = [0.0,0.0,0.0,0.0]
    stdquartiles = [0.0,0.0,0.0,0.0]

    for blockhash in network.final_quartiles:
        for i in range(4):
            if len(network.final_quartiles[blockhash])>i:
                sumquartiles[i] = sumquartiles[i] + network.final_quartiles[blockhash][i]
                sumsquarequartiles[i] += network.final_quartiles[blockhash][i]**2
                len_valid_sums[i] +=1
    '''
    for i in range(4):
        if len_valid_sums[i]>0:
            Equartiles[i] = Equartiles[i]/len_valid_sums[i]
            E2quartiles[i] = E2quartiles[i]/len_valid_sums[i]
            stdquartiles[i] = E2quartiles[i] - Equartiles[i]**2
            stdquartiles[i] = np.sqrt(stdquartiles[i])
        else:
            Equartiles[i] = None
            stdquartiles[i] = None
    '''
    print(len_valid_sums)
    return sumquartiles,sumsquarequartiles,len_valid_sums

def main_chain_size(validator):
    """Computes the number of blocks in the main chain."""
    return validator.highest_justified_checkpoint.height + 1

def blocks_under_highest_justified(validator):
    """Computes the height of blocks below the checkpoint of highest height."""
    res = 0
    for bhash, b in validator.processed.items():
        if isinstance(b, Block):
            if b.height <= validator.highest_justified_checkpoint.height:
                res += 1
    return res

def total_height_blocks(validator):
    """Total height of blocks processed by the validator.
    """
    res = 0
    for bhash, b in validator.processed.items():
        if isinstance(b, Block):
            res += 1
    return res

def count_forks(validator):
    """Compute the height of forks of each size.

    Returns a dict {1: 24, 2: 5, 3: 2} for instance.
    Compute forks up until the highest justified checkpoint.
    """
    # Compute a list of the block hashes in the main chain, up to the highest justified checkpoint.
    block = validator.highest_justified_checkpoint
    block_hash = block.hash
    main_blocks = [block_hash]

    # Stop when we reach the genesis block
    while block.height > 0:
        block_hash = block.prevhash
        block = validator.processed[block_hash]
        main_blocks.append(block_hash)

    # Check that we reached the genesis block
    assert block.height == 0
    assert len(main_blocks) == validator.highest_justified_checkpoint.height + 1

    # Now iterate through the blocks with height below highest_justified
    longest_fork = {}
    for block_hash, block in validator.processed.items():
        if isinstance(block, Block):
            if block.height <= validator.highest_justified_checkpoint.height:
                # Get the closest parent of block from the main blockchain
                fork_length = 0
                while block_hash not in main_blocks:
                    fork_length += 1
                    block_hash = block.prevhash
                    block = validator.processed[block_hash]
                assert block_hash in main_blocks
                longest_fork[block_hash] = max(longest_fork.get(block_hash, 0), fork_length)

    count_forks = {}
    for block_hash in main_blocks:
        l = longest_fork[block_hash]
        count_forks[l] = count_forks.get(l, 0) + 1

    assert sum(count_forks.values()) == validator.highest_justified_checkpoint.height + 1
    return count_forks

def print_metrics_latency (G, num_tries, latencies,
                          wait_fractions, vote_as_block, immediate_vote,
                          wait_for_majority, vote_confidence, validator_set=VALIDATOR_IDS):
    # metrics for analysis
    tp      = []
    delay   = []
    depth   = []
    mcf     = []

    for latency in latencies:

        #fcsum = {}
        new_delay_list      = []
        old_delay_list      = []
        tp_list             = []
        depth_list          = []
        wait_fraction_list  = []
        MCF_list            = []

        for wf in wait_fractions:

            print('wait_fraction: {}'.format(wf))

            sum_type_1_vote         = 0
            sum_type_2_vote         = 0
            mining_ids              = [i for i in range(NUM_VALIDATORS)]
            jfsum                   = 0.0
            squarejfsum             = 0.0
            ffsum                   = 0.0
            squareffsum             = 0.0
            jffsum                  = 0.0
            squarejffsum            = 0.0
            mcsum                   = 0.0
            squaremcsum             = 0.0
            busum                   = 0.0
            squarebusum             = 0.0
            delaysum                = 0.0
            squaredelaysum          = 0.0
            throughputsum           = 0.0
            squarethroughputsum     = 0.0
            total_finalized         = 0.0
            num_finalized_tries     = 0.0
            finalization_achieved   = True
            Equartiles              = [0.0,0.0,0.0,0.0]
            E2quartiles             = [0.0,0.0,0.0,0.0]
            stdquartiles            = [0.0,0.0,0.0,0.0]
            len_valid_sums          = [0.0,0.0,0.0,0.0]
            Etiming                 = np.zeros((8))
            timing_count            = np.zeros((8))
            sml_stats               = {}
            depth_finalized         = 0
            num_depth_finalized     = 0
            delay_array = np.array([])
            mcf_hist_array = np.array([])
            prev_mc_sum = 0

            for i in range(num_tries):
                network = Network(G.adj, latency)
                validators = [VoteValidator(network,latency,wf, i,vote_as_block, immediate_vote, wait_for_majority, vote_confidence) for i in validator_set]

                for t in tqdm(range(BLOCK_PROPOSAL_TIME * EPOCH_SIZE * NUM_EPOCH)):
                #for t in range(3):
                    network.tick(sml_stats)
                    # The code below does random shuffling of mining order
                    if t%BLOCK_PROPOSAL_TIME*NUM_VALIDATORS ==0:
                        random.shuffle(mining_ids)
                        for val in validators:
                            val.mining_id = mining_ids[val.id]

                    # if t % (BLOCK_PROPOSAL_TIME * EPOCH_SIZE) == 0:
                    #     filename = os.path.join(LOG_DIR, 'plot_{:03d}.png'.format(t))
                    #     plot_node_blockchains(validators, filename)

                for val in validators:
                    jf, ff, jff = fraction_justified_and_finalized(val)
                    jfsum += jf
                    squarejfsum += jf**2
                    ffsum += ff
                    squareffsum += ff**2
                    jffsum += jff
                    squarejffsum += jff**2
                    mcsum += main_chain_size(val)
                    squaremcsum += main_chain_size(val)**2
                    busum += blocks_under_highest_justified(val)
                    squarebusum += blocks_under_highest_justified(val)**2
                    temp_depth_finalized = val.depth_finalized
                    temp_num_depth_finalized = val.num_depth_finalized
                    num_depth_finalized += temp_num_depth_finalized
                    depth_finalized += temp_depth_finalized
                    sum_type_1_vote += val.type_1_vote
                    sum_type_2_vote += val.type_2_vote

                    #fc = count_forks(val)
                    #for l in fc.keys():
                        #fcsum[l] = fcsum.get(l, 0) + fc[l]

                Edelay,E2delay,throughput,num_finalized, temp_delay_array = delay_throughput(network)
                delay_array = np.append(delay_array,temp_delay_array)
                delaysum += Edelay*num_finalized
                squaredelaysum += E2delay*num_finalized
                if num_finalized>0:
                    throughputsum +=throughput
                    num_finalized_tries = num_finalized_tries + 1
                    squarethroughputsum += throughput**2
                total_finalized += num_finalized

                tempEquartiles,tempE2quartiles,templen_valid_sums = finalization_quartiles(network)
                for i in range(4):
                    if templen_valid_sums[i]>0 :
                        Equartiles[i] += tempEquartiles[i]
                        E2quartiles[i] += tempE2quartiles[i]
                        len_valid_sums[i] += templen_valid_sums[i]

                temp_timing,temp_timing_count = timing_chain(network)
                Etiming += temp_timing
                timing_count += temp_timing_count

                mcf_hist_array = np.append(mcf_hist_array, (mcsum-prev_mc_sum)/len(validators)/NUM_EPOCH/EPOCH_SIZE)
                prev_mc_sum = mcsum

            sd_delay = 0
            if total_finalized > 0 :
                Edelay = delaysum/total_finalized
                E2delay = squaredelaysum/total_finalized
                vardelay = E2delay - Edelay**2
                sd_delay = np.sqrt(vardelay)
                Ethroughput = throughputsum/num_finalized_tries
                E2throughput = squarethroughputsum/num_finalized_tries
                varthroughput = E2throughput - Ethroughput**2
            else:
                finalization_achieved = False
                #print('No finalization Achieved')

            for i in range(4):
                if len_valid_sums[i]>0:
                    Equartiles[i] = Equartiles[i]/len_valid_sums[i]
                    E2quartiles[i] = E2quartiles[i]/len_valid_sums[i]
                    stdquartiles[i] = E2quartiles[i] - Equartiles[i]**2
                    stdquartiles[i] = np.sqrt(stdquartiles[i])
                else:
                    Equartiles[i] = None
                    stdquartiles[i] = None

            Ejf = jfsum/len(validators)
            E2jf = squarejfsum/len(validators)
            Ejff = jffsum/len(validators)
            E2jff = squarejffsum/len(validators)
            Eff = ffsum/len(validators)
            E2ff = squareffsum/len(validators)
            Emc = mcsum/len(validators)
            E2mc = squaremcsum/len(validators)
            Ebu = busum/len(validators)
            E2bu = squarebusum/len(validators)
            varjf = E2jf - Ejf**2
            varjff = E2jff - Ejff**2
            varff = E2ff - Eff**2
            varmc = E2mc - Emc**2
            varbu = E2bu - Ebu**2

            depth_finalized = depth_finalized/num_depth_finalized

            Etiming = Etiming/timing_count

            tp += [Ethroughput]
            delay += [Edelay/(Emc/(EPOCH_SIZE*NUM_EPOCH + 1))]
            depth += [depth_finalized]
            mcf += [Emc/(EPOCH_SIZE*NUM_EPOCH + 1)]

            delay_array = delay_array/(Emc/(EPOCH_SIZE*NUM_EPOCH + 1))

            delay_quartiles = [np.percentile(delay_array,25),np.percentile(delay_array,50),np.percentile(delay_array,75),np.percentile(delay_array,100)]

            print("MCF hist array:{}".format(mcf_hist_array))
            mcf_hist = np.histogram(mcf_hist_array, bins = 10, density=True)
            print("MCF hist:{}".format(mcf_hist))
            print('=== Statistics ===')
            print('Latency: {}'
                    .format(latency))
            print('Timing: {}'
                    .format(Etiming))
            print('Bar_graph: {}'
                    .format([Etiming[1], (Etiming[2]-Etiming[1]),
                            (Etiming[4]-Etiming[2]),(Etiming[5]-Etiming[4]),
                            (Etiming[6]-Etiming[5]),(Etiming[7]-Etiming[6])]))
            print('Justified in forks: {}'
                    .format([Ejff,np.sqrt(varjff)]))
            print('Main chain size (root included): {}'
                    .format([Emc,np.sqrt(varmc)]))
            print('Probability of death: {}'
                    .format(1.0 - Emc/(EPOCH_SIZE*NUM_EPOCH + 1)))  # include ROOT
            print('Blocks under main justified: {}'
                    .format([Ebu,varbu]))
            print('finalization_quartiles:{}'
                    .format([Equartiles,stdquartiles]))
            print('Main chain fraction:{}'
                    .format([Emc/(EPOCH_SIZE*NUM_EPOCH + 1),        # include ROOT
                            np.sqrt(varmc)/EPOCH_SIZE/NUM_EPOCH ]))
            print('type_1_vote: {}'.format(sum_type_1_vote))
            print('type_2_vote: {}'.format(sum_type_2_vote))

            if finalization_achieved :
                print('---new, incld. dead blocks---')
                print('Delay:{}'
                        .format(Edelay/(Emc/(EPOCH_SIZE*NUM_EPOCH + 1))))
                print('Delay:{}'.format(np.mean(delay_array)))
                print('delay_quartiles: {}'.format(delay_quartiles))
                print('sd_delay:{}'.format(sd_delay/(Emc/(EPOCH_SIZE*NUM_EPOCH + 1))))
                print('Throughput:{}'
                        .format(Ethroughput))
                print('depth:{}'
                        .format(depth_finalized))
                print('---old, ignoring dead blocks---')
                print('Delay:{}'
                        .format([Edelay,vardelay]))
                print('Throughput:{}'
                        .format([Ethroughput,varthroughput]))
            else:
                print('No finalization achieved')
            print('supermajority link stats: {}'
                    .format(dict(Counter(sml_stats.values()))))
            print('=== END === ')

            wait_fraction_list.append(wf)
            new_delay_list.append(Edelay/(Emc/(EPOCH_SIZE*NUM_EPOCH + 1)))
            old_delay_list.append(Edelay)
            tp_list.append(Ethroughput)
            depth_list.append(depth_finalized)
            MCF_list.append(Emc/(EPOCH_SIZE*NUM_EPOCH + 1))

        print('wait_fraction_list: {}'.format(wait_fraction_list))
        print('new_delay_list: {}'.format(new_delay_list))
        print('tp_list: {}'.format(tp_list))
        print('depth_list: {}'.format(depth_list))
        print('MCF_list: {}'.format(MCF_list))
        print('old_delay_list: {}'.format(old_delay_list))

        return (tp, delay, depth, mcf)

if __name__ == '__main__':
    # LOG_DIR = 'metrics'
    # if not os.path.exists(LOG_DIR):
        # os.makedirs(LOG_DIR)

    # Uncomment to have fractions of disconnected nodes
    # fractions = np.arange(0.0, 0.4, 0.05)
    # fractions = [0.31, 0.32, 0.33]
    fractions = [0.0]
    vote_as_block = False
    immediate_vote = True
    wait_for_majority = False
    vote_confidence = False

    print('``````````````````')
    print("""running test
            PROTOCOL: vote-on-majority-after-delay
            NUM_EPOCH: {}
            SUPER_MAJORITY: {}
            NUM_VALIDATORS: {}
            D_REGULAR: {}
            Vote_as_block: {}
            Immediate_vote: {}
            Wait_for_majority: {}
            Vote_confidence: {}""".
            format(NUM_EPOCH,
                   SUPER_MAJORITY,
                   NUM_VALIDATORS,
                   D_REGULAR,
                   vote_as_block,
                   immediate_vote,
                   wait_for_majority,
                   vote_confidence))
    print('``````````````````')

    for fraction_disconnected in fractions:
        num_validators = int((1.0 - fraction_disconnected) * NUM_VALIDATORS)
        validator_set = VALIDATOR_IDS[:num_validators]

        print("height of connected of nodes: {}".format(len(validator_set)))

        num_tries = 100
        #latencies = [i for i in range(10, 300, 20)] + [500, 750, 1000]
        latencies = [75]
        wait_fractions =  [0.0]

        # Graph
        # G = nx.complete_graph(num_validators)                # fully connected
        G = nx.random_regular_graph(D_REGULAR,num_validators)  # d-regular

        tp, delay, depth, mcf = print_metrics_latency(G, num_tries,latencies, \
                                                      wait_fractions,
                                                        vote_as_block,
                                                        immediate_vote,
                                                        wait_for_majority,
                                                        vote_confidence,
                                                        validator_set)

        # save data to test.xlsx
        df = DataFrame({"latency":latencies,
                        "throughput":tp,
                        "mcf": mcf,
                        "delay":delay,
                        "depth":depth
                        },
                        index = wait_fractions).T

        df.to_excel('test.xlsx',sheet_name='sheet1',index=False)


import os

import pygame

pygame.init()

clock = pygame.time.Clock()

score = 0
sw = 800
sh= 500
win = pygame.display.set_mode((sw, sh))

walk_right = []
walk_left = []

dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'media')
os.chdir(filename)

# attribute iamge files for animation
for i in range(1, 10):
    walk_right.append(pygame.image.load('R{}.png'.format(i)))

for k in range(1, 10):
    walk_left.append(pygame.image.load('L{}.png'.format(k)))
    print(k)

bg = pygame.image.load('bg.jpg')
char = pygame.image.load('standing.png')

bullet_sound = pygame.mixer.Sound("bullet.wav")
hit_sound = pygame.mixer.Sound("hit.wav")

music = pygame.mixer.music.load("music.mp3")

os.chdir(dir)

# play music forever
pygame.mixer.music.play(-1)

class player(object):
    def __init__(self, x, y, w, h):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.v = 5
        self.is_jump = False
        self.jump_count = 10
        self.left = False
        self.right = False
        self.walk_count = 0
        self.standing = True
        self.hitbox = (self.x + 17, self.y + 11, 29, 52)

    def draw(self, win):
        if self.walk_count + 1 >= 27:
            self.walk_count = 0
        if not(self.standing):
            if self.left:
                win.blit(walk_left[self.walk_count//3], (self.x, self.y))
                self.walk_count += 1
            elif self.right:
                win.blit(walk_right[self.walk_count//3], (self.x, self.y))
                self.walk_count += 1
        else:
            if self.right:
                win.blit(walk_right[0], (self.x, self.y))
            else:
                win.blit(walk_left[0], (self.x, self.y))
        self.hitbox = (self.x + 17, self.y + 11, 29, 52)

    def hit(self):
        self.is_jump = False
        self.jump_count = 10
        self.x = 60
        self.y = 420
        self.walk_count = 0
        font1 = pygame.font.SysFont('comicsans', 100)
        text = font1.render('-5', 1, (255, 0 ,0))
        win.blit(text, (sw // 2 - (text.get_width()), sh // 2 - (text.get_height()) ))
        pygame.display.update()
        i = 0
        while i < 100:
            pygame.time.delay(10)
            i += 1
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    i = 301
                    pygame.quit()

class projectile(object):
    def __init__(self, x, y, r, color, facing,):
        self.x = x
        self.y = y
        self.r = r
        self.color = color
        self.facing = facing
        self.v = 8
        # retain direction based on direction jeff is facing.
        if jeff.left:
            self.facing = -1
        else:
            self.facing = 1

    def draw(self, win):
        pygame.draw.circle(win, self.color, (self.x, self.y), self.r)

class enemy(object):
    # attribute image files specifically for enemy animation
    os.chdir(filename)
    walk_right = []
    walk_left = []

    for o in range(1,12):
        walk_right.append(pygame.image.load('R{}E.png'.format(o)))

    for j in range(1,12):
        walk_left.append(pygame.image.load('L{}E.png'.format(j)))

    os.chdir(dir)

    def __init__(self, x, y, w, h, end):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.end = end
        self.path = [self.x, self.end]
        self.walk_count = 0
        self.v = 3
        self.hitbox = (self.x + 17, self.y + 2, 31, 57)
        self.health = 10
        self.visible = True

    def draw(self,win):
        self.move()
        if self.visible:
            if self.walk_count +1 >= 33:
                self.walk_count = 0

            if self.v > 0:
                win.blit(self.walk_right[self.walk_count //3], (self.x, self.y) )
                self.walk_count += 1
            else:
                win.blit(self.walk_left[self.walk_count //3], (self.x, self.y) )
                self.walk_count += 1
            self.hitbox = (self.x + 17, self.y + 2, 31, 57)
            pygame.draw.rect(win, (255, 0 ,0) , (self.hitbox[0], self.hitbox[1] - 20, 50, 10))
            pygame.draw.rect(win, (0, 123, 0) , (self.hitbox[0], self.hitbox[1] - 20, 50 - (5 * (10 - self.health)), 10))

    def move(self):
        if self.v > 0:
            if self.x  + self.v < self.path[1]:
                self.x += self.v
            else:
                self.v = self.v * -1
                self.walk_count = 0
        else:
            if self.x - self.v > self.path[0]:
                self.x += self.v
            else:
                self.v = self.v * -1
                self.walk_count = 0

    def hit(self):
        if self.health > 0:
            self.health -= 1
        else:
            self.visible = False

def redrawGameWin():
    win.blit(bg, (0,0))
    text = font.render('Score: ' + str(score), 1, (255, 255, 255))
    win.blit(text , (20, 20))
    jeff.draw(win)
    goblin.draw(win)
    for bullet in bullets:
        bullet.draw(win)
    pygame.display.update()

# mainloop
font = pygame.font.SysFont('comicsans', 30, True)
bullets = []
goblin = enemy(100, 420, 64, 64, 700)
jeff = player(20,420, 64, 64)
shoot_loop = 0
run = True

while run:
    # set FPS
    clock.tick(27)

    # allow collision damage unless goblin is dead
    if goblin.visible == True:
        if jeff.hitbox[1] < goblin.hitbox[1] + goblin.hitbox[3] and jeff.hitbox[1] + jeff.hitbox[1] > goblin.hitbox[1]:
            if jeff.hitbox[0] + jeff.hitbox[2] > goblin.hitbox[0] and jeff.hitbox[0] < goblin.hitbox[0] + goblin.hitbox[2]:
                jeff.hit()
                score -= 5

    # delay to prevent multiple bullets being shot at a time
    if shoot_loop > 0:
        shoot_loop += 1
    if shoot_loop > 1:
        shoot_loop = 0

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    keys = pygame.key.get_pressed()

    # bullet damage against goblin
    for bullet in bullets:
        if goblin.visible == True:
            if bullet.y - bullet.r < goblin.hitbox[1] + goblin.hitbox[3] and bullet.y + bullet.r > goblin.hitbox[1]:
                if bullet.x + bullet.r > goblin.hitbox[0] and bullet.x+ bullet.r < goblin.hitbox[0] + goblin.hitbox[2]:
                    hit_sound.play()
                    goblin.hit()
                    score += 1
                    bullets.pop(bullets.index(bullet))

        if bullet.x < sw and bullet.x > 0:
            bullet.x += bullet.v * bullet.facing
        else:
            bullets.pop(bullets.index(bullet))

    if jeff.left:
        facing = -1
    else:
        facing = 1

    #input data
    if keys[pygame.K_SPACE] and shoot_loop == 0:
       if len(bullets) < 5:
           bullet_sound.play()
           bullets.append(projectile(round(jeff.x + jeff.w // 2), round(jeff.y + jeff.h // 2), 5, (0,0,0), facing ))
    if keys[pygame.K_LEFT] and jeff.x > jeff.v:
        jeff.x -= jeff.v
        jeff.left = True
        jeff.right = False
        jeff.standing = False
    elif keys[pygame.K_RIGHT] and jeff.x < sw - jeff.w:
        jeff.x += jeff.v
        jeff.right = True
        jeff.left = False
        jeff.standing = False
    else:
        jeff.walk_count = 0
        jeff.standing = True

    if not(jeff.is_jump):
        if keys[pygame.K_UP] and jeff.is_jump is False:
            jeff.is_jump = True
            jeff.left = False
            jeff.right = False
    else:
        if jeff.jump_count >= -10:
            neg = 1
            if jeff.jump_count < 0:
                neg = -1
            jeff.y -= (jeff.jump_count ** 2) * 0.5 * neg
            jeff.jump_count -= 1
        else:
            jeff.is_jump = False
            jeff.jump_count = 10

    redrawGameWin()
pygame.quit


"""empty message

Revision ID: df46ba972287
Revises: a5947ca03a14
Create Date: 2021-11-28 22:07:07.318093

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'df46ba972287'
down_revision = 'a5947ca03a14'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint('tb_rel_projeto_user_guid_user_key', 'tb_rel_projeto_user', type_='unique')
    op.create_unique_constraint(None, 'tb_rel_projeto_user', ['guid_user', 'id_projetos', 'id_funcao'])
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(None, 'tb_rel_projeto_user', type_='unique')
    op.create_unique_constraint('tb_rel_projeto_user_guid_user_key', 'tb_rel_projeto_user', ['guid_user'])
    # ### end Alembic commands ###


import PIL.Image
import PIL.ImageOps

class Filters():
    def __init__(self):
        self.col = 0
        self.row = 0
        self.img = None

    def setImage(self, image):
        self.img = image
        self.col, self.row = self.img.size

    def negatif(self):
        image = self.img

        if image.mode == 'RGBA':
            r,g,b,a = image.split()
            image = PIL.Image.merge('RGB', (r,g,b))
        try:
            inverted_image = PIL.ImageOps.invert(image)
        except:
            return image
        return inverted_image
        """Nope
        try:
            imgF = PIL.Image.new(self.img.mode, self.img.size)
            for i in range(self.row):
                for j in range(self.col):
                    pixel = self.img.getpixel((j,i))
                    p = (255 - pixel[0], 255 - pixel[1], 255 - pixel[2])
                    imgF.putpixel((j,i), p)
            return imgF
        except:
            return self.img
        """

    def transpose(self):
        imgF = self.img.transpose(PIL.Image.FLIP_LEFT_RIGHT)
        return imgF

    def blackAndWhite(self):
        imgF = PIL.ImageOps.grayscale(self.img)
        return imgF


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on 27/04/2019

@author: Maurizio Ferrari Dacrema
"""

import os, json, zipfile, shutil, platform

import scipy.sparse as sps
from pandas import DataFrame
import pandas as pd
import numpy as np

def json_not_serializable_handler(o):
    """
    Json cannot serialize automatically some data types, for example numpy integers (int32).
    This may be a limitation of numpy-json interfaces for Python 3.6 and may not occur in Python 3.7
    :param o:
    :return:
    """

    if isinstance(o, np.integer):
        return int(o)

    if isinstance(o, np.bool_):
        return bool(o)

    raise TypeError("json_not_serializable_handler: object '{}' is not serializable.".format(type(o)))

class DataIO(object):
    """ DataIO"""

    _DEFAULT_TEMP_FOLDER = ".temp"

    # _MAX_PATH_LENGTH_LINUX = 4096
    _MAX_PATH_LENGTH_WINDOWS = 255

    def __init__(self, folder_path):
        super(DataIO, self).__init__()

        self._is_windows = platform.system() == "Windows"

        self.folder_path = folder_path
        self._key_string_alert_done = False

        # if self._is_windows:
        #     self.folder_path = "\\\\?\\" + self.folder_path

    def _print(self, message):
        print("{}: {}".format("DataIO", message))

    def _get_temp_folder(self, file_name):
        """
        Creates a temporary folder to be used during the data saving
        :return:
        """

        # Ignore the .zip extension
        file_name = file_name[:-4]

        current_temp_folder = "{}{}_{}/".format(self.folder_path, self._DEFAULT_TEMP_FOLDER, file_name)

        if os.path.exists(current_temp_folder):
            self._print("Folder {} already exists, could be the result of a previous failed save attempt or multiple saver are active in parallel. " \
            "Folder will be removed.".format(current_temp_folder))

            shutil.rmtree(current_temp_folder, ignore_errors=True)

        os.makedirs(current_temp_folder)

        return current_temp_folder

    def _check_dict_key_type(self, dict_to_save):
        """
        Check whether the keys of the dictionary are string. If not, transforms them into strings
        :param dict_to_save:
        :return:
        """

        all_keys_are_str = all(isinstance(key, str) for key in dict_to_save.keys())

        if all_keys_are_str:
            return dict_to_save

        if not self._key_string_alert_done:
            self._print("Json dumps supports only 'str' as dictionary keys. Transforming keys to string, note that this will alter the mapper content.")
            self._key_string_alert_done = True

        dict_to_save_key_str = {str(key):val for (key,val) in dict_to_save.items()}

        assert all(dict_to_save_key_str[str(key)] == val for (key,val) in dict_to_save.items()), \
            "DataIO: Transforming dictionary keys into strings altered its content. Duplicate keys may have been produced."

        return dict_to_save_key_str

    def save_data(self, file_name, data_dict_to_save):

        # If directory does not exist, create with .temp_model_folder
        if not os.path.exists(self.folder_path):
            os.makedirs(self.folder_path)

        if file_name[-4:] != ".zip":
            file_name += ".zip"

        current_temp_folder = self._get_temp_folder(file_name)

        attribute_to_file_name = {}
        attribute_to_json_file = {}

        for attrib_name, attrib_data in data_dict_to_save.items():

            current_file_path = current_temp_folder + attrib_name

            if isinstance(attrib_data, DataFrame):
                attrib_data.to_csv(current_file_path + ".csv", index=False)
                attribute_to_file_name[attrib_name] = attrib_name + ".csv"

            elif isinstance(attrib_data, sps.spmatrix):
                sps.save_npz(current_file_path, attrib_data)
                attribute_to_file_name[attrib_name] = attrib_name + ".npz"

            elif isinstance(attrib_data, np.ndarray):
                # allow_pickle is FALSE to prevent using pickle and ensure portability
                np.save(current_file_path, attrib_data, allow_pickle=False)
                attribute_to_file_name[attrib_name] = attrib_name + ".npy"

            else:
                # Try to parse it as json, if it fails and the data is a dictionary, use another zip file
                try:
                    _ = json.dumps(attrib_data, default=json_not_serializable_handler)
                    attribute_to_json_file[attrib_name] = attrib_data
                    attribute_to_file_name[attrib_name] = attrib_name + ".json"

                except TypeError:

                    if isinstance(attrib_data, dict):
                        dataIO = DataIO(folder_path = current_temp_folder)
                        dataIO.save_data(file_name = attrib_name, data_dict_to_save=attrib_data)
                        attribute_to_file_name[attrib_name] = attrib_name + ".zip"

                    else:
                        raise TypeError("Type not recognized for attribute: {}".format(attrib_name))

        # Save list objects
        attribute_to_json_file[".DataIO_attribute_to_file_name"] = attribute_to_file_name.copy()

        for attrib_name, attrib_data in attribute_to_json_file.items():

            current_file_path = current_temp_folder + attrib_name
            attribute_to_file_name[attrib_name] = attrib_name + ".json"

            # if self._is_windows and len(current_file_path + ".json") >= self._MAX_PATH_LENGTH_WINDOWS:
            #     current_file_path = "\\\\?\\" + current_file_path

            absolute_path = current_file_path + ".json" if current_file_path.startswith(os.getcwd()) else os.getcwd() + current_file_path + ".json"

            assert not self._is_windows or (self._is_windows and len(absolute_path) <= self._MAX_PATH_LENGTH_WINDOWS), \
                "DataIO: Path of file exceeds {} characters, which is the maximum allowed under standard paths for Windows.".format(self._MAX_PATH_LENGTH_WINDOWS)

            with open(current_file_path + ".json", 'w') as outfile:

                if isinstance(attrib_data, dict):
                    attrib_data = self._check_dict_key_type(attrib_data)

                json.dump(attrib_data, outfile, default=json_not_serializable_handler)

        with zipfile.ZipFile(self.folder_path + file_name, 'w', compression=zipfile.ZIP_DEFLATED) as myzip:

            for file_name in attribute_to_file_name.values():
                myzip.write(current_temp_folder + file_name, arcname = file_name)

        shutil.rmtree(current_temp_folder, ignore_errors=True)

    def load_data(self, file_name):

        if file_name[-4:] != ".zip":
            file_name += ".zip"

        dataFile = zipfile.ZipFile(self.folder_path + file_name)

        dataFile.testzip()

        current_temp_folder = self._get_temp_folder(file_name)

        try:

            try:
                attribute_to_file_name_path = dataFile.extract(".DataIO_attribute_to_file_name.json", path = current_temp_folder)
            except KeyError:
                attribute_to_file_name_path = dataFile.extract("__DataIO_attribute_to_file_name.json", path = current_temp_folder)

            with open(attribute_to_file_name_path, "r") as json_file:
                attribute_to_file_name = json.load(json_file)

            data_dict_loaded = {}

            for attrib_name, file_name in attribute_to_file_name.items():

                attrib_file_path = dataFile.extract(file_name, path = current_temp_folder)
                attrib_data_type = file_name.split(".")[-1]

                if attrib_data_type == "csv":
                    attrib_data = pd.read_csv(attrib_file_path, index_col=False)

                elif attrib_data_type == "npz":
                    attrib_data = sps.load_npz(attrib_file_path)

                elif attrib_data_type == "npy":
                    # allow_pickle is FALSE to prevent using pickle and ensure portability
                    attrib_data = np.load(attrib_file_path, allow_pickle=False)

                elif attrib_data_type == "zip":
                    dataIO = DataIO(folder_path = current_temp_folder)
                    attrib_data = dataIO.load_data(file_name = file_name)

                elif attrib_data_type == "json":
                    with open(attrib_file_path, "r") as json_file:
                        attrib_data = json.load(json_file)

                else:
                    raise Exception("Attribute type not recognized for: '{}' of class: '{}'".format(attrib_file_path, attrib_data_type))

                data_dict_loaded[attrib_name] = attrib_data

        except Exception as exec:

            shutil.rmtree(current_temp_folder, ignore_errors=True)
            raise exec

        shutil.rmtree(current_temp_folder, ignore_errors=True)

        return data_dict_loaded


import pygame
import sound_stream
import sound_bank
import interactive
import keyboard
import sound_bank
pygame.font.init()

#Define the initial conditions and create presets
L = 0.66     #longitude
d = 0.15     #distance
h =0.1       #height
b = 0.008    #stiffnes
gamma = 1    #damping

conditions_guit1 = [L, d, h, b, gamma]
conditions_guit2 = [0.66, 0.33, 0.1, 0.02, 4]
conditions_bell = [0.66, 0.33, 0.1, 0.5, 3]
conditions_bass = [1, 0.33, 0.2, 0.005, 0.6]
conditions_violin = [0.4, 0.15, 0.1, 0, 0.2]

presets = [conditions_guit1, conditions_guit2, conditions_bell, conditions_bass, conditions_violin]
presets_names = {'Guitar 1': conditions_guit1,
				 'Guitar 2': conditions_guit2,
				 'Bell': conditions_bell,
				 'Bass':conditions_bass,
				 'Violin': conditions_violin,
				 }
instrument_index = 0

#Starting parameters
vol = 1
octav = 1
presicion = 25
time = 2

sound_synth = [conditions_guit1, vol, octav, presicion, time]  #Starting point

#Load all the images and buttons
WIDTH, HEIGHT = 350,200
WIN = pygame.display.set_mode((WIDTH,HEIGHT))
WHITE = (255,255,255)
BLACK = (0,0,0)
WHITE_KEY_WIDTH = 25
WHITE_KEY_HEIGHT = 70
BLACK_KEY_WIDTH = 18
BLACK_KEY_HEIGHT = 40
KEYBOARD_POSITION = [20,110]
NUM_KEYS = 10  #White Keys

BACKGROUND = pygame.image.load('images/background.png')
BUTTON_IMAGE_UP = pygame.image.load('images/button_up.png').convert_alpha()
BUTTON_IMAGE_DOWN = pygame.image.load('images/button_down.png').convert_alpha()
BUTTON_IMAGE_UP_PRESS = pygame.image.load('images/button_up_press.png').convert_alpha()
BUTTON_IMAGE_DOWN_PRESS = pygame.image.load('images/button_down_press.png').convert_alpha()
LOADING_IMAGE = pygame.image.load('images/loading.png').convert_alpha()
UPDATE_IMAGE = pygame.image.load('images/button_upload.png').convert_alpha()
UPDATE_IMAGE_PRESS = pygame.image.load('images/button_upload_press.png').convert_alpha()
ICON = pygame.image.load('images/icon.png')

BUTTON_UP_1 = interactive.Button(82, 55, BUTTON_IMAGE_UP, BUTTON_IMAGE_UP_PRESS, 0.08)
BUTTON_UP_2 = interactive.Button(235, 55, BUTTON_IMAGE_UP, BUTTON_IMAGE_UP_PRESS, 0.08)

BUTTON_DOWN_1 = interactive.Button(82, 70, BUTTON_IMAGE_DOWN, BUTTON_IMAGE_DOWN_PRESS, 0.08)
BUTTON_DOWN_2 = interactive.Button(235, 70, BUTTON_IMAGE_DOWN, BUTTON_IMAGE_DOWN_PRESS, 0.08)

BUTTON_UPDATE = interactive.Button(310, 150, UPDATE_IMAGE, UPDATE_IMAGE_PRESS, 0.15)

LOADING = interactive.Loading(330,180, LOADING_IMAGE,0.15, 0)

SLIDER = interactive.Slider(310,40, 30,70)

font = pygame.font.Font('images/Closeness.ttf', 16)
font2 = pygame.font.Font('images/Closeness.ttf', 12)

pygame.display.set_caption('Synth')
pygame.display.set_icon(ICON)

#Loading objects
sound_state = sound_stream.Load()  #Handle loading the sounds

sound_state.load_sound(sound_synth)  #Load the starting sound

playing = sound_stream.Play_Sounds(256, 0, 20000)  #Create object to play the sounds

def draw_window(white_keys, black_keys,new_white, new_black, instrument, octave):
	WIN.blit(BACKGROUND, (0,0))

	#Draw the white keys
	for key in white_keys:
		pygame.draw.rect(WIN, WHITE, key)

	#Draw press white keys
	if len(new_white) != 0:
		for i in range(len(new_white)):
			pygame.draw.rect(WIN, (209, 209, 209), new_white[i])

	#Draw black keys
	for i in range(len(black_keys)):
		if i not in (2,6,9,12):  #Exception starting in do
			pygame.draw.rect(WIN, BLACK, black_keys[i])

	#Draw the borders
	pygame.draw.line(WIN, BLACK,KEYBOARD_POSITION, (KEYBOARD_POSITION[0] , KEYBOARD_POSITION[1] + WHITE_KEY_HEIGHT), width=2)
	pygame.draw.line(WIN, BLACK,(KEYBOARD_POSITION[0], KEYBOARD_POSITION[1] + WHITE_KEY_HEIGHT), (KEYBOARD_POSITION[0] + (WHITE_KEY_WIDTH * NUM_KEYS) , KEYBOARD_POSITION[1] + WHITE_KEY_HEIGHT), width=2)
	pygame.draw.line(WIN, BLACK,(KEYBOARD_POSITION[0] + (WHITE_KEY_WIDTH * NUM_KEYS), KEYBOARD_POSITION[1]), (KEYBOARD_POSITION[0] + (WHITE_KEY_WIDTH * NUM_KEYS) , KEYBOARD_POSITION[1] + WHITE_KEY_HEIGHT), width=2)
	for i in range(1,NUM_KEYS):
		pygame.draw.line(WIN, BLACK,(KEYBOARD_POSITION[0] + WHITE_KEY_WIDTH * i, KEYBOARD_POSITION[1]), (KEYBOARD_POSITION[0] + WHITE_KEY_WIDTH * i, KEYBOARD_POSITION[1] + WHITE_KEY_HEIGHT), width=2)

	#Draw press black key
	if len(new_black) != 0:
		for i in range(len(new_black)):
			pygame.draw.rect(WIN, (54, 54, 54), new_black[i])

	#Instrument selection
	actual_instrument = font.render(instrument, True, (255,255,255))
	WIN.blit(actual_instrument, (10,60))

	#Octave selection
	oct_name = font.render('Octave ' + str(octave), True, (255,255,255))
	WIN.blit(oct_name, (150,60))

	#Volume
	vol = font2.render('Volume', True, (255,255,255))
	WIN.blit(vol, (300,15))

def keys_press(keys_to_check):
	key_check = keys_to_check
	press = ''
	for key in key_check:
		if keyboard.is_pressed(key):
			press += key
	return press

def main():
	global sound_synth
	global presets
	global presets_names
	global instrument_index
	global vol

	#Draw black and white keys
	white_keys = []
	white_keys_position = [KEYBOARD_POSITION[0] + i * WHITE_KEY_WIDTH for i in range(NUM_KEYS)]
	for i in range(NUM_KEYS):
		white_keys.append(pygame.Rect(white_keys_position[i], KEYBOARD_POSITION[1], WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT))

	black_keys = []
	for i in range(NUM_KEYS - 1):
		black_keys.append(pygame.Rect(white_keys_position[i] + WHITE_KEY_WIDTH//1.5, KEYBOARD_POSITION[1], BLACK_KEY_WIDTH, BLACK_KEY_HEIGHT))

	run = True
	while run:
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				run = False

		#Draw black and white keys when pressed
		keys_white = keys_press(['a', 's','d','f','g','h','j','k','l',';'])  #ENG Keyboard
		press_white_key = []

		notes_ref = {
				'a':[0,'do'],
				's':[1,'re'],
				'd':[2,'mi'],
				'f':[3,'fa'],
				'g':[4,'sol'],
				'h':[5,'la'],
				'j':[6,'si'],
				'k':[7,'doh'],
				'l':[8,'reh'],
				';':[9,'mih']}

		if len(keys_white) != 0:
			if len(keys_white) == 1:
				new_white = pygame.Rect(white_keys_position[notes_ref[keys_white][0]], KEYBOARD_POSITION[1], WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT)
				press_white_key.insert(0, new_white)

			else:
				for i in range(len(keys_white)):
					new_white = pygame.Rect(white_keys_position[notes_ref[keys_white[i]][0]], KEYBOARD_POSITION[1], WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT)
					press_white_key.insert(i, new_white)

		keys_black = keys_press(['w', 'e','t','y','u','o','p'])
		press_black_key = []

		notes_ref_b = {
					'w':[0,'do#'],
					'e':[1,'re#'],
					't':[3,'fa#'],
					'y':[4,'sol#'],
					'u':[5,'sib'],
					'o':[7,'do#h'],
					'p':[8,'re#h']}

		if len(keys_black) != 0:
			if len(keys_black) == 1:
				new_black = pygame.Rect(white_keys_position[notes_ref_b[keys_black][0]] + WHITE_KEY_WIDTH//1.5,KEYBOARD_POSITION[1], BLACK_KEY_WIDTH, BLACK_KEY_HEIGHT)
				press_black_key.insert(0, new_black)

			else:
				for i in range(len(keys_black)):
					new_black = pygame.Rect(white_keys_position[notes_ref_b[keys_black[i]][0]] + WHITE_KEY_WIDTH//1.5, KEYBOARD_POSITION[1], BLACK_KEY_WIDTH, BLACK_KEY_HEIGHT)
					press_black_key.insert(0, new_black)

		#Draw current octave and preset
		octave = sound_synth[2]

		for name_preset in presets_names:
			if sound_synth[0] == presets_names[name_preset]:
				instrument = name_preset

		draw_window(white_keys, black_keys, press_white_key, press_black_key, instrument, octave)

		#Change sound preset
		if BUTTON_UP_1.draw(WIN):
			instrument_index += 1
			if instrument_index >= len(presets):
				instrument_index = 0
			sound_synth[0] = presets[instrument_index]

		if BUTTON_DOWN_1.draw(WIN):
			instrument_index -= 1
			if instrument_index == -(len(presets)):
				instrument_index = 0
			sound_synth[0] = presets[instrument_index]

		#Change octave
		if BUTTON_UP_2.draw(WIN):
			sound_synth[2] += 1
			if sound_synth[2] > 3:
				sound_synth[2] = 3

		if BUTTON_DOWN_2.draw(WIN):
			sound_synth[2] -= 1
			if sound_synth[2] < 0:
				sound_synth[2] = 0

		#Change volume
		if SLIDER.draw(WIN):
			sound_synth[1] = 2 * vol * SLIDER.volume()

		#Add the changes made by the user
		if not sound_state.loading():
			if BUTTON_UPDATE.draw(WIN):
				sound_state.load_sound(sound_synth)
			text = font2.render('Update', True, (255,255,255))
			WIN.blit(text, (300,170))

		detect = ['a','w','s', 'e','d','f','t','g','y','h','u','j','k','o','l','p',';']
		keys = keys_press(detect)

		#Show animation while loading sounds
		if sound_state.loading():
			keys = ''  #While loading can't access the sound part of the program
			LOADING.draw(WIN,3)
			LOADING.angle += 1

		pygame.display.update()

		#Detect key press and play the sound
		letter_to_note = {
					'a' : 0,
					'w' : 1,
					's' : 2,
					'e' : 3,
					'd' : 4,
					'f' : 5,
					't' : 6,
					'g' : 7,
					'y' : 8,
					'h' : 9,
					'u' : 10,
					'j' : 11,
					'k' : 12,
					'o' : 13,
					'l' : 14,
					'p' : 15,
					';' : 16}

		notes = []

		if len(keys) == 1:
			notes.insert(0,letter_to_note[keys])
			if len(keys) == 1:
				audio_new = playing.buffer_segmentation(sound_state.pluck_sounds, notes)
				playing.play(audio_new)
				playing.step += 1
				keys = keys_press(detect)

		elif len(keys) > 1:
			for i in range(len(keys)):
				notes.insert(i, letter_to_note[keys[i]])
			if len(keys) > 1:
				audio_new = playing.buffer_segmentation(sound_state.pluck_sounds, notes)
				playing.play(audio_new)
				playing.step += 1
				keys = keys_press(detect)
		else:
			playing.step = 0

	pygame.quit()

if __name__ == '__main__':
	main()


from ...models.tree_ensemble import TreeEnsembleRegressor, TreeEnsembleClassifier
from ...models._feature_management import process_or_validate_features

from ..._deps import _HAS_SKLEARN

if _HAS_SKLEARN:
    from sklearn.tree import _tree

import numpy as _np

def _get_value(scikit_value, mode="regressor", scaling=1.0, n_classes=2, tree_index=0):
    """ Get the right value from the scikit-tree
    """
    # Regression
    if mode == "regressor":
        return scikit_value[0] * scaling

    # Binary classification
    if n_classes == 2:
        # Decision tree
        if len(scikit_value[0]) != 1:
            value = scikit_value[0][1] * scaling / scikit_value[0].sum()
        # boosted tree
        else:
            value = scikit_value[0][0] * scaling
        if value == 0.5:
            value = value - 1e-7

    # Multiclass classification
    else:
        # Decision tree
        if len(scikit_value[0]) != 1:
            value = scikit_value[0] / scikit_value[0].sum()
        # boosted tree
        else:
            value = {tree_index: scikit_value[0] * scaling}
    return value

def _recurse(
    coreml_tree,
    scikit_tree,
    tree_id,
    node_id,
    scaling=1.0,
    mode="regressor",
    n_classes=2,
    tree_index=0,
):
    """Traverse through the tree and append to the tree spec.
    """
    if not (_HAS_SKLEARN):
        raise RuntimeError(
            "scikit-learn not found. scikit-learn conversion API is disabled."
        )

    ## Recursion should not be called on the leaf node.
    if node_id == _tree.TREE_LEAF:
        raise ValueError("Invalid node_id %s" % _tree.TREE_LEAF)

    # Add a branch node to the tree
    if scikit_tree.children_left[node_id] != _tree.TREE_LEAF:
        branch_mode = "BranchOnValueLessThanEqual"
        feature_index = scikit_tree.feature[node_id]
        feature_value = scikit_tree.threshold[node_id]
        left_child_id = scikit_tree.children_left[node_id]
        right_child_id = scikit_tree.children_right[node_id]

        # Add a branch node
        coreml_tree.add_branch_node(
            tree_id,
            node_id,
            feature_index,
            feature_value,
            branch_mode,
            left_child_id,
            right_child_id,
        )

        # Now recurse
        _recurse(
            coreml_tree,
            scikit_tree,
            tree_id,
            left_child_id,
            scaling,
            mode,
            n_classes,
            tree_index,
        )
        _recurse(
            coreml_tree,
            scikit_tree,
            tree_id,
            right_child_id,
            scaling,
            mode,
            n_classes,
            tree_index,
        )

    # Add a leaf node to the tree
    else:
        # Get the scikit-learn value
        if scikit_tree.n_outputs != 1:
            raise ValueError("Expected only 1 output in the scikit-learn tree.")
        value = _get_value(
            scikit_tree.value[node_id], mode, scaling, n_classes, tree_index
        )
        coreml_tree.add_leaf_node(tree_id, node_id, value)

def get_input_dimension(model):
    if hasattr(model, "n_features_"):
        return model.n_features_

    elif hasattr(model, "n_estimators"):
        if model.n_estimators == 0:
            raise ValueError("model not trained.")

        try:
            return model.estimators_[0, 0].n_features_
        except IndexError:
            raise ValueError("Model not trained or invalid model.")
    else:
        raise ValueError("Unable to obtain input dimension from model.")

def convert_tree_ensemble(
    model,
    input_features,
    output_features=("predicted_class", float),
    mode="regressor",
    base_prediction=None,
    class_labels=None,
    post_evaluation_transform=None,
):
    """
    Convert a generic tree regressor model to the protobuf spec.

    This currently supports:
      * Decision tree regression
      * Gradient boosted tree regression
      * Random forest regression
      * Decision tree classifier.
      * Gradient boosted tree classifier.
      * Random forest classifier.

    ----------
    Parameters
    model: [DecisionTreeRegressor | GradientBoostingRegression | RandomForestRegressor]
        A scikit learn tree model.

    feature_names : list of strings, optional (default=None)
        Names of each of the features.

    target: str
        Name of the output column.

    base_prediction: double
        Base prediction value.

    mode: str in ['regressor', 'classifier']
        Mode of the tree model.

    class_labels: list[int]
        List of classes

    post_evaluation_transform: list[int]
        Post evaluation transform

    Returns
    -------
    model_spec: An object of type Model_pb.
        Protobuf representation of the model
    """

    num_dimensions = get_input_dimension(model)
    features = process_or_validate_features(input_features, num_dimensions)

    n_classes = None
    if mode == "classifier":
        n_classes = model.n_classes_
        if class_labels is None:
            class_labels = range(n_classes)
        else:
            if len(class_labels) != n_classes:
                raise ValueError(
                    "Number of classes in model (%d) does not match "
                    "length of supplied class list (%d)."
                    % (n_classes, len(class_labels))
                )

        coreml_tree = TreeEnsembleClassifier(
            input_features, class_labels, output_features
        )
        if post_evaluation_transform is not None:
            coreml_tree.set_post_evaluation_transform(post_evaluation_transform)

        # Base prediction not provided
        if base_prediction is None:
            if n_classes == 2:
                base_prediction = [0.0]
            else:
                base_prediction = [0.0 for c in range(n_classes)]
        coreml_tree.set_default_prediction_value(base_prediction)
    else:
        if base_prediction is None:
            base_prediction = 0.0
        coreml_tree = TreeEnsembleRegressor(input_features, output_features)
        coreml_tree.set_default_prediction_value(base_prediction)

    # Single tree
    if hasattr(model, "tree_"):
        _recurse(
            coreml_tree,
            model.tree_,
            tree_id=0,
            node_id=0,
            mode=mode,
            n_classes=n_classes,
        )

    # Multiple trees
    elif hasattr(model, "estimators_"):
        is_ensembling_in_separate_trees = False
        if type(model.estimators_) != list:
            is_ensembling_in_separate_trees = (
                len(model.estimators_.shape) > 0 and model.estimators_.shape[1] > 1
            )
            estimators = model.estimators_.flatten()
        else:
            estimators = model.estimators_

        scaling = (
            model.learning_rate
            if hasattr(model, "learning_rate")
            else 1.0 / len(estimators)
        )
        for tree_id, base_model in enumerate(estimators):
            if is_ensembling_in_separate_trees:
                tree_index = tree_id % n_classes
            else:
                tree_index = 0
            _recurse(
                coreml_tree,
                base_model.tree_,
                tree_id,
                node_id=0,
                scaling=scaling,
                mode=mode,
                n_classes=n_classes,
                tree_index=tree_index,
            )
    else:
        raise TypeError("Unknown scikit-learn tree model type.")

    return coreml_tree.spec


from os.path import join
import os
#import keras.backend as K
#K.set_image_dim_ordering('')
from keras.callbacks import ModelCheckpoint #new
from keras.layers import Conv2D, Dense, Activation, MaxPooling2D, Lambda #lambda is new
from keras.layers.merge import concatenate #new
from keras.models import Model #new
from keras.layers import Flatten, BatchNormalization, Dropout
from keras.layers.advanced_activations import PReLU
from keras.models import Sequential, load_model
from keras.optimizers import Adam
from keras.regularizers import l2
from keras.utils import multi_gpu_model
from keras.callbacks import Callback
from data_provider import MODELS_DIR
from data_provider import load_organized_data_info
from data_provider import train_val_dirs_generators
import tensorflow as tf
#convert to channels last
#IMGS_DIM_3D = (3, 256, 256)
IMGS_DIM_3D = (256,256,3)
#Using this for testing multi-gpu models
CNN_MODEL_FILE = join(MODELS_DIR,'cnn.h5')
#CNN_MODEL_FILE = join(MODELS_DIR, 'cnn.h5')
MAX_EPOCHS = 500
BATCH_SIZE = 96
#BATCH_SIZE = 5 used for testing
L2_REG = 0.003
W_INIT = 'he_normal'
LAST_FEATURE_MAPS_LAYER = 46
LAST_FEATURE_MAPS_SIZE = (128, 8, 8)
PENULTIMATE_LAYER = 51
PENULTIMATE_SIZE = 2048
SOFTMAX_LAYER = 55
SOFTMAX_SIZE = 1584
KERNAL_SIZE = 3

def _train_model():
    # Change IMGS_DIM_3D to 0 instead of index 2 because we reversed order for the tensorflow switch
    data_info = load_organized_data_info(IMGS_DIM_3D[0])
    dir_tr = data_info['dir_tr']
    dir_val = data_info['dir_val']

    #just threw this in here from  https://github.com/keras-team/keras/issues/8649 flagged in git issue 6.
    # currently it just saves every epoch. can probably modify to only save if highest accuracy.
    class MyCbk(Callback):

        def __init__(self, model):
             self.model_to_save = model

        def on_epoch_end(self, epoch, logs=None):
            self.model_to_save.save('model_at_epoch_%d.h5' % epoch)

    gen_tr, gen_val = train_val_dirs_generators(BATCH_SIZE, dir_tr, dir_val)

    # with tf.device('/cpu:0'): # important!!!
    #     model = _cnn(IMGS_DIM_3D)
    #     print('Single tower model:')
    #     model.summary()
    #
    #     if gpu_count > 1:
    #         model = make_parallel(model, gpu_count)
    #
    #         print('Multi-GPU model:')
    #         model.summary()
    #
    #     model = compile_model(model)
    #     model.fit_generator(
    #         generator=gen_tr,
    #         epochs=MAX_EPOCHS,
    #         steps_per_epoch=data_info['num_tr'],
    #         validation_data=gen_val,
    #         validation_steps=data_info['num_val'],
    #         callbacks=[ModelCheckpoint(CNN_MODEL_FILE, save_best_only=True)],
    #         verbose=1)

    model = _cnn(IMGS_DIM_3D) #new

    #model.load_weights("/home/nkim/art_cnn/models/cnn.h5")
    #print("Model weights have been updated!")

    parallel_model = multi_gpu_model(model, gpus=2)
    parallel_model = compile_model(parallel_model)
    cbk = MyCbk(model) #new

    parallel_model.fit_generator(
        generator=gen_tr,
        epochs=MAX_EPOCHS,
        steps_per_epoch=data_info['num_tr'],
        validation_data=gen_val,
        validation_steps=data_info['num_val'],
        callbacks=[cbk], #new
        verbose=1)

    # model.fit_generator(
    #     generator=gen_tr,
    #     epochs=MAX_EPOCHS,
    #     steps_per_epoch=data_info['num_tr'],
    #     validation_data=gen_val,
    #     validation_steps=data_info['num_val'],
    #     callbacks=[ModelCheckpoint(CNN_MODEL_FILE, save_best_only=True)],
    #     verbose=1)

# def make_parallel(model, gpu_count):
#     def get_slice(data, idx, parts):
#         shape = tf.shape(data)
#         size = tf.concat([shape[:1] // parts, shape[1:]], axis=0)
#         stride = tf.concat([shape[:1] // parts, shape[1:] * 0], axis=0)
#         start = stride * idx
#         return tf.slice(data, start, size)
#
#     outputs_all = []
#     for i in range(len(model.outputs)):
#         outputs_all.append([])
#
#     #Place a copy of the model on each GPU, each getting a slice of the batch
#     for i in range(gpu_count):
#         with tf.device('/gpu:%d' % i):
#             with tf.name_scope('tower_%d' % i) as scope:
#
#                 inputs = []
#                 #Slice each input into a piece for processing on this GPU
#                 for x in model.inputs:
#                     input_shape = tuple(x.get_shape().as_list())[1:]
#                     slice_n = Lambda(get_slice, output_shape=input_shape, arguments={'idx':i,'parts':gpu_count})(x)
#                     inputs.append(slice_n)
#
#                 outputs = model(inputs)
#
#                 if not isinstance(outputs, list):
#                     outputs = [outputs]
#
#                 #Save all the outputs for merging back together later
#                 for l in range(len(outputs)):
#                     outputs_all[l].append(outputs[l])
#
#     # merge outputs on CPU
#     with tf.device('/cpu:0'):
#         merged = []
#         for outputs in outputs_all:
#             merged.append(concatenate(outputs, axis=0))
#
#         return Model(inputs=model.inputs, outputs=merged)

def _cnn(imgs_dim, compile_=True):
    model = Sequential()

    model.add(_convolutional_layer(nb_filter=16, input_shape=imgs_dim))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=16))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(_convolutional_layer(nb_filter=32))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=32))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=32))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(_convolutional_layer(nb_filter=64))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=64))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=64))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(_convolutional_layer(nb_filter=128))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=128))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=128))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(_convolutional_layer(nb_filter=256))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=256))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(_convolutional_layer(nb_filter=256))
    model.add(BatchNormalization(axis=-1))
    model.add(PReLU(alpha_initializer=W_INIT))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(rate=0.5))

    model.add(Flatten())
    model.add(_dense_layer(output_dim=PENULTIMATE_SIZE))
    model.add(BatchNormalization())
    model.add(PReLU(alpha_initializer=W_INIT))

    if compile_:
        model.add(Dropout(rate=0.5))
        model.add(_dense_layer(output_dim=SOFTMAX_SIZE))
        model.add(BatchNormalization())
        model.add(Activation(activation='softmax'))
        return compile_model(model)

    return model

def _convolutional_layer(nb_filter, input_shape=None):
    if input_shape:
        return _first_convolutional_layer(nb_filter, input_shape)
    else:
        return _intermediate_convolutional_layer(nb_filter)

def _first_convolutional_layer(nb_filter, input_shape):
    return Conv2D(nb_filter, (KERNAL_SIZE ,KERNAL_SIZE), input_shape=input_shape,
        padding='same', kernel_initializer=W_INIT, kernel_regularizer=l2(l=L2_REG))

def _intermediate_convolutional_layer(nb_filter):
    return Conv2D(nb_filter,(KERNAL_SIZE, KERNAL_SIZE), padding='same',
        kernel_initializer=W_INIT, kernel_regularizer=l2(l=L2_REG))

def _dense_layer(output_dim):
    return Dense(units=output_dim, kernel_regularizer=l2(l=L2_REG), kernel_initializer=W_INIT)

def compile_model(model):
    adam = Adam(lr=0.000074)
    #adam = Adam(lr=0.0007)
    model.compile(
        loss='categorical_crossentropy',
        optimizer=adam,
        metrics=['accuracy'])
    return model

def load_trained_cnn_feature_maps_layer(model_path):
    return _load_trained_cnn_layer(model_path, LAST_FEATURE_MAPS_LAYER)

def load_trained_cnn_penultimate_layer(model_path):
    return _load_trained_cnn_layer(model_path, PENULTIMATE_LAYER)

def load_trained_cnn_softmax_layer(model_path):
    return _load_trained_cnn_layer(model_path, SOFTMAX_LAYER)

def _load_trained_cnn_layer(model_path, layer_index):
    model = load_model(model_path)
    dense_output = K.function(
        [model.layers[0].input, K.learning_phase()],
        [model.layers[layer_index].output])
    # output in test mode = 0
    return lambda X: dense_output([X, 0])[0]

if __name__ == '__main__':
    #gpu_count = len([dev for dev in os.environ.get('CUDA_VISIBLE_DEVICES', '').split(',') if len(dev.strip()) > 0])
    #config = tf.ConfigProto()
    #config.gpu_options.allow_growth = True
    #sess = tf.Session(config = config)
    _train_model()


import numpy as np
import sys
import time
import itertools

def solve(M):
    n, _ = M.shape
    nodes = list(range(n))
    X = ring(nodes)
    best_matrix = X
    best_score = score(M, X)
    finish = False
    steps = 1
    while not finish:
        finish = True
        for pair in itertools.combinations(nodes, 2):
            copy = nodes.copy()
            i = pair[0]
            j = pair[1]
            copy[i], copy[j] = copy[j], copy[i]
            X = ring(copy)
            f = score(M, X)
            steps += 1
            if f < best_score:
                best_score = f
                best_matrix = X
                nodes = copy
                finish = False
                break
    return best_matrix, best_score, steps

in_file = sys.argv[1]
out_file = sys.argv[2]

M = np.loadtxt(in_file)
tic = time.perf_counter()
X, best_score, steps = solve(M)
toc = time.perf_counter()

def ring(nodes):
    n = len(nodes)
    A = np.zeros((n,n))
    for i in range(n):
        j = (i + 1) % n
        u = nodes[i]
        v = nodes[j]
        A[u,v] = A[v,u] = 1
    return A

def score(M, X):
    return np.sum(np.multiply(M, X))

with open(out_file, 'w') as f:
    f.write("Score: {0}\n".format(best_score))
    f.write("Solution:\n {0}\n".format(X))
    f.write("Steps: {0}\n".format(steps))
    f.write("Time: {0:0.4f} s\n".format(toc - tic))


import time
import cv2
from cvzone.HandTrackingModule import HandDetector
import numpy as np
import math

cap =cv2.VideoCapture(0)
dector = HandDetector(maxHands=1)

offset =20
imgSize = 300
counter = 0

folder = "Data/B"

while True :
    succuss,img =cap.read()
    hands,img = dector.findHands(img)
    if hands:
        hands = hands[0]
        x, y, w,h =hands['bbox']

        imgWhite = np.ones((imgSize,imgSize,3),np.uint8)*225
        imgCrop = img[y-offset:y+h+offset, x-offset:x+w+offset]
        imgCropShape = imgCrop.shape
        # imgWhite[0:imgCropShape[0],0:imgCropShape[1]] = imgCrop
        aspectRatio = h/w

        if aspectRatio >1:
            k = imgSize/h
            wCal = math.ceil(k*w)
            ImgResize = cv2.resize(imgCrop,(wCal,imgSize))
            ImgResizeShape = ImgResize.shape
            wGap = math.ceil((imgSize- wCal)/2)
            imgWhite[:, wGap:wCal +wGap] = ImgResize
        else:
            k = imgSize / w
            hCal = math.ceil(k * h)
            ImgResize = cv2.resize(imgCrop, (imgSize, hCal))
            ImgResizeShape = ImgResize.shape
            hGap = math.ceil((imgSize - hCal) / 2)
            imgWhite[hGap:hCal + hGap,:] = ImgResize

        cv2.imshow('imageCrop', imgCrop)
        cv2.imshow('imageWhite', imgWhite)

    cv2.imshow('image', img)
    key = cv2.waitKey(4)
    if key == ord("s"):
        counter +=1
        cv2.imwrite(f'{folder}/imge_{time.time()} .jpg', imgWhite)
        print(counter)