import os
import time
from math import inf
from threading import Thread, Lock
from pathlib import Path

from codes import config, utility, congestion_manager

class AckChecker:
    def __init__(self, ack_pipe, parent):
        self.there_is_ack = False
        self.ack_data = None
        self.parent = parent
        self.ack_pipe = ack_pipe
        self.task_done = False
        self.thread = Thread(target=self.watch_acks, args=())
        self.thread.start()

    def watch_acks(self):
        while True:
            if self.there_is_ack:
                continue
            if self.task_done:
                break

            _, self.ack_data = utility.read_pipe_blocking(self.ack_pipe)

            self.parent.ack_mutex.acquire()
            self.there_is_ack = True
            self.parent.ack_mutex.release()

    def read_data(self):
        return self.ack_data, self.there_is_ack

    def go_on(self):
        self.ack_data = None
        self.there_is_ack = False

    def shutdown(self):
        self.task_done = True

class TimeWatcher:
    def __init__(self, time_pipe, parent):
        self.current_time = 0
        self.parent = parent
        self.time_pipe = time_pipe
        self.task_done = False
        self.thread = Thread(target=self.watch_time, args=())
        self.thread.start()

    def watch_time(self):
        while True:
            if self.task_done:
                break
            data, has_data = utility.read_pipe_5(self.time_pipe)
            if not has_data:
                break

            self.parent.mutex.acquire()
            self.current_time += 1
            self.parent.mutex.release()

    def shutdown(self):
        self.task_done = True

class Sender:
    def __init__(self, sender_port, receiver_port, initial_rtt, max_window, file_path):

        self.sender_port = sender_port
        self.receiver_port = receiver_port

        self.check_receiver_existence()

        self.next_seq_number = 0
        self.window_size = 1
        self.window_base = 0
        self.window_iter = 0
        self.next_base = 0
        self.next_size = 0
        self.last_window_end_seq_num = 0  # TODO FIRST ASSIGNMENT; slow start to normal mode transition
        self.file = None
        self.congestion_window = []
        self.congestion_window_prev = []
        self.backlog_window = []

        self.slow_start_phase = True
        self.file_finished = False
        self.mutex = Lock()
        self.ack_mutex = Lock()

        self.dup_ack_scenario = False
        self.dup_ack_count = 0
        self.dup_ack_candidate = None
        self.exec_timeout = False

        self.timeout_scenario = False
        self.timeout_rem_time = 0
        self.timeout_candidate = None
        self.exec_dup_ack = False

        self.timeout_time = 0

        self.cng_manager = congestion_manager.CongestionManager(initial_rtt, max_window)

        self.ss_threshold = inf
        self.max_window = max_window

        try:
            os.mkfifo(utility.get_pipes_folder_path() + 'sender_' + sender_port + '_data.pipe')
            os.mkfifo(utility.get_pipes_folder_path() + 'sender_' + sender_port + '_time.pipe')
        except OSError as error:
            print('failed to create sender fifo files!')

        # self.ack_pipe = open(utility.get_pipes_folder_path() + 'receiver_' + receiver_port + '_data.pipe', 'rb')
        # self.ack_pipe = open(utility.get_pipes_folder_path() + 'backwardnet_data.pipe', 'rb')
        self.ack_pipe = open(utility.get_pipes_folder_path() + 'sender_' + sender_port + '_data.pipe', 'rb')
        self.data_pipe = open(utility.get_pipes_folder_path() + 'forwardnet_data.pipe', 'wb')
        self.time_pipe = open(utility.get_pipes_folder_path() + 'sender_' + sender_port + '_time.pipe', 'rb')

        self.time_watcher = TimeWatcher(self.time_pipe, self)
        self.ack_watcher = AckChecker(self.ack_pipe, self)

        # try:
        #     posix.open(utility.get_pipes_folder_path() + 'forwardnet_data.pipe', posix.O_WRONLY | posix.O_NONBLOCK)
        # except OSError as error:
        #     if error.errno == errno.ENXIO:
        #         self.print_log_message('no receiving host ' + self.receiver_port + ' is available')
        #     self.terminate()

        # TODO_DONE GUARD TO CHECK THERE IS A RECEIVER ON THE OTHER SIDE

        self.start(file_path)

    def send_file(self):
        # INITIAL VALUES
        self.window_base = self.next_seq_number
        self.next_base = self.next_seq_number
        self.window_size = 1
        self.slow_start_phase = True
        self.next_size = 1
        self.window_iter = 0

        while not (self.file_finished and self.window_iter == self.window_size and len(self.backlog_window) == 0):
            self.send_window()

    def send_window(self):
        executed_dup_ack = False
        if self.window_iter >= self.window_size or len(self.congestion_window) == 0:
            self.refill_window()
        while self.window_iter < self.window_size:

            self.mutex.acquire()
            send_time = self.time_watcher.current_time
            send_seq_num = self.send_segment(self.window_iter)
            self.print_verbose('sent data segment #' + str(send_seq_num))
            self.mutex.release()

            time.sleep(0.05)

            self.mutex.acquire()
            sample_rtt = self.time_watcher.current_time - send_time
            self.print_verbose('sample rtt time is :' + str(sample_rtt) + ' s')
            self.check_ack(sample_rtt, send_seq_num)
            self.mutex.release()

            if self.exec_dup_ack:
                executed_dup_ack = True
                self.scenario_call(is_timeout=False)
                break

        if not executed_dup_ack:
            if self.timeout_scenario:
                self.scenario_call(is_timeout=True)

    def scenario_call(self, is_timeout):
        self.timeout_scenario = False
        self.exec_timeout = False
        self.dup_ack_scenario = False
        self.exec_dup_ack = False

        candidate_seq_num = self.timeout_candidate if is_timeout else self.dup_ack_candidate

        self.print_verbose('non-normal scenario on segment #' + str(candidate_seq_num))

        # candidate = self.grep_segment_candidate(candidate_seq_num)

        to_backlog = [seg for seg in self.congestion_window if seg.seq_number >= candidate_seq_num]
        to_backlog.extend(self.backlog_window)
        self.backlog_window = to_backlog
        self.print_verbose('backlog updated')

        not_to_backlog = [seg for seg in self.congestion_window if seg.seq_number < candidate_seq_num]
        self.congestion_window_prev = not_to_backlog
        self.print_verbose('window history updated')

        self.congestion_window = []
        self.print_verbose('current window cleared')

        if is_timeout:
            self.slow_start_phase = True
            self.print_verbose('timeout scenario on segment #' + str(candidate_seq_num))
            self.ss_threshold = max(config.MIN_WIN_SIZE, self.next_size // 2)
            self.window_size = 1
            self.next_size = 1
            self.window_base = candidate_seq_num
            self.next_base = candidate_seq_num
            self.window_iter = 0
        else:
            self.slow_start_phase = False
            self.print_verbose('dup_ack scenario on segment #' + str(candidate_seq_num))
            self.next_size = max(config.MIN_WIN_SIZE, self.next_size // 2)
            self.window_base = candidate_seq_num
            self.next_base = candidate_seq_num
            self.window_iter = 0

    def grep_segment_candidate(self, seq_num):
        try:
            segment = next(seg for seg in self.congestion_window if seg.seq_number == seq_num)
        except StopIteration:
            segment = next(seg for seg in self.congestion_window_prev if seg.seq_number == seq_num)

        return segment

    def refill_window(self):
        self.print_verbose('refilling empty window')
        # make sure not to exceed maximum window size

        if self.next_size > self.max_window:
            self.next_size = self.max_window
            self.print_verbose('shrunk window size to max size of ' + str(self.next_size) + ' upon allocating')
        else:
            self.print_verbose('window size is ' + str(self.next_size) + ' upon allocating')

        if len(self.congestion_window) != 0:
            self.print_verbose('window is normally passed without timeout or dup_ack')
            self.congestion_window_prev = self.congestion_window
            self.congestion_window = []
            self.print_verbose('current window cleared and window history updated')

        for _ in range(self.next_size):
            if len(self.backlog_window) != 0:
                segment = self.backlog_window[0]
                self.backlog_window.pop(0)
            else:
                segment = self.new_segment()
            self.congestion_window.append(segment)

        self.window_size = self.next_size
        self.window_base = self.next_base
        self.next_base = self.window_base + self.next_size
        self.window_iter = 0

    def send_segment(self, offset):
        seq_num = self.window_base + offset % utility.TWO_POW_32
        try:
            segment = next(seg for seg in self.congestion_window if seg.seq_number == seq_num)
            utility.write_pipe(self.data_pipe, segment.raw_data)
        except StopIteration:
            self.print_verbose('couldn\'t find segment #' + str(seq_num) + ' in window! :\\')

        return seq_num

    def check_ack(self, sample_rtt, send_seq_num):
        self.print_verbose('checking for ack #' + str(send_seq_num + 1))

        ack_data, there_is_ack = self.check_single_ack()

        if not there_is_ack:
            if not self.timeout_scenario:
                self.timeout_scenario = True
                self.timeout_time = sample_rtt
                self.timeout_candidate = send_seq_num
            elif self.timeout_time > sample_rtt:
                self.timeout_time = sample_rtt
                self.timeout_candidate = send_seq_num
            self.print_verbose('there is no ack. reserved for timeout with value ' + str(self.timeout_time) + ' s')
            self.window_iter += 1
        else:  # THERE IS ACK
            if sample_rtt >= self.cng_manager.timeout_interval():  # ACK RETRIEVED LATE:
                if not self.timeout_scenario:
                    self.timeout_scenario = True
                    self.timeout_time = sample_rtt
                    self.timeout_candidate = send_seq_num
                elif self.timeout_time > sample_rtt:
                    self.timeout_time = sample_rtt
                    self.timeout_candidate = send_seq_num
                self.print_verbose(
                    'ack is not in time! reserved for timeout with value ' + str(self.timeout_time) + ' s')
                self.window_iter += 1
            else:  # ACK RETRIEVED IN TIME
                ack_segment = utility.SegmentParser(ack_data)
                self.print_verbose('found ack #' + str(ack_segment.ack_number))
                if self.congestion_window[self.window_iter].seq_number < ack_segment.ack_number:

                    ack_count = ack_segment.ack_number - self.congestion_window[self.window_iter].seq_number
                    self.print_verbose('this ack, acked ' + str(ack_count) + ' segment(s)')
                    self.window_iter += ack_count

                    if self.slow_start_phase:
                        self.next_size += ack_count
                        self.print_verbose('window size increased by ' + str(ack_count) + ' segment(s) and now is '
                                           + str(self.next_size))
                    elif self.window_iter == self.window_size + self.window_base:
                        self.next_size += 1
                        self.print_verbose('window size increased by 1 segment and now is '
                                           + str(self.next_size))

                    self.dup_ack_scenario = False
                    self.timeout_scenario = False
                    self.cng_manager.update_rtt(sample_rtt)
                else:
                    self.print_verbose('this ack is duplicate')
                    if not self.timeout_scenario:
                        self.timeout_scenario = True
                        self.timeout_time = sample_rtt
                        self.timeout_candidate = send_seq_num
                    elif self.timeout_time > sample_rtt:
                        self.timeout_time = sample_rtt
                        self.timeout_candidate = send_seq_num
                    self.print_verbose('reserved for timeout with value ' + str(self.timeout_time) + ' s')

                    if not self.dup_ack_scenario:
                        self.dup_ack_scenario = True
                        self.dup_ack_candidate = ack_segment.ack_number
                        self.dup_ack_count = 1
                        self.print_verbose('reserved for dup_ack in 2 dups')

                    elif self.dup_ack_candidate == ack_segment.ack_number:
                        if self.dup_ack_count == config.DUP_ACK_CNT - 1:
                            self.exec_dup_ack = True
                            self.print_verbose(str(config.DUP_ACK_CNT) + '_dups reached!')
                        else:
                            self.dup_ack_count += 1
                            self.print_verbose('reserved for dup_ack in ' +
                                               str(config.DUP_ACK_CNT - self.dup_ack_count) + ' dups')
                    self.window_iter += 1

    def new_segment(self):
        data_size, data = self.read_a_mss()
        self.file_finished = data_size != config.MSS

        packet = utility.create_payload_packet(self.sender_port, self.receiver_port, 0,
                                               self.window_size, self.next_seq_number, data)
        segment = utility.SegmentParser(packet)
        self.next_seq_number += 1

        self.print_verbose('created data segment #' + str(segment.seq_number))

        return segment

    def establish_connection(self):
        self.send_syn()
        self.wait_for_syn_ack()
        self.send_ack()

    def print_log_message(self, message):
        print('sending host ' + self.sender_port + ': ' + message)

    def print_verbose(self, message):
        self.print_log_message('---> ' + message)

    def read_a_mss(self):
        read_data = self.file.read(config.MSS).decode('Latin-1')
        data_length = len(read_data)

        return data_length, read_data

    def terminate(self):
        self.print_log_message('is terminated.')
        exit(0)

    def send_fin(self):
        self.print_verbose('sending FIN')
        packet = utility.create_fin_packet(self.sender_port, self.receiver_port, 0,
                                           self.window_size, self.next_seq_number)
        self.next_seq_number += 1
        utility.write_pipe(self.data_pipe, packet)
        self.print_verbose('sent FIN')

    def start(self, file_path):
        self.establish_connection()
        self.file = open(file_path, 'rb')
        self.send_file()
        self.file.close()
        self.close_connection()

    def check_single_ack(self):

        time.sleep(0.05)

        self.ack_mutex.acquire()

        ack_data, there_is_ack = self.ack_watcher.read_data()
        self.ack_watcher.go_on()

        self.ack_mutex.release()

        return ack_data, there_is_ack

    def close_connection(self):
        self.send_fin()
        self.wait_for_fin_ack()
        self.wait_for_fin()
        self.send_fin_ack()

        self.time_watcher.shutdown()
        self.ack_watcher.shutdown()

        self.terminate()

    def send_syn(self):
        self.print_verbose('sending SYN')
        packet, self.next_seq_number = utility.create_syn_packet(self.sender_port, self.receiver_port, 0,
                                                                 self.window_size)

        utility.write_pipe(self.data_pipe, packet)
        self.print_verbose('sent SYN')
        self.print_log_message('is running...')

    def wait_for_syn_ack(self):
        self.print_verbose('waiting for SYN/ACK')

        segment_data, _ = self.check_single_ack()

        segment = utility.SegmentParser(segment_data)

        if segment.checksum_valid() and segment.syn_flag is True and segment.ack_flag is True:
            self.print_verbose('received SYN/ACK')
        else:
            self.print_verbose('invalid SYN/ACK. resending SYN')
            self.send_syn()
            self.wait_for_syn_ack()

    def send_ack(self):
        self.print_verbose('sending ACK')
        packet = utility.create_ack_packet(self.sender_port, self.receiver_port, 0, self.window_size,
                                           self.next_seq_number)
        self.next_seq_number += 1
        utility.write_pipe(self.data_pipe, packet)
        self.print_verbose('sent ACK')

    def wait_for_fin_ack(self):
        self.print_verbose('waiting for FIN/ACK')

        segment_data, _ = self.check_single_ack()

        segment = utility.SegmentParser(segment_data)

        if segment.checksum_valid() and segment.ack_flag is True:
            self.print_verbose('received FIN/ACK')
        else:
            self.print_verbose('invalid FIN/ACK. resending FIN')
            self.send_fin()
            self.wait_for_fin_ack()

    def wait_for_fin(self):
        self.print_verbose('waiting for FIN')

        segment_data, _ = self.check_single_ack()

        segment = utility.SegmentParser(segment_data)

        if segment.checksum_valid() and segment.fin_flag is True:
            self.print_verbose('received FIN')
        else:
            self.print_verbose('invalid FIN. resending FIN')
            self.send_fin()
            self.wait_for_fin_ack()
            self.wait_for_fin()

    def send_fin_ack(self):
        self.print_verbose('sending FIN/ACK')
        packet = utility.create_fin_ack_packet(self.sender_port, self.receiver_port, 0, self.window_size,
                                               self.next_seq_number)
        self.next_seq_number += 1
        utility.write_pipe(self.data_pipe, packet)
        self.print_verbose('sent FIN/ACK')

    def check_receiver_existence(self):
        receiver_pipe = Path(utility.get_pipes_folder_path() + 'receiver_' + str(self.receiver_port) + '_data.pipe')

        if not receiver_pipe.exists():
            self.print_log_message('no receiving host ' + str(self.receiver_port) + ' is available.')
            exit(0)
        else:
            pass


from tempfile import TemporaryDirectory

import numpy as np
import pytest

from karabo_bridge.server import ServerInThread, SimServerInThread

@pytest.fixture(params=['1.0', '2.2'])
def protocol_version(request):
    yield request.param

@pytest.fixture(scope='function')
def sim_server(protocol_version):
    with TemporaryDirectory() as td:
        endpoint = "ipc://{}/server".format(td)
        with SimServerInThread(endpoint, detector='AGIPDModule', raw=True,
                               protocol_version=protocol_version) as s:
            yield s

@pytest.fixture(scope='function')
def sim_push_server(protocol_version):
    with TemporaryDirectory() as td:
        endpoint = "ipc://{}/push".format(td)
        with SimServerInThread(endpoint, sock='PUSH', detector='AGIPDModule',
                               raw=True, protocol_version=protocol_version) as s:
            yield s

@pytest.fixture(scope='function')
def server(protocol_version):
    with TemporaryDirectory() as td:
        endpoint = "ipc://{}/server".format(td)
        with ServerInThread(endpoint, protocol_version=protocol_version) as s:
            yield s

@pytest.fixture(scope='session')
def data():
    yield {
        'source1': {
            'parameter.1.value': 123,
            'parameter.2.value': 1.23,
            'list.of.int': [1, 2, 3],
            'string.param': 'True',
            'boolean': False,
            'metadata': {'timestamp.tid': 9876543210, 'timestamp': 12345678},
        },
        'XMPL/DET/MOD0': {
            'image.data': np.random.randint(255, size=(2, 3, 4), dtype=np.uint8),
            'something.else': ['a', 'bc', 'd'],
        },
    }

@pytest.fixture(scope='session')
def metadata():
    yield {
        'source1': {
            'source': 'source1',
            'timestamp': 1585926035.7098422,
            'timestamp.sec': '1585926035',
            'timestamp.frac': '709842200000000000',
            'timestamp.tid': 1000000
        },
        'XMPL/DET/MOD0': {
            'source': 'XMPL/DET/MOD0',
            'timestamp': 1585926036.9098422,
            'timestamp.sec': '1585926036',
            'timestamp.frac': '909842200000000000',
            'timestamp.tid': 1000010
        }
    }


from PIL import Image , ImageDraw
from tqdm import tqdm
import numpy as np
import glob
import sys
import cv2
import psutil
from keras.models import model_from_json

print("Getting model")
json_file = open('model\car_detection.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
model = model_from_json(loaded_model_json)
# load weights into new model
model.load_weights("model\model.h5")
print("Loaded model from disk")

responce = int(input("Single Image: 1\nList of Images: 2\nVideo: 3\nResponce: "))
print()
input_dim = 228

if responce == 1: #Single image

    path = r"testing\test.jpg"

    image = Image.open(path)
    image = image.resize((input_dim, input_dim))
    image = np.asarray(image) / 255.0

    image = np.expand_dims(image,axis=0)

    boxes = model.predict(image)
    image = image.squeeze(axis=0)
    #Grab the box locations and size them up to be the correct size.
    b = boxes[ 0 , 0 : 4 ] * input_dim
    img = image * input_dim
    #Rebuild the image.
    source_img = Image.fromarray( img.astype( np.uint8 ) , 'RGB' )
    #Draw the bounding box
    draw = ImageDraw.Draw( source_img )
    draw.rectangle( b , outline="blue" )
    #Save the image and add 1 to the image file name.
    print("Saving image")
    source_img.save( 'result\test_image_result.png' , 'png' )
    print("Image saved")

elif responce == 2:
    images = []
    boxes = []

    image_paths = glob.glob(r"testing\*.jpg")

    loader = tqdm(total=len(image_paths), position=0, leave=True, desc="Loading images...")
    for path in image_paths:
        image = Image.open(path)
        image = image.resize((input_dim, input_dim))
        image = np.asarray(image) / 255.0
        images.append(image)
        loader.update(1)

    loader.close()

    #Progress bar for predicting images and saving them to system
    images = np.asarray(images)

    loop = tqdm(total = len(image_paths), position=0, leave=True, desc="Predicting test images...")

    boxes = model.predict(images)

    #For each bounding box
    for i in range(len(image_paths)):
        #Grab the box locations and size them up to be the correct size.
        b = boxes[ i  , : ] * input_dim
        img = images[i] * input_dim

        #Rebuild the image.
        source_img = Image.fromarray( img.astype( np.uint8 ) , 'RGB' )
        #Draw the bounding box
        draw = ImageDraw.Draw( source_img )
        draw.rectangle( b , outline="blue" )
        #Save the image and add 1 to the image file name.
        source_img.save( 'result\image_{}.png'.format( i + 1 ) , 'png' )
        loop.update(1)

    loop.close()
    print("\nFinished")
    print("Complete")

elif responce == 3:
    #Create containers
    boxes = []
    images = []

    #Get video for capture
    cap = cv2.VideoCapture(r'testing\test.mp4')

    #If the video file cannot find data
    if cap.isOpened() == False:
        print("no video")

    #While there is data.
    while cap.isOpened():

        #Check if this frame exists and grab the frame regardless
        end, frame = cap.read()
        #If this is the end of the video exit the loop.
        if end == False:
            break

        #If memory usage goes above 95 stop the program to avoid a system crash.
        if psutil.virtual_memory()[2] > 95:
            print("Virtual memory at dangerously high levels. Closing application")
            sys.exit()

        #Perform preprocessing on the frame
        frame = cv2.resize(frame, (input_dim, input_dim))
        frame = np.asarray(frame) / 255
        #Store it in an array.
        images.append(frame)
    #Releases resources used by capture to reduce strain on memory.
    cap.release()

    #Make images into a numpy array
    images = np.asarray(images)

    #Initialise progress bar
    loop = tqdm(total = len(images), position=0, leave=True, desc="Predicting video...")

    #Predict the images
    boxes = model.predict(images)

    #Create video codec for MP4 videos that works on windows 10.
    fourcc = cv2.VideoWriter_fourcc(*'H264')
    #Create the video maker.
    out = cv2.VideoWriter('result\result.mp4', fourcc, 30, (input_dim, input_dim), 1)
    #For each bounding box
    for i in range(len(images)):
        #Grab the box locations and size them up to be the correct size.
        b = boxes[ i  , : ] * input_dim
        img = images[i] * input_dim

        #Rebuild the image.
        source_img = Image.fromarray( img.astype( np.uint8 ) , 'RGB' )
        #Draw the bounding box
        draw = ImageDraw.Draw( source_img )
        draw.rectangle( b , outline="blue" )
        img = np.asarray(source_img)
        #add the frame to the video.
        out.write(img)
        loop.update(1)
    out.release()
    loop.close()

else:
    sys.exit()


import json
import os

import pytest
from click.testing import CliRunner

import brain.parsers
import brain.parsers.mq_agent.rabbitmq_agent
from brain.parsers import run_parser, invoke_parser
from brain.parsers.__main__ import cli
from brain.parsers.framework import Context
from brain.parsers.mq_agent import load_mq_agent
from brain.utils.consts import *
from .data_generators import gen_snapshot_for_parsers, PARSERS
from .utils import protobuf2dict

@pytest.fixture
def random_snapshot(tmp_path):
    snapshot = gen_snapshot_for_parsers(tmp_path, should_gen_user=True)
    data = snapshot.SerializeToString()
    file_path = str(tmp_path / 'snapshot.raw')
    with open(file_path, 'wb') as file:
        file.write(data)
    return snapshot, data, file_path

def verify_result_header(result, snapshot):
    assert result['uuid'] == str(snapshot.uuid)
    assert result['datetime'] == str(snapshot.datetime)
    assert result['user'] == protobuf2dict(snapshot.user)

def verify_pose(result, snapshot):
    assert result == protobuf2dict(snapshot.pose)

def verify_color_image(result, snapshot):
    assert result['width'] == snapshot.color_image.width
    assert result['height'] == snapshot.color_image.height
    assert os.path.isfile(result['path'])

def verify_depth_image(result, snapshot):
    assert os.path.isfile(result['path'])

def verify_feelings(result, snapshot):
    assert result == protobuf2dict(snapshot.feelings)

def test_pose(random_snapshot):
    snapshot, data, _ = random_snapshot
    result = run_parser('pose', data)
    verify_result_header(result, snapshot)
    verify_pose(result['result'], snapshot)

def test_color_image(random_snapshot):
    snapshot, data, _ = random_snapshot
    result = run_parser('color_image', data)
    verify_result_header(result, snapshot)
    verify_color_image(result['result'], snapshot)

def test_depth_image(random_snapshot):
    snapshot, data, _ = random_snapshot
    result = run_parser('depth_image', data)
    verify_result_header(result, snapshot)
    verify_depth_image(result['result'], snapshot)

def test_feelings(random_snapshot):
    snapshot, data, _ = random_snapshot
    result = run_parser('feelings', data)
    verify_result_header(result, snapshot)
    verify_feelings(result['result'], snapshot)

class MockMQAgent:
    snapshot = None
    result = None

    def __init__(self, url):
        pass

    def consume_snapshots(self, callback, topic):
        callback(self.__class__.snapshot)

    def publish_result(self, result, topic):
        self.__class__.result = result

    @classmethod
    def clear(cls):
        cls.snapshot = cls.result = None

@pytest.fixture
def mock_mq_agent(monkeypatch):
    yield monkeypatch.setattr(brain.parsers.mq_agent.rabbitmq_agent, 'MQAgent', MockMQAgent)
    MockMQAgent.clear()

@pytest.mark.parametrize('parser', ['pose', 'color_image', 'depth_image', 'feelings'])
def test_invoke_parser(parser, mock_mq_agent, random_snapshot):
    snapshot, data, _ = random_snapshot
    MockMQAgent.snapshot = data
    invoke_parser(parser, MQ_URL)
    result = MockMQAgent.result
    verify_result_header(result, snapshot)
    result = result['result']
    if parser == 'pose':
        verify_pose(result, snapshot)
    elif parser == 'color_image':
        verify_color_image(result, snapshot)
    elif parser == 'depth_image':
        verify_depth_image(result, snapshot)
    elif parser == 'feelings':
        verify_feelings(result, snapshot)

@pytest.fixture
def mock_rabbitmq(monkeypatch):
    def expected_callback(data):
        pass

    expected_data = {'abc': 'def'}

    class MockRabbitMQ:
        def __init__(self, url):
            assert url == MQ_URL

        def consume(self, callback, exchange, queues, exchange_type='fanout'):
            assert callback == expected_callback
            assert exchange == 'snapshot'
            assert queues == ['pose']

        def publish(self, data, exchange='', queue=''):
            assert json.loads(data) == expected_data
            assert exchange == 'saver'
            assert queue == 'saver_pose'

    monkeypatch.setattr(brain.parsers.mq_agent.rabbitmq_agent, 'RabbitMQ', MockRabbitMQ)
    yield expected_callback, expected_data

def test_context(tmp_path):
    ctx = Context(str(tmp_path))
    ctx.save('file.txt', 'Some data')
    with open(str(tmp_path / 'file.txt'), 'r') as file:
        assert file.read() == 'Some data'
    ctx = Context()
    calls = {ctx.save: ['file.txt', 'Some data'], ctx.path: ['file.txt'], ctx.delete: ['file.txt']}
    for func, args in calls.items():
        with pytest.raises(Exception) as error:
            func(*args)

        assert 'Cannot access context' in str(error.value)

def test_mq_agent(mock_rabbitmq):
    expected_callback, expected_data = mock_rabbitmq
    mq_agent_module = load_mq_agent('rabbitmq')
    mq_agent = mq_agent_module.MQAgent(MQ_URL)
    mq_agent.consume_snapshots(expected_callback, 'pose')
    mq_agent.publish_result(expected_data, 'pose')

@pytest.fixture
def mock_invoke_parser(monkeypatch):
    def fake_invoke_parser(parser, mq_url):
        assert parser == 'pose'
        assert mq_url == MQ_URL

    monkeypatch.setattr(brain.parsers.__main__, 'invoke_parser', fake_invoke_parser)

def test_cli(random_snapshot, mock_invoke_parser):
    snapshot, data, file_path = random_snapshot
    runner = CliRunner()
    for parser in PARSERS:
        result = runner.invoke(cli, ['parse', parser, file_path])
        assert result.exit_code == 0, result.exception
        parsed = json.loads(result.stdout)
        result_dict = parsed['result']
        if parser == 'pose':
            verify_pose(result_dict, snapshot)
        elif parser == 'color_image':
            verify_color_image(result_dict, snapshot)
        elif parser == 'depth_image':
            verify_depth_image(result_dict, snapshot)
        elif parser == 'feelings':
            verify_feelings(result_dict, snapshot)

    res = runner.invoke(cli, ['run-parser', 'pose', MQ_URL])
    assert res.exit_code == 0, res.exception


from controller import Controller

import asyncio
import json
import websockets

class WebInterface(Controller):
    def __init__(self, key_interface={'Left': ('w', 's'), 'Right': ('o', 'l')}, start_key='r'):
        super()
        self.clients = {'Left': None, 'Right': None}
        self.keys = {'Left': None, 'Right': None, 'start': False}
        self.key_interface = key_interface
        self.start_key = start_key

    def server_run_forever(self, domain=None, port=6789):
        start_server = websockets.serve(self.client_handler, domain, port)

        asyncio.get_event_loop().run_until_complete(start_server)
        asyncio.get_event_loop().run_forever()

    def read(self):
        output = []

        if self.keys['start'] is True:
            output.append(self.start_key)

        for key in self.keys.keys():
            if self.keys[key] == 'up':
                output.append(self.key_interface[key][0])
            elif self.keys[key] == 'down':
                output.append(self.key_interface[key][1])
        return output

    async def register_client(self, websocket):
        for client in self.clients.keys():
            if self.clients[client] is None:
                self.clients[client] = websocket
                message = json.dumps({'player': client})
                await websocket.send(message)
                return client

        message = json.dumps({'player': 'Not a'})
        await websocket.send(message)
        return None

    async def unregister_client(self, websocket):
        for client in self.clients.keys():
            if self.clients[client] is websocket:
                self.clients[client] = None
                break

    async def client_handler(self, websocket, path):
        # Register new web socket as player
        player = await self.register_client(websocket)

        # Handle overflow players
        if not player:
            return

        # Handle web socket connection
        try:
            # Parse each message for button presses and releases
            async for message in websocket:
                data = json.loads(message)
                if data['action'] == 'none':
                    self.keys[player] = None
                elif data['action'] == 'start':
                    self.keys['start'] = True
                else:
                    self.keys[player] = data['action']
        finally:
            # Remove player on disconnect
            await self.unregister_client(websocket)

if __name__ == '__main__':
    controller = WebInterface()
    controller.server_run_forever()


# coding: utf-8

# # Vanilla LSTM in Keras

# In this notebook, we use an LSTM to classify IMDB movie reviews by their sentiment.

# #### Load dependencies

# In[1]:

import keras
from keras.datasets import imdb
from keras.preprocessing.sequence import pad_sequences
from keras.models import Sequential
from keras.layers import Dense, Dropout, Embedding, SpatialDropout1D
from keras.layers import LSTM # new!
from keras.callbacks import ModelCheckpoint
import os
from sklearn.metrics import roc_auc_score
import matplotlib.pyplot as plt
get_ipython().magic('matplotlib inline')

# #### Set hyperparameters

# In[2]:

# output directory name:
output_dir = 'model_output/vanillaLSTM'

# training:
epochs = 1 # 4
batch_size = 128

# vector-space embedding:
n_dim = 64
n_unique_words = 10000
max_review_length = 100 # lowered due to vanishing gradient over time
pad_type = trunc_type = 'pre'
drop_embed = 0.2

# LSTM layer architecture:
n_lstm = 256
drop_lstm = 0.2

# dense layer architecture:
# n_dense = 256
# dropout = 0.2

# #### Load data

# In[3]:

(x_train, y_train), (x_valid, y_valid) = imdb.load_data(num_words=n_unique_words) # removed n_words_to_skip

#Amit - see actual review.. just for debugging
INDEX_FROM=3
word_to_id = keras.datasets.imdb.get_word_index()
word_to_id = {k:(v+INDEX_FROM) for k,v in word_to_id.items()}
word_to_id["<PAD>"] = 0
word_to_id["<START>"] = 1
word_to_id["<UNK>"] = 2

id_to_word = {value:key for key,value in word_to_id.items()}
print(' '.join(id_to_word[id] for id in x_train[0] ))

# #### Preprocess data

# In[4]:

x_train = pad_sequences(x_train, maxlen=max_review_length, padding=pad_type, truncating=trunc_type, value=0)
x_valid = pad_sequences(x_valid, maxlen=max_review_length, padding=pad_type, truncating=trunc_type, value=0)

# #### Design neural network architecture

# In[5]:

model = Sequential()
model.add(Embedding(n_unique_words, n_dim, input_length=max_review_length))
model.add(SpatialDropout1D(drop_embed))
model.add(LSTM(n_lstm, dropout=drop_lstm))
# model.add(Dense(n_dense, activation='relu')) # typically don't see top dense layer in NLP like in
# model.add(Dropout(dropout))
model.add(Dense(1, activation='sigmoid'))

# In[6]:

model.summary()

# #### Configure model

# In[7]:

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

# In[8]:

modelcheckpoint = ModelCheckpoint(filepath=output_dir+"/weights.{epoch:02d}.hdf5")
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

# #### Train!

# In[ ]:

# go have a gander at nvidia-smi
# 85.2% validation accuracy in epoch 2
model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(x_valid, y_valid), callbacks=[modelcheckpoint])

# #### Evaluate

# In[10]:

model.load_weights(output_dir+"/weights.01.hdf5") # zero-indexed

# In[11]:

y_hat = model.predict_proba(x_valid)

# In[12]:

plt.hist(y_hat)
_ = plt.axvline(x=0.5, color='orange')

# In[13]:

"{:0.2f}".format(roc_auc_score(y_valid, y_hat)*100.0)


import os

# 必须在加载 加之前
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d, %d" % (0, 30)

import random
import pygame
from pygame.locals import *
from math import pi, sin, cos

pygame.init()
# 获取显示器大小
screen_width, screen_height = pygame.display.get_desktop_sizes()[0]

ICON = "./icon.png"
TITLE = "见到你时我的心"
WIDTH = 800
HEIGHT = 800
main_loops = True
# 心形中心点
center_x = screen_width / 2
center_y = screen_height / 2
#
screen = pygame.display.set_mode((screen_width, screen_height), pygame.FULLSCREEN)
pygame.display.set_caption(TITLE)
pygame.mouse.set_visible(False)
try:
    pygame.display.set_icon(pygame.image.load(ICON))
except:
    pass

bottomlefttip_h = "[f:全屏/窗口][s:闪烁][t:跳动][+/-:频率][esc:退出]: "
bottomlefttip = bottomlefttip_h
bottomrighttip_h = "[鼠标位置]: "
bottomrighttip = bottomrighttip_h
HOT_PINK = (255,105,180)

class Particle():
    def __init__(self, pos, size, f):
        # (left, top, width, height)
        self.pos = pos.copy()
        self.pos0 = pos.copy()
        self.size = size
        self.f = f

    def draw(self, center_x, center_y):
        """
        Rect((left, top), (width, height)) -> Rect
        :return:
        """

        pygame.draw.rect(screen, HOT_PINK,
                         pygame.Rect((self.size * self.f * self.pos[0] + center_x, -self.size * self.f * self.pos[1] + center_y),
                            (self.pos[2], self.pos[3])),
                         0)

    def update(self, t):
        # 全部一个呼吸系数
        # df = 1 + (2 - 1.5 ) * sin(t * 3) / 8
        # df = 1 +  (heartbeatmplitude )*sin(t * 3) / 8

        # 外内，内快，参数外小内大
        df = 1 + (2 - 1.5 * self.f) * sin(t * 3) / 8
        self.pos[0] = self.pos0[0] * df
        self.pos[1] = self.pos0[1] * df

class MouseParticle():

    def __init__(self, pos):
        # (left, top, width, height)
        self.pos = pos.copy()
        self.particles = []
        self.xiaoshishudu = .8
        self.xiaoshishuduxishu = 1.2

        self.show = .5
        no_p = 50
        # dt 离散点数
        dt = 2 * pi / no_p
        t = 0
        while t <= 2 * pi:
            # 正向随机分面
            l = mu - abs(random.gauss(mu, sigma) - mu)
            # 双向分布
            # l=random.gauss(mu, sigma)
            # l=1，表示画一个线
            # l=1

            xleft = l * 16 * sin(t) ** 3
            ytop = l * (13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t))
            t += dt
            self.particles.append(Particle([xleft, ytop, static_wh[0], static_wh[1]], 1, l))

    def draw(self):
        """
        Rect((left, top), (width, height)) -> Rect
        :return:
        """
        if not self.show:
            return
        if self.xiaoshishudu < 0.000005:
            self.show = 0

        for p in self.particles:
            p.draw(self.pos[0], self.pos[1])

        self.update()

    def update(self):
        self.xiaoshishudu = self.xiaoshishudu ** self.xiaoshishuduxishu
        for p in self.particles:
            p.update(self.xiaoshishudu)

    def jiashudu(self):
        if self.xiaoshishuduxishu < 3:
            self.xiaoshishuduxishu += .1

    def jianshudu(self):
        if self.xiaoshishuduxishu > 1.1:
            self.xiaoshishuduxishu -= .1

mouseParticleList = []

particles = []

"""
若随机变量X服从一个数学期望为μ、方差为σ^2的正态分布，记为N(μ，σ^2)
期望值μ决定了其位置，其标准差σ决定了分布的幅度。当μ = 0,σ = 1时的正态分布是标准正态分布

心形公式
    x=16*sin(t)**3
    y=13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)
"""
# 均值，心形的大小
mu = 1.1
# 是标准差，辐射范围
sigma = .15

# 静态心形点的大小
static_wh = (1.5, 1.5)
# 动态心形点大小，
dynamic_wh = (1, 2)
# 心跳幅度
heartbeatmplitude = 1.2

# 心形大小
size = 15

# 外部开关
waiweikaiguan = True
# 跳动开关
tiaodongkaiguan = True
# 窗口，全屏
fullscreenkaiguan = False
# 跳动频率
jumpfreq=30

no_p = 10000
# dt 离散点数
dt = 2 * pi / no_p
t = 0

#
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++
# 3万个点

def init_dynamic_particles():
    # dt 离散点数
    global t
    # 初始化跳动心形点
    while t <= 2 * pi:
        # 正向随机分面
        l = mu - abs(random.gauss(mu, sigma) - mu)
        # 双向分布
        # l=random.gauss(mu, sigma)
        # l=1，表示画一个线
        # l=1

        xleft = l * 16 * sin(t) ** 3
        ytop = l * (13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t))
        t += dt
        particles.append(Particle([xleft, ytop, static_wh[0], static_wh[1]], size, l))

# def draw():
#     screen.clear()
#     for i in range(len(x)):
#         screen.draw.filled_rect(Rect((x[i]*10+center_x, -y[i]*10+center_y), (4, 4)), 'pink')

def show_ynamic_particles():
    for p in particles:
        p.draw(center_x, center_y)

def show_static_particles():
    # 3万个点
    # no_p = 20000
    # dt 离散点数
    t = 0
    while waiweikaiguan and t < 2 * pi:
        f = random.gauss(mu, sigma * 2)
        x = 16 * sin(t) ** 3
        y = 13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)
        # uniform成下一个实数，它在 [x,y] 范围内
        pygame.draw.rect(screen, HOT_PINK,
                         Rect((17 * f * x + center_x, -17 * f * y + center_y), (random.uniform(.5, 3), random.uniform(.5, 3))),
                         0
                         )
        # screen.draw.filled_rect(
        #     Rect((17 * f * x + center_x, -17 * f * y + center_y), (random.uniform(.5, 3), random.uniform(.5, 3))),
        #     'hot pink')
        t += dt * 2

def show_mouse_particles():
    global mouseParticleList
    t = []
    for p in mouseParticleList:
        if p.show:
            t.append(p)
            p.draw()
        else:
            break
    mouseParticleList = t

def add_mouse_particles(pos):
    global mouseParticleList
    mouseParticleList = [MouseParticle(pos)] + mouseParticleList

def draw_text(sc, str, position, pos:tuple , color, background="black", fontsize=24, name=None):
    text = pygame.font.SysFont(name, fontsize).render(str, True, color, background)
    textRect = text.get_rect()
    if position.startswith("c"):
        textRect.center = pos
    elif position.startswith("m"):
        pass
    elif position.startswith("bottomleft"):
        textRect.bottomleft=pos
    elif position.startswith("bottomright"):
        textRect.bottomright=pos
    elif position.startswith("topleft"):
        textRect.topleft=pos
    elif position.startswith("topright"):
        textRect.topright=pos
    else:
        try:
            raise AttributeError("position")  # 假装这里有异常，一般针对难以复现的异常
        except:
            print("""postion
                    # bottomleft=(100, 100)
                    # topleft=(100, 100)
                    # topright=(100, 100)
                    # bottomright=(100, 100)
                    #
                    # midtop=(100, 100)
                    # midleft=(100, 100)
                    # midbottom=(100, 100)
                    # midright=(100, 100)
                    # center=(100, 100)""")

    sc.blit(text, textRect)

    # bottomleft=(100, 100)
    # topleft=(100, 100)
    # topright=(100, 100)
    # bottomright=(100, 100)
    #
    # midtop=(100, 100)
    # midleft=(100, 100)
    # midbottom=(100, 100)
    # midright=(100, 100)
    # center=(100, 100)
    # centerx
    # centery

def draw():
    # 清空全部内容
    screen.fill("black")
    # 显示区域
    draw_text(screen, "显示文字", "center", (center_x, center_y), HOT_PINK, "black", 24, "SimSun")
    draw_text(screen, bottomlefttip, "bottomleft", (0, center_y * 2), HOT_PINK, "black", 12, "SimSun")
    draw_text(screen, bottomrighttip, "bottomright", (center_x * 2, center_y * 2), HOT_PINK, "black", 12, "SimSun")

    # 显示动态心形
    show_ynamic_particles()
    """
        初始化外部心形情况
    """
    show_static_particles()
    # 显示鼠标
    show_mouse_particles()
    """
        screen.draw.text("ccccccccc\nbbbbbbbbb", center=(100, 100), color='hot pink', background="black", fontsize=24)
        screen.draw.text("1", bottomleft=(100, 100), color=(200, 200, 200), background="black")
        screen.draw.text("2", topleft=(100, 100), color=(200, 200, 200), background="black")
        screen.draw.text("3", topright=(100, 100), color=(200, 200, 200), background="black")
        screen.draw.text("4", bottomright=(100, 100), color=(200, 200, 200), background="black")

        screen.draw.text("5", midtop=(100, 100), color=(200, 200, 200), background="black")
        screen.draw.text("6", midleft=(100, 100), color=(200, 200, 200), background="black")
        screen.draw.text("7", midbottom=(100, 100), color=(200, 200, 200), background="black")
        screen.draw.text("8", midright=(100, 100), color=(200, 200, 200), background="black")
    """
    #刷新一下画面，将画的东西显示到画面上
    pygame.display.update()

def update(dt):
    # dt 1/fps 两帧之间的时间间隔 单位是秒
    global t
    t += dt
    if tiaodongkaiguan:
        for p in particles:
            p.update(t)

# 加载背景音乐
def musicloops(path):
    pygame.mixer.init()
    try:
        pygame.mixer.music.load(path)
        pygame.mixer.music.play(-1)
    except:
        pass

def on_mouse_down(pos, button):
    # print(pos, button)
    global bottomrighttip
    bottomrighttip = bottomrighttip_h + str(pos) + str(button)

def on_mouse_up(pos, button):
    pass

def on_mouse_move(pos, rel, buttons):
    # print(pos, rel, buttons)
    global bottomrighttip
    bottomrighttip = bottomrighttip_h + str(pos)
    # 更新状态
    add_mouse_particles([pos[0], pos[1]])

def on_key_down(key):
    global screen
    global bottomlefttip, fullscreenkaiguan, waiweikaiguan, tiaodongkaiguan
    bottomlefttip = bottomlefttip_h + pygame.key.name(key)
    global center_x, center_y
    global jumpfreq

    if key == K_f:
        if fullscreenkaiguan:
            # screen =
            pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)
            # 发现从pygame.FULLSCREEN，到pygame.RESIZABLE调用一次不起作用
            pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)
            center_x = WIDTH / 2
            center_y = HEIGHT / 2
            pass
            pygame.mouse.set_visible(True )
        else:
            # pygame.display.set_mode((screen_width, screen_height), pygame.NOFRAME)
            # screen =
            pygame.display.set_mode((screen_width, screen_height), pygame.FULLSCREEN)
            pygame.display.set_mode((screen_width, screen_height), pygame.FULLSCREEN)
            center_x = screen_width / 2
            center_y = screen_height / 2
            pygame.mouse.set_visible(False)
        # , pygame.NOFRAME
        fullscreenkaiguan = not fullscreenkaiguan
        bottomlefttip += " 全屏"+str(fullscreenkaiguan)
    elif key == K_ESCAPE:
        global main_loops
        main_loops=False
    elif key == K_SPACE:
        pass
    elif key == K_s:
        waiweikaiguan = not waiweikaiguan
        bottomlefttip += " 闪烁"+str(waiweikaiguan)

    elif key == K_t:
        tiaodongkaiguan = not tiaodongkaiguan
        bottomlefttip += " 跳动"+str(tiaodongkaiguan)

    elif key == K_KP_PLUS or key == K_PLUS:
        if jumpfreq>5:
            jumpfreq-=5
        bottomlefttip += " 频率=" + str(jumpfreq)
    elif key == K_KP_MINUS or key == K_MINUS:
        if jumpfreq<60:
            jumpfreq+=5
        bottomlefttip += " 频率=" + str(jumpfreq)
    elif key == K_MENU:
        pass
    else:
        bottomlefttip += " 无动作 "

# pgzrun.go()

def event():
    global center_x, center_y

    for event in pygame.event.get():

        # if event.type not in [KEYDOWN, MOUSEMOTION, MOUSEBUTTONDOWN, MOUSEBUTTONUP]:
        #     print(event)

        if event.type == QUIT:
            global main_loops
            main_loops = False

        elif event.type == KEYDOWN:
        # 键盘被按下 unicode 、key 、mod
            on_key_down(event.key)
        # https://blog.csdn.net/qq_41556318/article/details/86304649
        # http://t.zoukankan.com/liquancai-p-13235734.html
        elif event.type == MOUSEMOTION:
        # MOUSEMOTION 鼠标移动  pos 、rel 、buttons
        # <Event(1024-MouseMotion {'pos': (289, 464), 'rel': (2, -5), 'buttons': (0, 0, 0), 'touch': False, 'window': None})>
            on_mouse_move(event.pos, event.rel, event.buttons)
        elif event.type == MOUSEBUTTONDOWN:
        # MOUSEBUTTONDOWN 鼠标被按下pos 、button
        # <Event(1025-MouseButtonDown {'pos': (289, 464), 'button': 1, 'touch': False, 'window': None})>
            on_mouse_down(event.pos, event.button)
        elif event.type == MOUSEBUTTONUP:
        # MOUSEBUTTONUP鼠标被放开pos 、button
            on_mouse_up(event.pos, event.button)

        elif event.type == VIDEORESIZE:
            center_x = event.w / 2
            center_y = event.h / 2

        elif event.type == WINDOWMAXIMIZED:
            # 窗口最大化
            print(event)
        elif event.type == WINDOWMINIMIZED:
            # 窗口最大化
            print(event)
            pygame.mixer.music.pause()
        elif event.type == WINDOWRESTORED:
            # 重新显示
            pygame.mixer.music.unpause()
        elif event.type == WINDOWSHOWN:
            print(event)

        elif event.type == ACTIVEEVENT:
            # print(pygame.mixer.music.get_busy())
            # try:
            #     if event.gain and not pygame.mixer.music.get_busy():
            #         #显示内容
            #         pygame.mixer.music.pause()
            #     elif not event.gain and pygame.mixer.music.get_busy():
            #         pygame.mixer.music.pause()
            # except:
            #     pass
            pass

if __name__ == '__main__':
    musicloops("bfa.mp3")
    # 初始化动态心形点，只执行一次
    init_dynamic_particles()

    # Run the game loop.
    while main_loops:
        event()
        update(1/jumpfreq)
        draw()

    pygame.quit()

# pyinstaller -F -c -w -i favicon.ico --clean xx-pygame.py
# cxfreeze xg.py --target-dir x --base-name=win32gui


import numpy as np
import onnx
from onnx import AttributeProto, GraphProto, OperatorSetIdProto, TensorProto, helper, numpy_helper  # noqa: F401

# Gelu formula: x * 0.5 * (1.0 + tanh((sqrt(2 / pi) * (x + 0.044715 * pow(x, 3)))))

X = helper.make_tensor_value_info("input", TensorProto.FLOAT, ["batch", "seqlen", 64])
Y = helper.make_tensor_value_info("output", TensorProto.FLOAT, ["batch", "seqlen", 64])

pow_np_vals = np.asarray([3]).astype(np.float32).reshape(())
pow_initializer = numpy_helper.from_array(pow_np_vals, "pow_init")

a_weight_np_vals = np.asarray([0.044714998453855515]).astype(np.float32).reshape(())
a_weight_initializer = numpy_helper.from_array(a_weight_np_vals, "mul1_init")

b_weight_np_vals = np.asarray([0.7978845834732056]).astype(np.float32).reshape(())
b_weight_initializer = numpy_helper.from_array(b_weight_np_vals, "mul2_init")

c_weight_np_vals = np.asarray([0.5]).astype(np.float32).reshape(())
c_weight_initializer = numpy_helper.from_array(c_weight_np_vals, "mul3_init")

b_bias_np_vals = np.asarray([1.0]).astype(np.float32).reshape(())
b_bias_initializer = numpy_helper.from_array(b_bias_np_vals, "add2_init")

nodes = []
gelu_input = "input"
leading_identity = helper.make_node("Identity", [gelu_input], ["identity_leading"], name="identity_leading")
gelu_input = "identity_leading"
nodes.append(leading_identity)

mul_input_name = gelu_input

cast1 = helper.make_node("Cast", [mul_input_name], ["cast1"], name="cast1", to=1)
nodes.append(cast1)

pow1 = helper.make_node("Pow", ["cast1", pow_initializer.name], ["pow1"], name="pow1")
nodes.append(pow1)

mul1 = helper.make_node("Mul", ["pow1", a_weight_initializer.name], ["mul1"], name="mul1")
nodes.append(mul1)

cast2 = helper.make_node("Cast", [mul_input_name], ["cast2"], name="cast2", to=1)
nodes.append(cast2)

add1 = helper.make_node("Add", ["mul1", "cast2"], ["add1"], name="add1")
nodes.append(add1)

mul2 = helper.make_node("Mul", ["add1", b_weight_initializer.name], ["mul2"], name="mul2")
nodes.append(mul2)

tanh = helper.make_node("Tanh", ["mul2"], ["tanh"], name="tanh")
nodes.append(tanh)

add2 = helper.make_node("Add", ["tanh", b_bias_initializer.name], ["add2"], name="add2")
nodes.append(add2)

mul5 = helper.make_node("Mul", [mul_input_name, c_weight_initializer.name], ["mul5"], name="mul5")
nodes.append(mul5)

cast3 = helper.make_node("Cast", ["mul5"], ["cast3"], name="cast3", to=1)
nodes.append(cast3)

mul6 = helper.make_node("Mul", ["cast3", "add2"], ["mul6"], name="mul6")
ending_identity = helper.make_node("Identity", ["mul6"], ["output"], name="ending_identity")
nodes.extend([mul6, ending_identity])

initializers = []

initializers.extend(
    [
        pow_initializer,
        a_weight_initializer,
        b_weight_initializer,
        b_bias_initializer,
        c_weight_initializer,
    ]
)
# Create the graph (GraphProto)
graph_def = helper.make_graph(nodes, "test-model", [X], [Y], initializers)

opsets = []
onnxdomain = OperatorSetIdProto()
onnxdomain.version = 13
onnxdomain.domain = ""  # The empty string ("") or absence of this field implies the operator set that is defined as part of the ONNX specification.
opsets.append(onnxdomain)

msdomain = OperatorSetIdProto()
msdomain.version = 1
msdomain.domain = "com.microsoft"

opsets.append(msdomain)
kwargs = {}
kwargs["opset_imports"] = opsets

model_def = helper.make_model(graph_def, producer_name="onnx-example", **kwargs)

onnx.save(model_def, "fast_gelu3_with_casts.onnx")


# coding=utf-8
import json

from django.contrib.auth.decorators import login_required
from django.forms.models import model_to_dict
from django.http.response import HttpResponse, HttpResponseBadRequest

from app.utils import require_in_GET, update_object, require_in_POST, update_post_object, get_post_object, json_encoder
from ..models.labs import StudentLab, StudentTask
from ..models.discipline import Discipline
from ..models import Group
from ..models.labs import StudentTaskResult

permitted_keys = ['title', 'description', 'discipline_id', 'visible', 'columns_count', 'regular']

@require_in_GET('discipline_id')
def index(request):
    """
    Возвращает лабы для дисциплины
    """
    discipline_id = request.GET['discipline_id']

    labs = StudentLab.objects.filter(discipline_id=discipline_id)
    if not request.user.is_authenticated():
        labs = labs.filter(visible=True)

    labs = list([model_to_dict(l) for l in labs])
    for l in labs:
        marks = StudentTaskResult.objects.filter(task__lab=l['id'])
        marks = list([t.as_dict for t in marks])
        tasks = list([t.as_dict for t in StudentTask.objects.filter(lab=l['id']).order_by('complexity', '_order', 'id')])
        l.update({
            'tasks': tasks,
            'marks': marks
        })

    if not request.user.is_authenticated():
        labs = filter(lambda x: len(x['tasks']) > 0, labs)

    return HttpResponse(json.dumps({
        'labs': labs,
        'complex_choices': dict(StudentTask.COMPLEX_CHOICES)
    }, default=json_encoder), content_type='json')

#
# @require_in_GET('discipline_id', 'group_id')
# def progress_table(request):
#     g = Group.objects.get(id=request.GET['group_id'])
#     assert isinstance(g, Group)
#     students = g.students
#     labs = StudentLab.objects.filter()
#     return HttpResponseBadRequest()

@login_required
@require_in_POST('discipline_id')
def new(request):
    """
    add new lab to discipline
    :param request:
    :return:
    """
    lab = StudentLab()

    if 'bgimage' in request.FILES:
        lab.bgimage = request.FILES['bgimage']
    update_object(request.POST, lab, *permitted_keys)
    lab.save()

    return HttpResponse(json.dumps(lab.as_dict()), content_type='json')

@login_required
@require_in_POST('id')
def delete(request):
    lab = get_post_object(request, StudentLab)
    lab.delete()
    return HttpResponse()

@login_required
@require_in_POST('id')
def clear_image(request):
    lab = get_post_object(request, StudentLab)
    lab.bgimage.delete()
    return HttpResponse()

@login_required
@require_in_POST('id')
def save(request):
    lab = get_post_object(request, StudentLab)
    assert isinstance(lab, StudentLab)

    if 'order_array' in request.POST:
        order_array = json.loads(request.POST['order_array'])
        order_array = list([int(i) for i in order_array])
        lab.set_studenttask_order(order_array)

    if 'bgimage' in request.FILES:
        if lab.bgimage:
            lab.bgimage.delete()
        lab.bgimage = request.FILES['bgimage']

    update_object(request.POST, lab, *permitted_keys)
    lab.save()
    return HttpResponse(json.dumps(lab.as_dict()), content_type='json')

@login_required
@require_in_POST('marks')
def save_task_marks(request):
    marks = json.loads(request.POST['marks'])

    for m in marks:
        tr = StudentTaskResult.objects.filter(student=m['student'], task=m['task']).first()
        if m['done'] == 'false':
            if tr:
                tr.delete()
        else:
            if tr is None:
                tr = StudentTaskResult()
                tr.student_id = m['student']
                tr.task_id = m['task']

            tr.done = m['done']
            tr.save()

    return HttpResponse()

@login_required
@require_in_POST('order_array', 'id')
def lab_save_order(request):
    discipline = get_post_object(request, Discipline)
    order_array = json.loads(request.POST['order_array'])
    order_array = list([int(i) for i in order_array])
    discipline.set_studentlab_order(order_array)
    return HttpResponse()


import pandas as pd
import networkx as nx
from pyvis import network as net
from torch_geometric.utils import to_networkx, degree
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
import plotly.express as px
from rdkit import Chem
from rdkit.Chem import Descriptors
import mols2grid
import py3Dmol

def generate_nx_graph(data):
    # Create a directed graph from the data object, then convert it to undirected.
    G = to_networkx(data)  # Do not use the parameter to_undriected. It does not work properly.
    G_undirected = G.to_undirected()
    return G, G_undirected

def generate_khop_subgraph(msi, G, gid, k):
    # Get the neighbors of the drug amphetamine (id=DB00182, name="Amphetamine", gid=17713)
    node_label_dict = {}
    node_type_dict = {}
    node_size_dict = {}
    subgraph_nodes = nx.single_source_shortest_path_length(G, gid, cutoff=k)
    print(f"Total subgraph nodes: {len(subgraph_nodes)}")

    # Get the maximum gids of each type of node.
    max_prot_gid = max(msi.mappings["prot_id_gid_map"].values())
    max_drug_gid = max(msi.mappings["drug_id_gid_map"].values())
    max_dz_gid = max(msi.mappings["dz_id_gid_map"].values())
    max_func_gid = max(msi.mappings["func_id_gid_map"].values())

    # Get the id_map, name_map, type, and size for each node in the subgraph.
    for node in subgraph_nodes:
        if node <= max_prot_gid:
            id_mapping = msi.mappings["prot_id_gid_map"].copy()
            name_mapping = msi.mappings["prot_id_name_map"].copy()
            node_type = "protein"
            node_size = "10"
        elif node > max_prot_gid and node <= max_drug_gid:
            id_mapping =  msi.mappings["drug_id_gid_map"].copy()
            name_mapping = msi.mappings["drug_id_name_map"].copy()
            node_type = "drug"
            node_size = "25"
        elif node > max_drug_gid and node <= max_dz_gid:
            id_mapping = msi.mappings["dz_id_gid_map"].copy()
            name_mapping = msi.mappings["dz_id_name_map"].copy()
            node_type = "disease"
            node_size = "35"
        else:
            id_mapping = msi.mappings["func_id_gid_map"].copy()
            name_mapping = msi.mappings["func_id_name_map"].copy()
            node_type = "function"
            node_size = "20"

        # Assign the node a label, type, and size.
        for id, gid in id_mapping.items():
            if node == gid:
                node_label_dict[gid] = name_mapping[id]
                node_type_dict[gid] = node_type
                node_size_dict[gid] = node_size

    # Generate the networkx subraph and assign node attributes.
    subG = G.subgraph(list(subgraph_nodes.keys()))
    for i, node in enumerate(subG.nodes):
        subG.nodes[node]["label"] = node_label_dict[node]
        subG.nodes[node]["group"] = node_type_dict[node]
        subG.nodes[node]["size"] = node_size_dict[node]

    return subG

def generate_pyvis_graph(G):
    pyvis_graph = net.Network(notebook=True)
    pyvis_graph.from_nx(G)
    return pyvis_graph

def draw_molecule(mol_smiles):

    mol = Chem.MolFromSmiles(mol_smiles)
    mol = Chem.MolToMolBlock(mol, confId=-1)
    view = py3Dmol.view(width=400,height=400)
    view.removeAllModels()

    view.addModel(mol,'sdf')
    view.setStyle({'stick':{}})
    view.setBackgroundColor('0xeeeeee')
    view.zoomTo()

    return view.show()

def interactive_mol_grid(
        mol_dict,
        n_cols=3,
        size=(210,130),
        gap=0,
        selection=True,
        border="2px ridge black",
        fontfamily="Times New Roman"):

    """ Create interactive molecule grid using mol2grid library.

    Parameters
    ----------
    mol_dict : dict
        Dictionary requiring 'name' key and list of molecule names as value and
        'smiles' key with list of smiles string as value.
    n_cols : int
        Set the number of columns for the viewer (default = 3).
    size : tuple
        Set the size of each molecule (default = (210,13))
    gap : int
        Set the spacing between the molecules in the grid (default=0).
    selection : bool
        Turn the selection box on or off (default = True).
    border : str
        Set the css border style (default = 2px ridge black).
    fontfamily : str
        Set the css-style font (default = Times New Roman).

    Returns
    -------
    viewer : IPython.core.display.HTML
        Interactive HTML object displaying molecules and accompanying information in
        a grid.
    df : DataFrame
        Dataframe with chemical information for each molecule.
    """

    # Load the dictionary into a dataframe.
    df = pd.DataFrame.from_dict(mol_dict)

    # Create mol objects from smiles strings.
    df["mol"] = df["SMILES"].apply(Chem.MolFromSmiles)

    # Compute extra molecular properties.
    df["MolWt"] = df["mol"].apply(Descriptors.ExactMolWt)
    df["QED"] = df["mol"].apply(Descriptors.qed)
    df["LogP"] = df["mol"].apply(Descriptors.MolLogP)
    df["NumHDonors"] = df["mol"].apply(Descriptors.NumHDonors)
    df["NumHAcceptors"] = df["mol"].apply(Descriptors.NumHAcceptors)

    # Reformat the dataframe.
    df.drop(columns=["mol"], inplace=True)

    # Create molecule viewer.
    viewer = mols2grid.display(
            df,
            n_cols=3,
            subset=["name", "img"],  # set the fields  displayed on the grid.
            tooltip=["name", "MolWt", "QED"], # set the fields displayed on mouse hover.
            template="pages",
            size=size, # size of each image.
            gap=gap, # spacing between molecules.
            selection=selection, # toggle selection box.
            border=border, # css border style.
            fontfamily=fontfamily) # css font style.

    return df, viewer

def visualize_tsne_embeddings(model, data, title, perplexity=30.0,
                              labeled=False, labels=[]):
    """Visualizes node embeddings in 2D space with t-SNE.

    Parameters
    ----------
    model : trained or untrained model
        A trained or untrained model.
    data : Data object
        A data object
    title : str
        Title of the plot
    perplexity : float
        t-SNE hyperparameter for perplexity
        """
    model.eval()
    x = data.x
    z = model.encode(x, data.edge_index)
    ax1, ax2 = zip(*TSNE(n_components=2, learning_rate='auto', perplexity=perplexity,
                       init='random').fit_transform(z.detach().cpu().numpy()))

    fig = px.scatter(x=ax1, y=ax2, color=['r']*17660 + ['g']*1661 + ['b']*840 + ['p']*9798,
                   title=title)

    if labeled:
        for i in labels:
            fig.add_annotation(x=ax1[i], y=ax2[i],
                         text=str(i), showarrow=False)
    fig.show()

def visualize_pca_embeddings(model, data, title, labeled=False, labels=[]):
    """Visualizes node embeddings in 2D space with PCA (components=2).

    Parameters
    ----------
    model : trained or untrained model.
    data : Data object
        A data object.
    title : str
        Title of the plot.
        """

    model.eval()
    x = data.x
    z = model.encode(x, data.edge_index)
    pca = PCA(n_components=2)
    components = pca.fit_transform(z.detach().cpu().numpy())
    fig = px.scatter(components, x=0, y=1, color=['r']*17660 + ['g']*1661 + ['b']*840 + ['p']*9798,
                   title=title)
    if labeled:
        for i in labels:
            fig.add_annotation(x=components[:,0][i], y=components[:,1][i],
                         text=str(i), showarrow=False)
    fig.show()


from django.conf import settings

import requests
from rest_framework import viewsets, response, mixins, generics

from real_deal.offers.models import Offer

from .serializers import OfferSerializer

ENGLISH = 1
MONEY_OFF = 35
FREE_TRIAL = 74

class OffersViewSet(mixins.ListModelMixin,
                    viewsets.GenericViewSet):

    queryset = Offer.objects.all()
    serializer_class = OfferSerializer
    pagination_class = None

    def list(self, request, *args, **kwargs):
        url = 'https://sandbox.api.visa.com/vmorc/offers/v1/byfilter'
        r = requests.get(
            url,
            cert=(settings.VISA_CERT_PATH, settings.VISA_PRIVKEY_PATH),
            auth=(settings.VISA_USERNAME, settings.VISA_PASSWORD),
            params={'language': ENGLISH}
        )
        visa_data = r.json()

        data = [
            {
                'title': offer['offerTitle'],
                'short_description': offer['offerShortDescription']['text'],
                'copy': offer_copy(offer['offerTitle'], offer['offerCopy']['text']),
                'merchants': [
                    {'name': merchant_name(merchant['merchant'])}
                    for merchant in offer['merchantList']
                ]
            }
            for offer in visa_data['Offers']
            if offer['offerType'][0]['key'] in (MONEY_OFF, FREE_TRIAL)
        ]

        return response.Response(data)

def merchant_name(name):
    '''Convert ugly name to pretty name!'''
    if name == 'Merchant One 1':
        return 'Walmart'
    elif name == 'Merchant Two':
        return 'Target'
    return 'Bath & Body Works'

def offer_copy(offer_title, orig_copy):
    if offer_title == 'Save $30':
        return 'Save $30 on purchases that exceed $125 when you use your visa card'
    elif offer_title == 'Save $40':
        return 'Save $40 on purchases that exceed $150 when you use your visa card'
    elif offer_title == 'Free Trial':
        return 'Exlusive for Visa cardholders, call for availability'
    return orig_copy

class AlexaAskViewSet(viewsets.GenericViewSet):
    def create(self, request, *args, **kwargs):
        r = requests.post('http://localhost:5000/_ask', data=request.data)
        return response.Response(r.json(), status=r.status_code)


from setuptools import setup, find_packages

with open('README.md', 'r') as readmefile:
    readme = readmefile.read()

setup(
    name='glitch_this',
    version='1.0.2',
    author='TotallyNotChase',
    author_email='totallynotchase42@gmail.com',
    description='A package to glitch images and GIFs, with highly customizable options!',
    long_description=readme,
    long_description_content_type='text/markdown',
    url='https://github.com/TotallyNotChase/Glitch-and-Gif',
    packages=find_packages(),
    entry_points={
        'console_scripts':['glitch_this=glitch_this.commandline:main'],
    },
    install_requires=[
        'Pillow>=6.2.1',
        'numpy>=1.18.1',
    ],
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.6',
)


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import sys
import os
import argparse
ROOT_DIR = os.path.abspath(os.path.join(__file__, "../.."))
sys.path.insert(0, ROOT_DIR)
from utils import logger
from engine import config
from importlib import import_module
from dataset.loader import TorchDataset, TorchDataLoader
from dataset.reader import read_h_matrix_file_list
from utils.interpolation import interpolate
from utils.label_fusion import heirarchical_ensemble
from utils.metric import IouMetric, AccuracyMetric, HierarchicalConsistency
from datetime import datetime
import numpy as np

parser = argparse.ArgumentParser()
parser.add_argument('-cfg', '--config', type=str, default='configs/sem_seg_default_block',
                    help='config directory')
parser.add_argument('-s', '--set', type=str, default='TEST_SET',
                    help='evaluation set: TEST_SET/VALIDATION_SET')
parser.add_argument('-ckpt', '--check_point', type=str, default='epoch108_model.ckpt',
                    help='check point file name')
parser.add_argument('-o', '--output', type=str, default='eval_{}.log'.format(datetime.now().strftime('%Y_%m_%d_%I%M')),
                    help='output log file')
parser.add_argument('-gpu', '--gpu_knn', type=int, default=None, help='gpu id for knn')
args = parser.parse_args()
eval_set = args.set

# Load config
abs_cfg_dir = os.path.abspath(os.path.join(__file__, "../../{}".format(args.config)))
config.merge_cfg_from_dir(abs_cfg_dir)
cfg = config.CONFIG

#Load logger
logger.file_output(args.output, level='info')
log = logger.LOG
log.info('Config DIR: {}\n PID: {}'.format(args.config, os.getpid()))
log.info('Config Details {}:'.format(cfg))

#load hierirachical relationship
HM = read_h_matrix_file_list(cfg.DATASET.DATA.H_MATRIX_LIST_FILE)

#Load train module
test_process = import_module("models." + cfg.MODEL + '.campusnet_test')
metric_cal = import_module('utils.metric')

#Set GPU
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = ','.join([str(i) for i in cfg.DEVICES.GPU_ID])

log.info('Eval area:{}'.format(eval_set))
if True:
    TEST_DATASET = TorchDataset(eval_set,
                                params=cfg.DATASET,
                                is_training=False,)
    TEST_LOADER = TorchDataLoader(dataset=TEST_DATASET,
                                  batch_size=cfg.TRAIN.BATCH_SIZE,
                                  num_workers=cfg.TRAIN.BATCH_SIZE)
    log.info('Eval size:{}'.format(TEST_DATASET.labels[0].shape))

    points, logits = test_process.test(cfg, TEST_LOADER, log, args.check_point)

    log.info('Heirarchical ensemble')
    path_label = heirarchical_ensemble(logits, HM, weight=np.full((5,), 1.0))

    log.info('Interpolation')
    D, I = interpolate(sparse_points=points, dense_points=TEST_DATASET.points[0], GPU_id=args.gpu_knn)

    log.info('Cal IoU/OA MT')
    pred_labels = []
    for i in range(len(logits)):
        log.info('IoU {}'.format(i))
        tmp_label = np.argmax(logits[i], axis=1)
        new_label = tmp_label[I]
        iou = IouMetric.cal_iou(np.squeeze(new_label), TEST_DATASET.labels[0][...,i], label_range=list(range(logits[i].shape[-1])))

        iou_string = [str(layer_iou) for layer_iou in iou]
        iou_string = '\n'.join(iou_string)
        log.info(iou_string)
        oa = AccuracyMetric.cal_oa(pred=np.squeeze(new_label), target=TEST_DATASET.labels[0][...,i])
        log.info('OA {}:{}'.format(i, oa))
        pred_labels.append(new_label)

    labels = np.asarray(pred_labels).transpose()
    cr = HierarchicalConsistency.cal_consistency_rate(HM, np.squeeze(labels))
    log.info('Cal consistent rate MT: {}'.format(cr))

    log.info('Cal IoU/OA HE')
    pred_labels = []
    for i in range(len(logits)):
        log.info('IoU {}'.format(i))
        new_label = path_label[..., i][I]
        iou = IouMetric.cal_iou(np.squeeze(new_label), TEST_DATASET.labels[0][...,i], label_range=list(range(logits[i].shape[-1])))
        iou_string = [str(layer_iou) for layer_iou in iou]
        iou_string = '\n'.join(iou_string)
        log.info(iou_string)
        oa = AccuracyMetric.cal_oa(pred=np.squeeze(new_label), target=TEST_DATASET.labels[0][...,i])
        log.info('OA {}:{}'.format(i, oa))
        pred_labels.append(new_label)

    labels = np.asarray(pred_labels).transpose()
    cr = HierarchicalConsistency.cal_consistency_rate(HM, np.squeeze(labels))
    log.info('Cal consistent rate HE: {}'.format(cr))


import collections as coll
import heapq
import re

def bin_to_int(x):
    # Converts a string of bits to an integer.
    if not x:
        return 0
    return int(x, 2)

def hex_to_bin(x):
    # Converts a string of hexadecimal numbers to a string of bits (preserving leading zeros).
    return bin(int(x, 16))[2:].zfill(len(x) * 4)

def extract_ints(text):
    # Extracts all integers.
    return [int(x) for x in re.findall(r'-?\d+', text)]

def a_star(graph, start, end, neighbors, heuristic, start_cost=0):
    visited = set()
    costs = coll.defaultdict(lambda: 1e9)
    costs[start] = start_cost
    queue = []
    heapq.heappush(queue, (costs[start], start))
    while len(queue) > 0:
        _, current = heapq.heappop(queue)
        if current == end:
            return costs[current]
        for n, c in neighbors(graph, current):
            if n not in visited:
                cost = costs[current] + c
                if cost < costs[n]:
                    costs[n] = cost
                priority = cost + heuristic(graph, n)
                heapq.heappush(queue, (priority, n))
        visited.add(current)
    return None


import logging

from flask import request, abort, jsonify, g
from flask_restplus import Resource
from api.problems.serializers import problem as api_problem
from api.problems.serializers import problem_table, problem_description, problem_edition, main_problem
from api.restplus import api
from models import db, Problem, Topic, ProblemTopic, \
    Case, Language, User, Taken, Statistic, Team, Teamenroll, Submission
from sqlalchemy import join, and_
from sqlalchemy.orm import Load
from authorization import auth_required
from api.evaluators.services import update_test_cases_in_filesystem

log = logging.getLogger(__name__)

ns = api.namespace('problems', description='Operations related to problems')

@ns.route('/')
@api.header('Authorization', 'Auth token', required=True)
class ProblemCollection(Resource):

    @api.marshal_list_with(api_problem)
    @auth_required('professor')
    def get(self):
        """
        Returns list of problems.
        """
        problems = db.session.query(Problem).filter(Problem.is_subproblem == False).order_by(Problem.id).all()

        # Get user
        token = request.headers.get('Authorization', None)
        user = User.verify_auth_token(token)

        # Retrieve problem's language name
        for problem in problems:
            problem.language = Language.query.filter(Language.value == problem.language).one().name
            if (user.role == 'admin'):
                problem.can_edit = True
            elif (user.role == 'professor' and problem.author_id == user.id):
                problem.can_edit = True
            else:
                problem.can_edit = False

        return problems

@ns.route('/changestatus/<int:problem_id>/<int:status>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found.')
class ProblemStatus(Resource):

    @api.response(204, 'Problem successfully updated.')
    @auth_required('professor')
    def put(self, problem_id, status):
        """
        Updates a problem's status.
        """
        if (status == 0):
            active = False
        else:
            active = True

        Problem.query.filter(Problem.id == problem_id).update({'active' : active})

        db.session.commit()

        return None, 204

@ns.route('/problembyname/<string:problem_name>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found.')
class ProblemStatus(Resource):

    @api.marshal_with(api_problem)
    @api.response(204, 'Problem successfully updated.')
    @auth_required('professor')
    def get(self, problem_name):
        """
        Return problem by name
        """
        # Check if problem exists
        problem = db.session.query(Problem).filter(Problem.name == problem_name).first()

        if (problem is not None):
            problem.language = Language.query.filter(Language.value == problem.language).one().name
        else:
            return None, 404

        return problem

@ns.route('/<int:id>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found.')
class ProblemItem(Resource):

    @api.marshal_with(api_problem)
    @auth_required('professor')
    def get(self, id):
        """
        Returns a problem.
        """

        # Check if id is valid
        try:
            id = int(id)
        except ValueError:
            return None, 404

        # Check if problem exists
        problem = db.session.query(Problem).filter(Problem.id == id).first()

        if (problem is not None):
            problem.language = Language.query.filter(Language.value == problem.language).one().name
        else:
            return None, 404

        return problem

    @api.response(204, 'Problem successfully deleted.')
    @auth_required('admin')
    def delete(self, id):
        """
        Deletes a problem.
        """
        problem = Problem.query.filter(Problem.id == id).one()
        case = Case.query.filter(Case.problem_id == id).one()
        problemtopic = ProblemTopic.query.filter(ProblemTopic.problem_id == id).one()
        statistic = Statistic.query.filter(Statistic.problem_id == id).one()
        db.session.delete(case)
        db.session.delete(problemtopic)
        db.session.delete(statistic)
        db.session.delete(problem)
        db.session.commit()

        return None, 204

    @api.expect(api_problem)
    @api.response(204, 'Problem successfully updated.')
    @auth_required('professor')
    def put(self, id):
        """
        Updates a problem.
        """
        data = request.json

        topics = data['topics']
        test_cases  = data.get('cases')
        data.pop('topics', None)
        data.pop('cases', None)

        # Update problem's common fields
        Problem.query.filter(Problem.id == id).update(data)

        # Update test cases
        if (test_cases is not None):
            Case.query.filter(Case.problem_id == id).delete()

            # Create test cases
            for i in range(len(test_cases)):
                new_case = Case(is_sample=test_cases[i]['is_sample'],
                                input=test_cases[i]['input'],
                                feedback=test_cases[i]['feedback'],
                                output=test_cases[i]['output'],
                                problem_id=id)
                db.session.add(new_case)

            update_test_cases_in_filesystem({'problem_id' : id, 'test_cases' : test_cases})

        # Delete problem's topics
        ProblemTopic.query.filter(ProblemTopic.problem_id == id).delete()

        # Create new problem's topic
        new_problemtopic = ProblemTopic(problem_id=id,
                                    topic_id=topics)
        db.session.add(new_problemtopic)

        db.session.commit()

        return None, 204

@ns.route('/description/<int:id>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found.')
class ProblemDescription(Resource):

    @api.marshal_with(problem_description)
    @auth_required('student')
    def get(self, id):
        """
        Returns the descriptions of a problem.
        """
        # Check if id is valid
        try:
            id = int(id)
        except ValueError:
            return None, 404

        problem = db.session.query(Problem).filter(Problem.id == id).first()

        if (problem is None):
            return None, 404

        # If user is student, check that problem is activated
        # Get user
        token = request.headers.get('Authorization', None)
        user = User.verify_auth_token(token)
        if (user.role == 'student' and problem.active == False):
            return None, 404

        # Complete missing fields
        cases = db.engine.execute("""
            SELECT c.input, c.output
            FROM problem p, \"case\" c
            WHERE c.problem_id = p.id AND p.id = %d AND c.is_sample = TRUE""" % (id)).fetchall()

        descriptions = {}
        descriptions["english"]       = problem.description_english
        descriptions["spanish"]       = problem.description_spanish
        descriptions["title"]         = problem.name
        descriptions["test_cases"]    = cases
        descriptions["signature"]     = problem.signature
        descriptions["language_name"] = Language.query.filter(Language.value == problem.language).one().name
        descriptions["language_code"] = problem.language

        print(descriptions)

        return descriptions

@ns.route('/listbytopic/<int:user_id>/<int:topic_id>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found.')
class ProblemsByTopic(Resource):

    @auth_required('student')
    def get(self, user_id, topic_id):
        """
        Returns list of problems by topic, indicating if the problem has been solved by user
        """

        # Retrieve raw list of problems by topic

        # If topic id == 0, return all problems
        if (topic_id == 0):
            result = db.engine.execute("SELECT p.id, p.name, p.difficulty FROM Problem p WHERE p.active=true AND p.is_subproblem =false")
        else:
            result = db.engine.execute("SELECT p.id, p.name, p.difficulty FROM Problem p, ProblemTopic pt WHERE p.active=true AND p.is_subproblem = false AND p.id = pt.problem_id AND pt.topic_id = %d" % (topic_id))

        problems_list = []

        # Mark problem status (not attempted, attempted but wrong answer, or solved)
        for problem in result:
            # Problem not attempted
            if (len(db.engine.execute("SELECT p.id FROM Problem p WHERE p.id = %d AND NOT EXISTS (SELECT s.id FROM Submission s WHERE p.id = s.problem_id AND s.user_id = %d)" % (problem[0], user_id)).fetchall()) > 0):
                problems_list.append({'problem_id' : problem[0], 'name' : problem[1], 'difficulty' : problem[2], 'status' : 'not_attempted'})
            # Problem attempted but not solved
            elif (len(db.engine.execute("SELECT p.id FROM Problem p WHERE p.id = %d AND NOT EXISTS (SELECT s.id FROM Submission s WHERE p.id = s.problem_id AND s.grade = 100 AND s.user_id = %d)" % (problem[0], user_id)).fetchall()) > 0):
                problems_list.append({'problem_id' : problem[0], 'name' : problem[1], 'difficulty' : problem[2], 'status' : 'wrong_answer'})
            # Problem solved
            else:
                problems_list.append({'problem_id' : problem[0], 'name' : problem[1], 'difficulty' : problem[2], 'status' : 'accepted'})

        return problems_list

@ns.route('/list/')
@api.header('Authorization', 'Auth token', required=True)
class ProblemsList(Resource):

    @api.marshal_list_with(problem_table)
    @auth_required('professor')
    def get(self):
        """
        Returns list of problems for table display
        """

        # Retrieve raw list of problems by topic
        result = db.engine.execute("SELECT p.id, p.name, t.name as topic, p.difficulty, p.active, p.author_id FROM Problem p, Topic t, ProblemTopic pt WHERE p.id = pt.problem_id AND t.id = pt.topic_id").fetchall()

        # Get user
        token = request.headers.get('Authorization', None)
        user = User.verify_auth_token(token)

        problems = []

        # Retrieve problem's language name
        for row in result:
            problem = dict(row.items())
            if (user.role == 'admin'):
                problem["can_edit"] = True
            elif (user.role == 'professor' and row.author_id == user.id):
                problem["can_edit"] = True
            else:
                problem["can_edit"] = False
            problems.append(problem.copy())

        return problems

@ns.route('/mainproblems/')
@api.header('Authorization', 'Auth token', required=True)
class ProblemsList(Resource):

    @api.marshal_list_with(main_problem)
    @auth_required('professor')
    def get(self):
        """
        Returns list of main problems
        """
        # problems = db.engine.execute("""
        # SELECT *
        # FROM Problem p
        # WHERE p.is_subproblem = 'False'
        # """).fetchall()

        problems = Problem.query.filter(Problem.is_subproblem == False).all()

        return problems

#
@ns.route('/check-parent-team/<int:team_id>/<int:problem_id>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found.')
class ProblemsByTopic(Resource):

    @auth_required('student')
    def get(self, team_id, problem_id):
        """
        Returns a list containing whether the team has done all the subchilds
        problems or not = can they submit the parent problem?
        """

        # Retrieve raw list of the sub-problems of the main problem(problem_id)
        result = db.engine.execute("SELECT p.id, p.name, p.difficulty, p.is_subproblem FROM Problem p WHERE p.belongs_to = %d" % (problem_id)).fetchall()
        # get students of the team
        users = Teamenroll.query.filter(Teamenroll.team_id == team_id).all()
        # initialize the states of the subproblems. False means they are not done by anyone on the team
        problems_list = [False for p in result]
        index = 0
        for user in users:
            index = 0
            for problem in result:
                res = Submission.query.filter(
                    Submission.user_id == user.student_id,
                    Submission.problem_id == problem.id,
                    Submission.grade == 100
                    ).all()
                # if the result is not an empty list
                if res:
                    problems_list[index] = True
                index = index + 1

        canSolveParent = True
        for p in problems_list:
            if p == False:
                canSolveParent = False

        if canSolveParent:
            problems_list.append({'all_accepted':'ok'})
        else:
            problems_list.append({'all_accepted':'no'})

        return problems_list

@ns.route('/listbysub-problem/<int:user_id>/<int:problem_id>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found.')
class ProblemsByTopic(Resource):

    @auth_required('student')
    def get(self, user_id, problem_id):
        """
        Returns list of sub-problem by problem, indicating if the problem has been solved by user
        """

        # Retrieve raw list of problems by sub-problems

        result = db.engine.execute("SELECT p.id, p.name, p.difficulty, p.is_subproblem FROM Problem p WHERE p.belongs_to = %d" % (problem_id))
        problems_list = []
        counter = True

        print(result)
        # Mark problem status (not attempted, attempted but wrong answer, or solved)
        for problem in result:
            # Problem not attempted
            print(problem[0])
            if (len(db.engine.execute("SELECT p.id FROM Problem p WHERE p.id = %d AND NOT EXISTS (SELECT s.id FROM Submission s WHERE p.id = s.problem_id AND s.user_id = %d)" % (problem[0], user_id)).fetchall()) > 0):
                problems_list.append({'problem_id' : problem[0], 'name' : problem[1], 'difficulty' : problem[2], 'status' : 'not_attempted', 'is_subproblem': problem[3]})
                counter = False
            # Problem attempted but not solved
            elif (len(db.engine.execute("SELECT p.id FROM Problem p WHERE p.id = %d AND NOT EXISTS (SELECT s.id FROM Submission s WHERE p.id = s.problem_id AND s.grade = 100 AND s.user_id = %d)" % (problem[0], user_id)).fetchall()) > 0):
                problems_list.append({'problem_id' : problem[0], 'name' : problem[1], 'difficulty' : problem[2], 'status' : 'wrong_answer', 'is_subproblem':problem[3]})
                counter = False
            # Problem solved
            else:
                problems_list.append({'problem_id' : problem[0], 'name' : problem[1], 'difficulty' : problem[2], 'status' : 'accepted', 'is_subproblem':problem[3]})

        if counter:
            problems_list.append({'all_accepted':'ok'})
        else:
            problems_list.append({'all_accepted':'no'})

        return problems_list

@ns.route('/take-problem/<int:user_id>/<int:problem_id>/<int:team_id>')
@api.header('Authorization', 'Auth token', required=True)
@api.response(404, 'Problem not found')
class TakeProblem(Resource):
    @auth_required('student')
    def put(self, user_id, problem_id, team_id):
        """
        Marks a problem as Taken
        """
        validates = db.engine.execute("SELECT p.belongs_to FROM Problem p WHERE p.id = %d" % (problem_id))
        main_problem = validates
        for row in main_problem:
            print(row[0])
            if row[0] is not None:
                validates = db.engine.execute("SELECT p.id FROM Problem p WHERE p.belongs_to = %d" % (row[0]))
                sub_problems = validates
                for problem in sub_problems:
                    print(problem[0])
                    validates = db.engine.execute("SELECT t.id FROM Taken t WHERE t.user_id = %d AND t.problem_id = %d" % (user_id, problem[0]))
                    # User already has one problem from the sub_problems taken
                    if validates.rowcount > 0:
                        return None, 404
            else:
                return None, 404

        new_taken = Taken(user_id = user_id, problem_id = problem_id, team_id = team_id)
        db.session.add(new_taken)
        db.session.commit()

@ns.route('/leave_problem/<int:user_id>/<int:problem_id>')
@api.header('Authorization', 'Auth toke', required=True)
@api.response(404, 'Problem not found')
class LeaveProblem(Resource):
    @auth_required('student')
    def delete(self, user_id, problem_id):
        """
        Unmarks a problem
        """
        taken = Taken.query.filter(and_(Taken.user_id == user_id, Taken.problem_id == problem_id)).first()
        print(type(taken))
        if taken is not None:
            db.session.delete(taken)
            db.session.commit()
            return None, 204
        else:
            return None, 404


from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .forms import VideoFileForm
from .models import Categories
from django.core.files.storage import FileSystemStorage
from django.contrib import messages
from django.conf import settings
import uuid
import io
from moviepy.editor import *
from google.cloud import language_v1, speech
from .words import programming_words

# Create your views here.

# Instantiates a client
client = language_v1.LanguageServiceClient()
speech_client = speech.SpeechClient()

# separates video and audio in video file
def strip_video(video_file):
    filename = os.path.join(settings.MEDIA_ROOT, 'audio') + '/' + str(uuid.uuid4()) + '.wav' # 'audio/' + str(uuid.uuid4()) + '.wav'
    audio = AudioFileClip(video_file)
    audio.write_audiofile(filename, fps=16000, bitrate='256k', nbytes=2, codec='pcm_s16le')
    return filename

# returns transcription of audio file
def transcribe(audio_file):
    with io.open(audio_file, 'rb') as a:
        content = a.read()
        audio = speech.RecognitionAudio(content=content)

    config = speech.RecognitionConfig(
        encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16,
        sample_rate_hertz=16000,
        language_code='en-US',
        audio_channel_count=2,
    )

    response = speech_client.recognize(config=config, audio=audio)

    transcript = ""

    for result in response.results:
        transcript += result.alternatives[0].transcript
    print(transcript)
    return transcript

# incorporates functions above to analyze key words (wrapper function)
def find_key_words(video_file):
    audio_file = strip_video(video_file)
    text = transcribe(audio_file)

    document = language_v1.Document(content=text, type_=language_v1.Document.Type.PLAIN_TEXT)
    response = client.analyze_entities(document=document, encoding_type='UTF32')

    entity_list = []
    for entity in response.entities:
        for word in programming_words:
            if entity.name.lower() == word.lower():
                entity_list.append(entity.name)
    print(entity_list)
    return entity_list

# Use information to find programming words
# programming words are classified as beginner, intermediate, and advanced
# Also consider level if person asks question about the topic because that means they don't know what it is.

def place_categories(user, words):
    for word in words:
        category = Categories.objects.filter(tag=word, user=user)
        if category.count() == 0:
            c = Categories(tag=word, user=user)
            c.save()

@login_required
def add_category(request):
    form = VideoFileForm()
    if request.method == 'POST':
        form = VideoFileForm(request.POST, request.FILES)
        if form.is_valid():
            # save video file
            file = request.FILES['video']
            file_name = str(uuid.uuid4())
            file_location = os.path.join(settings.MEDIA_ROOT, 'videos')
            fs = FileSystemStorage(location=file_location)
            fs.save(file_name, file)

            # analyze video file
            key_words = find_key_words(os.path.join(file_location, file_name))

            # create categories
            # this will probably be more complicated in the future
            place_categories(request.user, key_words)

            messages.success(request, 'Your file has been uploaded and analyzed. You will see tags shortly.')
    return render(request, 'categories/add_category.html', {'form': form})

@login_required
def show_tags(request):
    tags = Categories.objects.filter(user=request.user)
    return render(request, 'categories/show_categories.html', {'tags': tags})