import tkinter as tk
import tkinter.ttk as ttk
from tkinter import filedialog
from PIL import Image, ImageTk, ImageFile
import subprocess
import os
import json

from constants import *
from logic import get_page_maps

ImageFile.LOAD_TRUNCATED_IMAGES = True

class ToolBar(tk.Frame):
    def __init__(self, master, vars, **kwargs):
        super().__init__(master, **kwargs)

        self.vars = vars
        self.search = ""

        left_frame = tk.Frame(self)
        left_frame.pack(side= tk.LEFT)

        center_frame = tk.Frame(self)
        center_frame.pack(side= tk.RIGHT)

        search_entry = tk.Entry(left_frame, textvariable=self.vars["search_word"])
        search_button = ttk.Button(left_frame, text="搜索", width=6)
        kind_selector = ttk.Combobox(left_frame, width=15, values=SUGGEST,
                                     textvariable=self.vars["kind_selector"])
        search_reset = ttk.Button(left_frame, text="重置", width=6)

        left_button = ttk.Button(center_frame, text="上一页")
        page_label = tk.Label(center_frame, text="当前页面: ")
        page_i_label = tk.Label(center_frame, textvariable=self.vars["page_index"])
        page_label2 = tk.Label(center_frame, text="总地图数量: ")
        nums_label = tk.Label(center_frame, textvariable=self.vars["nums_count"])
        right_button = ttk.Button(center_frame, text="下一页")

        search_entry.grid(row=0, column=0)
        search_button.grid(row=0, column=1, padx=5)
        kind_selector.grid(row=0, column=2)
        search_reset.grid(row=0, column=3, padx=5)

        left_button.grid(row=0, column=0)
        page_label.grid(row=0, column=1)
        page_i_label.grid(row=0, column=2, padx=10)
        page_label2.grid(row=0, column=3)
        nums_label.grid(row=0, column=4, padx=10)
        right_button.grid(row=0, column=5)

        self.widgets = {
            "left": left_button,
            "right": right_button,
            "search": search_button,
            "reset": search_reset,

            "return_search":  search_entry,
            "select_search": kind_selector
        }

    def bind_funcs(self, **funcs):
        for k in funcs:
            if k.startswith("return") and k in self.widgets:
                self.widgets[k].bind("<Return>", funcs[k])
            elif k.startswith("select") and k in self.widgets:
                self.widgets[k].bind("<<ComboboxSelected>>", funcs[k])
            elif k in self.widgets:
                self.widgets[k].config(command=funcs[k])

class MapBoard(tk.Frame):
    def __init__(self, master, map_path, size, cr):
        super().__init__(master)
        self.map_path = map_path
        self.size = size
        self.c = cr[0]
        self.r = cr[1]

        self.img_list = [None for _ in range(self.r * self.c)]
        self.label_list = [None for _ in range(self.r * self.c)]
        self.text_list = [None for _ in range(self.r * self.c)]
        self.page_info = get_page_maps(self.map_path, page=0, nums=self.r * self.c)

        self.vars = {}

    def set_vars_map(self, vars):
        self.vars = vars

    def refresh_with_setting(self, **kwargs):
        """
        :param kwargs: dict, keys:map_path, size, cr,
        """
        if "map_path" in kwargs:
            self.map_path = kwargs["map_path"]
        if "size" in kwargs:
            self.size = kwargs["size"]
        if "cr" in kwargs:
            self.c, self.r = kwargs["cr"]

        lst = self.grid_slaves()
        for l in lst:
            l.destroy()

        self.img_list = [None for _ in range(self.r * self.c)]
        self.label_list = [None for _ in range(self.r * self.c)]
        self.text_list = [None for _ in range(self.r * self.c)]
        self.page_info = get_page_maps(self.map_path, page=0, nums=self.r * self.c)
        self.show_page_by_index(0)

    def refresh_vars(self):
        if "page_index" in self.vars:
            pi_str = "%s / %s" % (self.page_info["page"] + 1, self.page_info["pages"])
            self.vars["page_index"].set(pi_str)

        if "nums_count" in self.vars:
            self.vars["nums_count"].set(self.page_info["total"])

        if "left_button" in self.vars:
            if self.page_info.get("prev"):
                self.vars["left_button"].config(state=tk.NORMAL)
            else:
                self.vars["left_button"].config(state=tk.DISABLED)

        if "right_button" in self.vars:
            if self.page_info.get("next"):
                self.vars["right_button"].config(state=tk.NORMAL)
            else:
                self.vars["right_button"].config(state=tk.DISABLED)

        if "show_info" in self.vars:
            if os.path.exists(self.map_path):
                self.vars["show_info"].set("")
            else:
                self.vars["show_info"].set("你的地图文件夹不存在或已丢失")

    def show_page_by_index(self, index):
        if os.path.exists(self.map_path):
            key = self.vars["search_val"]
            self.page_info = get_page_maps(self.map_path, page=index, nums=self.r * self.c, filter=key)
        else:
            self.page_info = {}
        for i in range(self.r * self.c):
            ri = i // self.c
            ci = i % self.c

            if i < len(self.page_info["maps"]):
                map_info = self.page_info["maps"][i]
                if "art_tga" in map_info:
                    self.img_list[i] = Image.open(map_info["art_tga"])
                elif "tga" in map_info:
                    self.img_list[i] = Image.open(map_info["tga"])
                else:
                    self.img_list[i] = Image.new("RGB", IMG_SIZE[self.size], (0, 0, 0))

                # print(map_info["dir"])
                # if map_info["dir"] == "D:/Documents/Red Alert 3/other\(as)kuzhanmaqinuo1.6":
                #     print("debug")
                self.img_list[i] = self.img_list[i].resize(IMG_SIZE[self.size])
                self.img_list[i] = ImageTk.PhotoImage(self.img_list[i])

                if self.label_list[i] is None:
                    self.label_list[i] = tk.Label(self, image=self.img_list[i])
                    self.label_list[i].grid(row=ri * 2, column=ci)
                    self.label_list[i].bind("<Double-Button-1>", lambda e, i=i: self.click_map(i))
                else:
                    self.label_list[i].config(image=self.img_list[i])

                if self.text_list[i] is None:
                    tr = LINES[self.size]
                    # self.text_list[i] = tk.Label(self, text=map_info["map"][:self.max_width], width=self.max_width)
                    self.text_list[i] = tk.Text(self, width=MAX_WIDTH[self.size], height=tr, background=TEXT_BG)
                    self.text_list[i].insert(tk.END, map_info["map"])
                    self.text_list[i].grid(row=ri * 2 + 1, column=ci)
                    self.text_list[i].configure(state='disabled')
                else:
                    self.text_list[i].configure(state='normal')
                    self.text_list[i].delete("1.0", "end")
                    self.text_list[i].insert(tk.END, map_info["map"])
                    self.text_list[i].configure(state='disabled')
            else:
                # 最后一页的空白位置
                self.img_list[i] = Image.new("RGB", IMG_SIZE[self.size], BLANK)
                self.img_list[i] = ImageTk.PhotoImage(self.img_list[i])

                if self.label_list[i] is None:
                    self.label_list[i] = tk.Label(self, image=self.img_list[i])
                    self.label_list[i].grid(row=ri * 2, column=ci)
                    self.label_list[i].bind("<Double-Button-1>", lambda e, i=i: self.click_map(i))
                else:
                    self.label_list[i].config(image=self.img_list[i])

                if self.text_list[i] is None:
                    tr = LINES[self.size]
                    self.text_list[i] = tk.Text(self, width=MAX_WIDTH[self.size], height=tr, background=TEXT_BG)
                    self.text_list[i].grid(row=ri * 2 + 1, column=ci)
                    self.text_list[i].configure(state='disabled')
                else:
                    self.text_list[i].configure(state='normal')
                    self.text_list[i].delete("1.0", "end")
                    self.text_list[i].configure(state='disabled')

        self.refresh_vars()

    def click_map(self, index):
        if "maps" in self.page_info and index <len(self.page_info["maps"]):
            map_info = self.page_info["maps"][index]
            dir_path = map_info["dir"]

            dir_path= dir_path.replace("/", "\\")
            subprocess.Popen(r'explorer /select,"%s"' % dir_path)

    def search(self, event=None):
        if self.vars["search_val"] != self.vars["search_word"].get():
            self.vars["search_val"] = self.vars["search_word"].get()
            self.show_page_by_index(0)

    def reset_search(self):
        self.vars["search_word"].set("")
        self.vars["search_val"] = ""

        self.show_page_by_index(0)

    def select_search(self, event=None):
        val = self.vars["kind_selector"].get()
        if val in SUGGEST and val != SUGGEST[0]:
            self.vars["search_val"] = val
            self.show_page_by_index(0)

    def prev_page(self):
        if self.page_info.get("prev"):
            prev_index = self.page_info["page"] - 1
            self.show_page_by_index(prev_index)

    def next_page(self):
        if self.page_info.get("next"):
            next_index = self.page_info["page"] + 1
            self.show_page_by_index(next_index)

class MainBoard(tk.Frame):
    def __init__(self, master, **kwargs):
        super().__init__(master, **kwargs)
        self.setting = {}
        self.vars = {}

    def set_setting(self, **setting):
        """
        :param setting: dict, keys: map_path, size, cr, font
        """
        self.setting.update(setting)

    def set_vars_main(self, vars):
        self.vars = vars

    def refresh(self):
        self.map_board.refresh_with_setting(
            map_path=self.setting["map_path"], size=self.setting["size"], cr=self.setting["cr"])

    def init(self):
        frame_top = ToolBar(self, self.vars)
        frame_top.pack(fill="x")

        self.map_board = MapBoard(self, self.setting["map_path"], self.setting["size"], self.setting["cr"])
        self.map_board.set_vars_map(self.vars)
        self.map_board.show_page_by_index(0)
        self.map_board.pack()

        frame_bottom = tk.Frame(self)
        frame_bottom.pack()
        tk.Label(frame_bottom, text="双击地图图片，即可在文件浏览器中打开地图文件夹", font=self.setting["font_1"]).grid()
        tk.Label(frame_bottom, textvariable=self.vars["show_info"], font=self.setting["font_1"], fg="#FF69B4").grid()

        self.map_board.refresh_vars()

        frame_top.bind_funcs(left=self.map_board.prev_page, right=self.map_board.next_page,
                             search=self.map_board.search, reset=self.map_board.reset_search,
                             return_search=self.map_board.search, select_search=self.map_board.select_search)

        self.master.bind('<Left>', lambda e: self.map_board.prev_page())
        self.master.bind('<Right>', lambda e: self.map_board.next_page())

class SettingBoard(tk.Frame):
    def __init__(self, master):
        super().__init__(master)

        self.init_done = False

        self.local_vars = {}
        self.local_vars["r"] = tk.IntVar(self, 0)
        self.local_vars["c"] = tk.IntVar(self, 0)
        self.local_vars["size"] = tk.StringVar(self, "S")

    def read_setting(self, main_board):
        self.local_vars["mb"] = main_board
        self.local_vars["size"].set(main_board.setting["size"])
        self.local_vars["c"].set(main_board.setting["cr"][0])
        self.local_vars["r"].set(main_board.setting["cr"][1])

    def set_vars(self, vars):
        """
        :param vars: dict, keys: map_path(StringVar), setting_info(StringVar)
        """
        self.vars = vars

    def init(self):
        ROW1 = tk.Frame(self)
        ROW2 = tk.Frame(self)
        ROW3 = tk.Frame(self)
        ROW1.pack()
        ROW2.pack()
        ROW3.pack()
        label0 = ttk.Label(ROW1, textvariable=self.vars["show_info"], foreground="red")
        label0.grid(row=0, columnspan=2, pady=10)

        b0 = ttk.Button(ROW1, text="修改地图文件夹", command=self.select_dir)
        b0.grid(row=1, column=0)

        label_1 = ttk.Entry(ROW1, textvariable=self.vars["map_path"], state="disabled", width=80)
        label_1.grid(row=1, column=1, columnspan=2)

        ttk.Label(ROW2, text="行数：").grid(row=0, column=0)
        ttk.Label(ROW2, text="列数：").grid(row=1, column=0)

        tk.Scale(ROW2, from_=ROW_RANGE[0], to=ROW_RANGE[1], variable=self.local_vars["r"], orient='horizontal',
                 length=RANGE_WIDTH * (ROW_RANGE[1] - ROW_RANGE[0])).grid(row=0, column=1, columnspan=3)
        tk.Scale(ROW2, from_=COLUMN_RANGE[0], to=COLUMN_RANGE[1], variable=self.local_vars["c"], orient='horizontal',
                 length=RANGE_WIDTH * (COLUMN_RANGE[1] - COLUMN_RANGE[0])).grid(row=1, column=1, columnspan=3, padx=10)

        ttk.Label(ROW2, textvariable=self.local_vars["r"]).grid(row=0, column=4)
        ttk.Label(ROW2, textvariable=self.local_vars["c"]).grid(row=1, column=4)

        ttk.Label(ROW2, text="图片大小：").grid(row=2, column=0, padx=10, pady=10)
        tk.Radiobutton(ROW2, text="小", variable=self.local_vars["size"], value="S", width=7,
                       indicatoron=False).grid(row=2, column=1)
        tk.Radiobutton(ROW2, text="中", variable=self.local_vars["size"], value="M", width=7,
                       indicatoron=False).grid(row=2, column=2)
        tk.Radiobutton(ROW2, text="大", variable=self.local_vars["size"], value="L", width=7,
                       indicatoron=False).grid(row=2, column=3)

        tk.Button(self, text="保存并退出", command=self.save).pack(pady=10)
        tk.Button(self, text="返回", command=self.back).pack(pady=10)
        self.init_done = True

    def save(self):
        map_path = self.vars["map_path"].get()
        c = self.local_vars["c"].get()
        r = self.local_vars["r"].get()
        size = self.local_vars["size"].get()
        self.pack_forget()

        data = {
            "size": size,
            "c": c,
            "r": r
        }

        with open(JSON_NAME, "w") as f:
            json.dump(data, f)

        self.local_vars["mb"].set_setting(map_path=map_path, size=size, cr=(c, r))
        self.local_vars["mb"].refresh()

        self.local_vars["mb"].pack()

    def back(self):
        self.pack_forget()
        self.local_vars["mb"].pack()

    def select_dir(self):
        try:
            dir = filedialog.askdirectory()
            if dir:
                self.vars["map_path"].set(dir)
                print(dir)

            self.vars["show_info"].set("")
        except Exception as e:
            self.vars["show_info"].set(str(e))

def open_setting(gvars):
    gvars["mb"].pack_forget()
    gvars["sb"].read_setting(gvars["mb"])
    if not gvars["sb"].init_done:
        gvars["sb"].init()
    gvars["sb"].pack()


#!usr/bin/env python3
#Jacob Foppes Project 6 Game

#game like pokenmon where you go down differnent paths and can run into pokemon on the way.
# Uses random to randomly slect a pokemon from a list for you to fight
# you fight the poken nad it cna wither be cought or run away attacks are effective or ineffective
# differnt color texts for diffenrt poeple or actions
#   ie prof oak one color
#   you heard a rumble in the busehd another
# for random path lengths: print ... in differnt ammouns based on list of []"......","..",".","..................."]
#use turtle to visualize map
#use tkinter to pupup new window when a fight is happeneing.
import time
import random
import os
from pathlib import Path
import sys
'''Intro: Welocme to the game baisic tutorial.
    Choose your name, color '''

# GLOBAL VARIABLES
savedGames = [] #list of all saved games
auth_usr = ""
save = "" # users current location in the game  game
owd = os.getcwd()
wokeDex = {} #authenitcated users wokedex
currentLevel = 0

with open("accounts.txt","r+") as users:
    games = users.read()
    savedGames = games.split("\n")
print(savedGames)

wokemon = {"Wikachu":10,"Wetapod":5,"Wonix":10,"Wortle":5,"Wewtwo":20,"Wurtterfree":3,"Wattata":4}# dictionary of pokemon with health points
waterWokemon = {"Wyaradose":21, "Wlastoise":8, "Wampert": 10, "Wagicarp":4}
fireWokemon = {"Warmander":6,"Wapidash": 8,"Wagmar": 7, "Wimchar": 4, "Winetails":12}
earthWokemon = {"Whydor":3, "Wolem":5,"Wonix": 18,"Weodude":8, "Womastart": 3}
def print_slow(str):# Credit : Sebastian - Stack overflow https://stackoverflow.com/questions/4099422/printing-slowly-simulate-typing
    for letter in str:
        sys.stdout.write(letter)
        sys.stdout.flush()
        time.sleep(0.05)
def input_slow(str): # Credit: https://www.101computing.net/python-typing-text-effect/
  for character in str:
    sys.stdout.write(character)
    sys.stdout.flush()
    time.sleep(0.05)
  value = input()
  return value
def battle(randWokemon):# take
    rwok = (randWokemon[0],randWokemon[1]) # extract name and healktgh
    while True:
        wok = input_slow("\nChoose your Wokemon:\n"+str(wokeDex)+"\n")
        if wok in wokeDex:
            global cwokhealth
            global pwokhealth
            pwokhealth = int(wokeDex[wok])
            print_slow("\nYour HP: " + str(pwokhealth) + "\n") #print player helath
            cwokhealth = int(rwok[1])
            print_slow("Oponent HP: " + str(cwokhealth) + "\n")# print oponent helath
            print_slow("\nLooks like its " + wok + " vs " + rwok[0] + "\n")
            time.sleep(1)
            def attack():
                global cwokhealth
                global pwokhealth
                phit1 = random.randrange(0,5,1)#randomly genreated player dammage to compouter
                cwokhealth -= phit1 #subtract hit points form health
                print_slow(wok + " Strikes!\nIt does " + str(phit1) + " dammage!\n\n")
                chit1 = random.randrange(0,5,1)#randomly genereated compouter dammage to play
                pwokhealth -= chit1#subtract hit points form health
                print_slow(rwok[0] + " Strikes!\nIt does " + str(chit1) + " dammage!\n\n")
                print_slow("Oponent HP: " + str(cwokhealth) + "\n")
                print_slow("Your HP: " + str(pwokhealth) + "\n\n")
            attack()
        else:
            print("Please choose a Wokemon from your Wokedex")
            continue

        while pwokhealth > 0 and cwokhealth > 0: # while both players health greate than 0 comntinue the attack function
            attack()
        if pwokhealth <= 0 and  pwokhealth <= cwokhealth: #if comoputer wins
            print_slow("Dang..., Thats tough boss. \nLooks like you lost this one.\nTime to head home and heal your Wokemon\n ")
            loby()
        elif cwokhealth <= 0 and pwokhealth >= cwokhealth: # if player wins
            global currentLevel
            currentLevel += 1
            print_slow("You won!!\nYou now have " + rwok[0] + " added to your wokedex!!\nYou will now move on to "+ str(currentLevel)+"!\n\n")
            wokeDex[rwok[0]] = rwok[1]
            savel()
            loby()

def l1():#game level 1
    global wokemon
    print_slow("Welcome to level 1!\n")
    while True:
        path1 = input_slow("You are wlaking down the street and you encounter a set a of 2 trail heads:\n 'Elk Road', and 'Spoon Drive' which do you take \n Enter 'Elk' or 'Spoon'\n").lower()
        if path1 == "elk":
            print_slow("Your walking down the elk path when you spot something in the bushes...\n")
            time.sleep(.75)
            print_slow(".")
            time.sleep(.75)
            print_slow(".")
            time.sleep(.75)
            print_slow(".")
            randWokemon = random.choice(list(wokemon.items())) #chose random Wokemon from dict of wokemon
            rwok = randWokemon[0] # extract just the name
            while True:
                fight1 = input_slow("\nA wild " + rwok + " appears!!\n Do you battle? or Run away?\n Enter 'Battle', or 'Run\n").lower()
                if fight1 == "run":
                    print_slow("You got away just in time! Better head back.\n\n")
                    time.sleep(2)
                    break
                elif fight1 == "battle":
                    battle(randWokemon)
                else:
                    print_slow("Please choose run or battle\n")
                    continue

        elif path1 == "spoon":
            print_slow("\nYour walking down the Spoon Path and you come to a fork in the path\n The left looks like it leads to a river, and the right looks to be more forrest.\n ")
            while True:
                lefRig = input_slow("Do you Turn left or right\n").lower()
                if lefRig == "left":
                    print_slow("\nWhile Crossing the shallow end of the river, you see some splashing...")
                    time.sleep(.75)
                    print_slow(".")
                    time.sleep(.75)
                    print_slow(".")
                    randWokemon = random.choice(list(waterWokemon.items())) #chose random Wokemon from dict of wokemon
                    rwok = randWokemon[0] # extract just the name
                    fight2 = input_slow("\n\nA wild " + rwok + " appears!!\n Do you battle? or Run away?\n Enter 'Battle', or 'Run'\n").lower()
                    if fight2 == "battle":
                        battle(randWokemon)
                    elif fight2 == "run":
                        print_slow("You got away just in time, better head back to that fork in the path...\n\n")
                        continue
                    else:
                        print_slow("Choose battle, or Run.\n")
                elif lefRig == "right":
                    print_slow("Your take a right on Spoon Path when you spot something in the bushes...\n")
                    time.sleep(.75)
                    print_slow(".")
                    time.sleep(.75)
                    print_slow(".")
                    time.sleep(.75)
                    randWokemon = random.choice(list(wokemon.items())) #chose random Wokemon from dict of wokemon
                    rwok = randWokemon[0] # extract just the name
                    fight1 = input_slow("\nA wild " + rwok + " appears!!\n Do you battle? or Run away?\n Enter 'Battle', or n'Run\n").lower()
                    if fight1 == "run":
                        print_slow("You got away just in time! Better head back to that fork in the path.\n\n")
                        time.sleep(2)
                        continue
                    elif fight1 == "battle":
                        battle(randWokemon)
                    else:
                        print_slow("Choose battle, or Run.\n")
                else:
                    print_slow("Please choose left or right\n")
                    continue

def l2():#game level 2
    print_slow("Welcome to Level 2!\n\n")
    print_slow("You have entered a new area now...\n")
    time.sleep(.25)
    print_slow("You see new kinds of terrain ready to explore!\n")
    time.sleep(.25)
    while True:
        print_slow("To your left you see a massive volcano, and to your right you see a vast rocky desert.\n")
        time.sleep(.15)
        path3 = input_slow("Do you visit the desert or the volcano?\nsay 'Desert', or 'Volcano'\n").lower()
        if path3 == "desert":
            print_slow("You begin to wander the desert.\nThe Sun is beating down on you\n")
            time.sleep(.5)
            print_slow("You see something in the distance....\n")
            time.sleep(.5)
            print_slow("You walk closer......\n")
            time.sleep(.75)
            randWokemon = random.choice(list(earthWokemon.items())) #chose random Wokemon from dict of wokemon
            rwok = randWokemon[0] # extract just the name
            while True:
                fight1 = input_slow("\nA wild " + rwok + " appears!!\n Do you battle? or Run away?\n Enter 'Battle', or n'Run\n").lower()
                if fight1 == "run":
                    print_slow("You got away just in time! Better head back.\n\n")
                    time.sleep(2)
                    continue
                elif fight1 == "battle":
                    battle(randWokemon)
                else:
                    print_slow("Choose battle, or Run.\n")
        elif path3 == "volcano":
            print_slow("You start walking towards the volcano.\n")
            time.sleep(.75)
            print_slow("Suddenly a creature rushes twords you!")
            randWokemon = random.choice(list(fireWokemon.items())) #chose random Wokemon from dict of wokemon
            rwok = randWokemon[0] # extract just the name
            while True:
                fight1 = input_slow("\nA wild " + rwok + " appears!!\n Do you battle? or Run away?\n Enter 'Battle', or n'Run\n").lower()
                if fight1 == "run":
                    print_slow("You got away just in time! Better head back.\n\n")
                    time.sleep(2)
                    continue
                elif fight1 == "battle":
                    battle(randWokemon)
                else:
                    print_slow("Choose battle, or Run.\n")
        else:
            print_slow("Please choose desert, or volcano.\n")
            continue
def l3(): #Game level 3
    print("Level 3 Comming Soon!")
    time.sleep(3)
    pass
levels = {1:l1,2:l2,3:l3}

def loby():# lobby is where the player once logged in, can either view thier Wokedex, or continue playing at the start of thier current  level
    global wokeDex
    global currentLevel
    lvl = open("saveG.txt","r")
    currentLevel = int(lvl.read())
    while True:
        lchoice = input("Hello "+auth_usr+" Welcome to the lobby!\n Your Currently at Level: " + str(currentLevel) + "\nSay 'start' to resume your game, 'view' to view your wokedex, 'Prev' to redo a previos level, or 'Exit' to retun to the main screen."+"\n").lower()
        if lchoice == "start":
            level = open("saveG.txt","r").read()# save game file
            level = int(level)
            levels[level]()# read savegame file and call level finciton based on the text in the file. this text is used as a key in a dictionary of all levels where the values are the fucntions that start the levels
        elif lchoice == "prev":
            if currentLevel == 1:
                print_slow("\nYou have not completed any levels yet!. Come back here after you have progressed.\n")
                time.sleep(1)
                continue
            elif currentLevel == 2:
                try:
                    lev = int(input("You can Visit the Following Levels:\nLevel 1, Level 2\nType the number of the level you want to visit\n"))
                    if lev > currentLevel:
                        print("You can not access this yet.")
                    else:
                        levels[lev]()
                except ValueError:
                    print("Level does not exist.\n")
                    continue
            elif currentLevel == 3:
                try:
                    lev = int(input("You can Visit the Following Levels:\nLevel 1, Level 2, Level 3\nType the number of the level you want to visit\n"))
                    if lev > currentLevel:
                        print("You can not access this yet.\n\n")
                    else:
                        levels[lev]()
                except ValueError:
                    print("Level does not exist.\n")
                    continue

        elif lchoice == "view":
            print("\n"+str(wokeDex)+"\n")
            time.sleep(1)
        elif lchoice == "exit":#exit loby and return to welocme/ main directory
            os.chdir(owd)
            wokeDex = {}
            currentLevel = 0
            welcome()
        else:
            print("Select a valid choice\n")
            time.sleep(.5)

def mkuser(): # if the user does not have an account they can make one
    breaker = True
    while breaker ==True:
        print_slow("Prof. Woke: Wecome to WokeyWorld!")
        pname = input("What shall I call you?\n")
        if pname in savedGames or os.path.exists(pname+"/"):
            print("User already exists. Try again ")
            continue
        else:
            global auth_usr
            global wokeDex
            auth_usr = pname
            savedGames.append(pname) # ad new name to the saved games list
            auth = open("accounts.txt","r+")
            auth.write("\n".join(str(line) for line in savedGames))# write easch line of the saved gmaes list  to the accouts file
            auth.close()
            p = Path(pname)
            os.chdir("savedGames") # changes dir to the users folder so that a new game can be saved
            p.mkdir() # make play direcotry
            os.chdir(pname) # enter player direcotry
            global save
            global wokeDex
            sav = open("saveG.txt", "x") # create save file
            wd = open("wokedex.txt", "x") # create save file
            os.chdir(owd)
            print("Account creation sucessfull. Logged in as:", pname,"\n")
            breaker == False
            newGame()
            break
def saveg():## this fuction can be called to save the game durring play by typing save
    os.chdir("savedGames/" + auth_usr)
    dex = open("wokedex.txt","w")
    for key, value in wokeDex.items():
        dex.write('%s %s\n' % (key, value))
    lvl = open("saveG.txt","w")
    lvl.write(str(currentLevel))
def savel():## this fuction can be called to save the game durring play by typing save
    dex = open("wokedex.txt","w")
    for key, value in wokeDex.items():
        dex.write('%s %s\n' % (key, value))
    dex.close()
    lvl = open("saveG.txt","w")
    lvl.write(str(currentLevel))
    lvl.close()
    print("Game Saved. Your currnet level: "+str(currentLevel))

def newGame():# First Sequence in game after user amkes account
    time.sleep(.25)
    print_slow("\nProf Woke: Hello "+auth_usr+" My Name is Professor Woke! Ill show you arround\n")
    time.sleep(.1)
    print_slow("Prof Woke: Im giving you a wokedex.\n")
    time.sleep(.1)
    print_slow("Prof Woke: This is where you will store the wokemon you catch along the way.\n")
    time.sleep(.1)
    print_slow("Prof Woke: I going to start you off with this Wikachu.\n")
    wokeDex["Wikachu"] = 11
    print("\n",auth_usr,"'s Wokedex:",wokeDex,"\n")
    time.sleep(1)
    global currentLevel
    currentLevel = 1
    saveg()
    loby()
    pass

def welcome():# where user logs in to contine or creates new game
    while True:
        print("\n\nWokemon Gotta Snatch em' all!")
        game = input("To start a new game, say 'New', to continue a game, enter 'cont'\n").lower()
        if game == "new":
            mkuser()
        elif game == "cont":
            while True:
                print("Available saved Games:",savedGames,"\n")
                un = input("Enter your username or type 'Exit to return to main menue\n")
                if un == "exit":
                    welcome()
                    break
                elif un not in savedGames:
                    print("\n User not found. Try agian. OR Type Exit to return to the main screen \n")
                    time.sleep(1)
                elif un in savedGames: #checks username agains list of saved games
                    global auth_usr
                    auth_usr = un
                    global wokeDex
                    os.chdir("savedGames/")
                    os.chdir(auth_usr)
                    with open("wokedex.txt", "r+") as wd:
                        for line in wd:
                            (wok,pow) = line.split()# Create tuple of wokemon/powerlevels
                            wokeDex[(wok)] = pow #break the tuple in to doctiuonary key,value

                    print("\n Found Your Game!\n")

                    print(" Lets Get to it ", auth_usr,"\n")
                    loby()
                    break
        else: print("Please select a valid choice")
welcome()


# hcm/util/file_utils.py
"""File utilities. """
import os
import sys
import logging
import pandas as pd

logger = logging.getLogger(__name__)
pd.set_option('display.width', 1000)

def days_suffix(obs_period):
    return obs_period.replace('-', '_').replace(' ', '_')

def progress(count, total, status=''):
    bar_len = 60
    filled_len = int(round(bar_len * count / float(total)))
    percents = round(100.0 * count / float(total), 1)
    bar = '=' * filled_len + '-' * (bar_len - filled_len)
    sys.stdout.write('[{}] {}{} ...{}\r'.format(bar, percents, '%', status))
    sys.stdout.flush()  # As suggested by Rom Ruben (see: http://stackoverflow.com/questions/3173320/text-progress
    #  #  -bar-in-the-console/27871113#comment50529068_27871113)

def dataroot():
    return "/data/HCM/"

def datadir(exp_name):  # day=None):
    """ TODO: Move this to the data field. """
    daydir = ""
    other_exp = ["2cD1A2aCRE", "2cD1A2aCRE2", "2CFast", "1ASTRESS", "Stress_HCMe1r1", "CORTTREAT", "HiFat2", "HiFat1"]
    if exp_name in other_exp:
        datadir_parent = ""
        data_dir = exp_name
    # if day is not None: daydir = "D%d" % (day + 1)
    # elif exp_name.startswith("SS_Data_051905_FV"):
    #     datadir, expround = self.name.split(":")
    #     datadir_parent = "EventFiles/EventFiles_SSe1r%s" % expround
    #     date = (self.start + datetime.timedelta(days=day)).strftime("%m%d%Y")
    #     daydir = "%se1r%sd%s" % (date, expround, day + 1)
    elif exp_name == "StrainSurvey":
        datadir_parent = ""
        data_dir = "SS_Data_051905_FV"
    elif exp_name.startswith("WR"):
        datadir_parent = "WR"
        data_dir = exp_name
    else:
        raise ValueError("Unknown experiment: {}".format(exp_name))
    return os.path.join(dataroot(), "Experiments", datadir_parent, data_dir, daydir)

def hcm_dir():
    dir_parent, sub_dir = None, None
    if os.uname()[1] == "giorgios-MacBook-Pro.local":
        dir_parent = "/Users/go"
        sub_dir = "Projects/HomeCageMonitoring/"
    return os.path.join(dir_parent, sub_dir)

def repo_dir():
    return os.path.join(hcm_dir(), "hcm2")

def find_files(path, ext='npy'):
    """ returns a generator over filenames in path """
    return (os.path.join(dirpath, f) for dirpath, _, files in os.walk(path) for f in sorted(files) if
            f.endswith('.{}'.format(ext)))

def mouseday_label_from_filename(fname, ext="npy"):
    stripped = fname.strip('.{}'.format(ext)).split('/')
    exp_name, akind, qty, md_label = [stripped[x] for x in [6, 7, -2, -1]]
    one, group, two, mouse, day = md_label.split('_')
    return group, mouse, int(day[1:])

# # kinda old
# # # EVENTS
# def create_df_from_series_data(experiment, dfs, days, ignore=False):
#     df = pd.DataFrame(dfs).T
#     df = df.reset_index().rename(index=str, columns={'level_0': 'group', 'level_1': 'mouse', 'level_2': 'day'})
#     df = df[df['day'].isin(days)]
#     if ignore:
#         # ignored = list() or experiment.ignored
#         df = remove_ignored_from_dataframe(experiment, df)
#     df = set_df_indices(df, experiment, index=['group', 'mouse', 'day'])
#     return df
#

# def load_ingestion_events_dataframe(experiment, days=(), ev_type='F', ignore=False):
#     path_to_npy1 = path_to_binary(experiment, subdir=os.path.join('preprocessing', '{}_timeset'.format(ev_type)))
#     print "loading {} event data from npy:\n{}".format(ev_type, path_to_npy1)
#     tot = len(list(find_files(path_to_npy1, ext='npy')))
#     dfs1, dfs2 = dict(), dict()
#     # durations
#     for cnt, fname in enumerate(find_files(path_to_npy1, ext='npy')):
#         vals = np.load(fname)
#         if len(vals):
#             dur, = np.diff(vals).T
#             interdur = vals[1:, 0] - vals[:-1, 1]
#         else:
#             dur, interdur = list(), list()
#
#         index = mouseday_label_from_filename(fname)
#         dfs1[index] = pd.Series(dur)
#         dfs2[index] = pd.Series(interdur)
#         progress(cnt, tot)
#
#     df1 = create_df_from_series_data(experiment, dfs1, days, ignore)
#     df2 = create_df_from_series_data(experiment, dfs2, days, ignore)
#
#     # Feeding and Licking Coeff
#     coeff_name = 'FC' if ev_type == 'F' else "LC"
#     path_to_npy2 = path_to_binary(experiment, subdir=os.path.join('preprocessing', coeff_name))
#     print "loading {} data from npy:\n{}".format(coeff_name, path_to_npy2)
#     tot = len(list(find_files(path_to_npy2, ext='npy')))
#     dfs3 = dict()
#     for cnt, fname in enumerate(find_files(path_to_npy2, ext='npy')):
#         vals = np.load(fname).tolist()
#         index = mouseday_label_from_filename(fname)
#         dfs3[index] = pd.Series(vals)
#         progress(cnt, tot)
#
#     df3 = create_df_from_series_data(experiment, dfs3, days, ignore) * 1000  # to mg/s
#     return df1, df2, df3
#
#
# @utils.timing
# def load_locomotion_events_dataframe(experiment, days=(), ignore=False):
#     keys = ['delta_t', 'idx_timestamps_out_hb', 'idx_timestamps_at_hb', 'velocity', 'distance']
#     print "loading timestamp data from npy:\n{}".format(keys)
#     dfs_list = [dict() for _ in range(9)]
#     tot = len(list(experiment.mousedays))
#     for cnt, md in enumerate(experiment.mousedays):
#         if md.day in days:
#             delta_t, idx_out, idx_at, vel, dist = [load_preprocessing_data(md, keys=[key])['preprocessing'][key]
#                                                    for key in keys]
#             dfs_list[0][md.label] = pd.Series(delta_t)
#             dfs_list[1][md.label] = pd.Series(vel)
#             dfs_list[2][md.label] = pd.Series(dist)
#             dfs_list[3][md.label] = pd.Series(delta_t[idx_out])
#             dfs_list[4][md.label] = pd.Series(vel[idx_out])
#             dfs_list[5][md.label] = pd.Series(dist[idx_out])  # todo: REWRITE
#             dfs_list[6][md.label] = pd.Series(delta_t[idx_at])
#             dfs_list[7][md.label] = pd.Series(vel[idx_at])
#             dfs_list[8][md.label] = pd.Series(dist[idx_at])
#             progress(cnt, tot)
#             cnt += 1
#
#     return [create_df_from_series_data(experiment, dfs, days, ignore) for dfs in dfs_list]


"""Remove order in shop product and replace with the unwieldy row/column again :/

Revision ID: 4f786f3c132b
Revises: 2d4ea6b57d6e
Create Date: 2016-03-20 07:11:09.422825

"""

# revision identifiers, used by Alembic.
revision = '4f786f3c132b'
down_revision = '2d4ea6b57d6e'

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('Shop_product', sa.Column('column', sa.Integer(), nullable=True))
    op.add_column('Shop_product', sa.Column('row', sa.Integer(), nullable=True))
    op.drop_column('Shop_product', 'order')
    ### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.alter_column('permission', 'user_id',
               existing_type=mysql.INTEGER(display_width=11),
               nullable=False)
    op.add_column('Shop_product', sa.Column('order', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True))
    op.drop_column('Shop_product', 'row')
    op.drop_column('Shop_product', 'column')
    op.create_table('backup_Class_description',
    sa.Column('description_id', mysql.INTEGER(display_width=11), server_default=sa.text(u"'0'"), autoincrement=False, nullable=False),
    sa.Column('order', mysql.SMALLINT(display_width=5, unsigned=True), autoincrement=False, nullable=False),
    sa.Column('title', mysql.VARCHAR(length=255), nullable=False),
    sa.Column('abbr', mysql.VARCHAR(length=255), nullable=False),
    sa.Column('description', mysql.LONGTEXT(), nullable=False),
    sa.Column('menu', mysql.LONGTEXT(), nullable=False),
    sa.Column('knife_level', mysql.VARCHAR(length=1), nullable=False),
    sa.Column('veggie_level', mysql.VARCHAR(length=1), nullable=False),
    sa.Column('dairy_level', mysql.VARCHAR(length=1), nullable=False),
    sa.Column('wheat_level', mysql.VARCHAR(length=1), nullable=False),
    sa.Column('cost_override', mysql.SMALLINT(display_width=5, unsigned=True), autoincrement=False, nullable=True),
    sa.Column('wine', mysql.VARCHAR(length=255), nullable=False),
    mysql_default_charset=u'latin1',
    mysql_engine=u'InnoDB'
    )
    op.create_table('gift_certificate',
    sa.Column('certificate_id', mysql.INTEGER(display_width=11), nullable=False),
    sa.Column('campus_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
    sa.Column('created', mysql.DATETIME(), nullable=True),
    sa.Column('sender_name', mysql.VARCHAR(length=100), nullable=True),
    sa.Column('sender_email', mysql.VARCHAR(length=75), nullable=True),
    sa.Column('sender_phone', mysql.VARCHAR(length=20), nullable=True),
    sa.Column('amount_to_give', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
    sa.Column('recipient_name', mysql.VARCHAR(length=100), nullable=True),
    sa.Column('recipient_email', mysql.VARCHAR(length=75), nullable=True),
    sa.Column('message', mysql.TEXT(), nullable=True),
    sa.Column('giftcard', mysql.TINYINT(display_width=1), autoincrement=False, nullable=True),
    sa.Column('date_sent', sa.DATE(), nullable=True),
    sa.Column('name_on_envelope', mysql.VARCHAR(length=255), nullable=True),
    sa.Column('street_address', mysql.VARCHAR(length=255), nullable=True),
    sa.Column('city', mysql.VARCHAR(length=100), nullable=True),
    sa.Column('state', mysql.VARCHAR(length=2), nullable=True),
    sa.Column('zip_code', mysql.VARCHAR(length=10), nullable=True),
    sa.Column('code', mysql.VARCHAR(length=10), nullable=True),
    sa.Column('creditcard_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True),
    sa.Column('paid_with', mysql.VARCHAR(length=10), nullable=True),
    sa.Column('expiration_date', sa.DATE(), nullable=True),
    sa.ForeignKeyConstraint(['campus_id'], [u'Class_campus.campus_id'], name=u'gift_certificate_ibfk_1'),
    sa.PrimaryKeyConstraint('certificate_id'),
    mysql_default_charset=u'latin1',
    mysql_engine=u'InnoDB'
    )
    op.drop_table('forgot_password_links')
    ### end Alembic commands ###


from typing import Iterable
from minecraft_launch.modules.interface.iresource import IResource
from minecraft_launch.modules.models.launch.game_core import GameCore
from minecraft_launch.modules.utils.extend_util import ExtendUtil

class ResourceInstaller():
    max_download_threads: int = 64

    def __init__(self, game_core: GameCore) -> None:
        self.game_core: GameCore = game_core
        self.failed_resouces: list[IResource] = []

    def get_file_resources(self) -> Iterable[IResource]:
        if(self.game_core.client_file != None):
            yield self.game_core.client_file

    async def get_assets_resources_async(self):
        if(not(ExtendUtil.verify(self.game_core.assets_index_file.file_info, self.game_core.assets_index_file.size) or\
            ExtendUtil.verify(self.game_core.assets_index_file.file_info, self.game_core.assets_index_file.check_sum))):
            ...


# -*- coding: utf-8 -*-
'''
    :codeauthor: :email:`Rahul Handay <rahulha@saltstack.com>`
'''

# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import (
    MagicMock,
    patch,
    NO_MOCK,
    NO_MOCK_REASON
)

ensure_in_syspath('../../')

# Import Salt Libs
from salt.modules import oracle
import os

# Globals
oracle.__salt__ = {}
oracle.cx_Oracle = object()

@skipIf(NO_MOCK, NO_MOCK_REASON)
class OracleTestCase(TestCase):
    '''
    Test cases for salt.modules.oracle
    '''
    def test_run_query(self):
        '''
        Test for Run SQL query and return result
        '''
        with patch.object(oracle, '_connect', MagicMock()) as mock_connect:
            mock_connect.cursor.execute.fetchall.return_value = True
            with patch.object(oracle, 'show_dbs', MagicMock()):
                self.assertTrue(oracle.run_query('db', 'query'))

    def test_show_dbs(self):
        '''
        Test for Show databases configuration from pillar. Filter by `*args`
        '''
        with patch.dict(oracle.__salt__, {'pillar.get':
                                          MagicMock(return_value='a')}):
            self.assertDictEqual(oracle.show_dbs('A', 'B'),
                                 {'A': 'a', 'B': 'a'})

            self.assertEqual(oracle.show_dbs(), 'a')

    def test_version(self):
        '''
        Test for Server Version (select banner  from v$version)
        '''
        with patch.dict(oracle.__salt__, {'pillar.get':
                                          MagicMock(return_value='a')}):
            with patch.object(oracle, 'run_query', return_value='A'):
                self.assertDictEqual(oracle.version(), {})

    def test_client_version(self):
        '''
        Test for Oracle Client Version
        '''
        with patch.object(oracle, 'cx_Oracle',
                          MagicMock(side_effect=MagicMock())):
            self.assertEqual(oracle.client_version(), '')

    def test_show_pillar(self):
        '''
        Test for Show Pillar segment oracle.*
        '''
        with patch.dict(oracle.__salt__, {'pillar.get':
                                          MagicMock(return_value='a')}):
            self.assertEqual(oracle.show_pillar('item'), 'a')

    def test_show_env(self):
        '''
        Test for Show Environment used by Oracle Client
        '''
        with patch.object(os, 'environ',
                          return_value={'PATH': 'PATH',
                                        'ORACLE_HOME': 'ORACLE_HOME',
                                        'TNS_ADMIN': 'TNS_ADMIN',
                                        'NLS_LANG': 'NLS_LANG'}):
            self.assertDictEqual(oracle.show_env(), {})

if __name__ == '__main__':
    from integration import run_tests
    run_tests(OracleTestCase, needs_daemon=False)


import os
import sys
from dataclasses import dataclass

from src.logger import logging
from src.exception import CustomException
from src.utils import save_object, evaluate_model

from sklearn.ensemble import AdaBoostRegressor, GradientBoostingRegressor, RandomForestRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.neighbors import KNeighborsRegressor
from sklearn.tree import DecisionTreeRegressor
from xgboost import XGBRegressor

@dataclass
class ModelTrainerConfig:
    trained_model_file_path = os.path.join('artifacts','model.pkl')

class ModelTrainer:
    def __init__(self):
        self.model_trainer_config = ModelTrainerConfig()

    def initiate_model_trainer(self, train_arr, test_arr, preprocessor_path=None):
        try:

            # separate the target variable in the train and test arrays
            logging.info('Splitting training and testing input data')
            X_train, y_train, X_test, y_test = (
                train_arr[:,:-1],
                train_arr[:,-1],
                test_arr[:,:-1],
                test_arr[:,-1],
            )

            # a list of models to experiment with
            models = {
                "Random Forest": RandomForestRegressor(),
                "Decision Tree": DecisionTreeRegressor(),
                "Gradient Boosting": GradientBoostingRegressor(),
                "Linear Regression": LinearRegression(),
                "K-Neighbours Regressor": KNeighborsRegressor(),
                "XGBRegressor": XGBRegressor(),
                "AdaBoost Regressor": AdaBoostRegressor(),
            }

            model_report:dict = evaluate_model(x_train=X_train, y_train=y_train, x_test=X_test,
                                                y_test=y_test, models=models)

            ## to get the best model score from dict
            best_model_score = max(sorted(model_report.values()))

            ## to get best model froom dict
            best_model_name = list(model_report.keys())[
                list(model_report.values()).index(best_model_score)
            ]

            best_model = models[best_model_name]

            if best_model_score < 0.6:
                raise CustomException('No best Model Found!!!')
            logging.info('Found best model on both training and test data')

            save_object(file_path=self.model_trainer_config.trained_model_file_path,
                            obj=best_model)

            return (best_model, best_model_name, best_model_score)

        except Exception as e:
            raise CustomException(e, sys)


"""Functions for various pipeline use cases.

Author: Seth Axen
E-mail: seth.axen@gmail.com
"""
from .config.params import params_to_sections_dict
from .conformer.util import mol_from_smiles, mol_from_sdf, mol_to_sdf
from .conformer.generate import generate_conformers
from .fingerprint.generate import fprints_dict_from_mol

def params_to_dicts(params):
    """Get params dicts for pipeline functions from INI format params file."""
    sections_dict = params_to_sections_dict(params, auto=True)

    # preproc_params will eventually be returned separately, when there's a
    # pipeline function for protonation
    preproc_params = sections_dict.get("preprocessing", {})
    confgen_params = sections_dict.get("conformer_generation", {})
    confgen_params.update(preproc_params)
    fprint_params = sections_dict.get("fingerprinting", {})
    return confgen_params, fprint_params

def confs_from_smiles(smiles, name, confgen_params={}, save=False):
    """Generate conformations of molecule from SMILES string."""
    mol = mol_from_smiles(smiles, name)
    confgen_result = generate_conformers(
        mol, name, save=save, **confgen_params
    )
    mol = confgen_result[0]
    return mol

def sdf_from_smiles(
    smiles, name, confgen_params={}, out_file=None, out_ext=".sdf.bz2"
):
    """Generate conformations from SMILES string and save to SDF file."""
    mol = confs_from_smiles(
        smiles, name, confgen_params=confgen_params, save=False
    )
    if out_file is None:
        out_file = name + out_ext
    mol_to_sdf(mol, out_file)

def fprints_from_fprints_dict(fprints_dict, level=-1):
    """Get fingerprint at `level` from dict of level to fingerprint."""
    fprints_list = fprints_dict.get(
        level, fprints_dict[max(fprints_dict.keys())]
    )
    return fprints_list

def fprints_from_mol(mol, fprint_params={}, save=False):
    """Generate fingerprints for all `first` conformers in mol."""
    fprints_dict = fprints_dict_from_mol(mol, save=save, **fprint_params)
    level = fprint_params.get("level", -1)
    fprints_list = fprints_from_fprints_dict(fprints_dict, level=level)
    return fprints_list

def fprints_from_smiles(
    smiles, name, confgen_params={}, fprint_params={}, save=False
):
    """Generate conformers and fingerprints from a SMILES string."""
    if save is False and "first" not in confgen_params:
        confgen_params["first"] = fprint_params.get("first", -1)
    mol = confs_from_smiles(
        smiles, name, confgen_params=confgen_params, save=save
    )
    fprints_list = fprints_from_mol(
        mol, fprint_params=fprint_params, save=save
    )
    return fprints_list

def fprints_from_sdf(sdf_file, fprint_params={}, save=False):
    """Generate fingerprints from conformers in an SDF file."""
    mol = mol_from_sdf(sdf_file)
    fprints_list = fprints_from_mol(
        mol, fprint_params=fprint_params, save=save
    )
    return fprints_list


from __future__ import annotations

import typing
from typing import Any
from typing import cast
from typing import Dict
from typing import Generic
from typing import Iterator
from typing import List
from typing import Mapping
from typing import MutableMapping
from typing import Optional
from typing import overload
from typing import Tuple
from typing import Type
from typing import Union
import weakref

from .attr import _ClsLevelDispatch
from .attr import _EmptyListener
from .attr import _InstanceLevelDispatch
from .attr import _JoinedListener
from .registry import _ET
from .registry import _EventKey
from .. import util
from ..util.typing import Literal

_registrars: MutableMapping[
    str, List[Type[_HasEventsDispatch[Any]]]
] = util.defaultdict(list)

def _is_event_name(name: str) -> bool:
    # _sa_event prefix is special to support internal-only event names.
    # most event names are just plain method names that aren't
    # underscored.

    return (
        not name.startswith("_") and name != "dispatch"
    ) or name.startswith("_sa_event")

class _UnpickleDispatch:
    """Serializable callable that re-generates an instance of
    :class:`_Dispatch` given a particular :class:`.Events` subclass.

    """

    def __call__(self, _instance_cls: Type[_ET]) -> _Dispatch[_ET]:
        for cls in _instance_cls.__mro__:
            if "dispatch" in cls.__dict__:
                return cast(
                    "_Dispatch[_ET]", cls.__dict__["dispatch"].dispatch
                )._for_class(_instance_cls)
        else:
            raise AttributeError("No class with a 'dispatch' member present.")

class _DispatchCommon(Generic[_ET]):
    __slots__ = ()

    _instance_cls: Optional[Type[_ET]]

    def _join(self, other: _DispatchCommon[_ET]) -> _JoinedDispatcher[_ET]:
        raise NotImplementedError()

    def __getattr__(self, name: str) -> _InstanceLevelDispatch[_ET]:
        raise NotImplementedError()

    @property
    def _events(self) -> Type[_HasEventsDispatch[_ET]]:
        raise NotImplementedError()

class _Dispatch(_DispatchCommon[_ET]):
    """Mirror the event listening definitions of an Events class with
    listener collections.

    Classes which define a "dispatch" member will return a
    non-instantiated :class:`._Dispatch` subclass when the member
    is accessed at the class level.  When the "dispatch" member is
    accessed at the instance level of its owner, an instance
    of the :class:`._Dispatch` class is returned.

    A :class:`._Dispatch` class is generated for each :class:`.Events`
    class defined, by the :meth:`._HasEventsDispatch._create_dispatcher_class`
    method.  The original :class:`.Events` classes remain untouched.
    This decouples the construction of :class:`.Events` subclasses from
    the implementation used by the event internals, and allows
    inspecting tools like Sphinx to work in an unsurprising
    way against the public API.

    """

    # "active_history" is an ORM case we add here.   ideally a better
    # system would be in place for ad-hoc attributes.
    __slots__ = "_parent", "_instance_cls", "__dict__", "_empty_listeners"

    _active_history: bool

    _empty_listener_reg: MutableMapping[
        Type[_ET], Dict[str, _EmptyListener[_ET]]
    ] = weakref.WeakKeyDictionary()

    _empty_listeners: Dict[str, _EmptyListener[_ET]]

    _event_names: List[str]

    _instance_cls: Optional[Type[_ET]]

    _joined_dispatch_cls: Type[_JoinedDispatcher[_ET]]

    _events: Type[_HasEventsDispatch[_ET]]
    """reference back to the Events class.

    Bidirectional against _HasEventsDispatch.dispatch

    """

    def __init__(
        self,
        parent: Optional[_Dispatch[_ET]],
        instance_cls: Optional[Type[_ET]] = None,
    ):
        self._parent = parent
        self._instance_cls = instance_cls

        if instance_cls:
            assert parent is not None
            try:
                self._empty_listeners = self._empty_listener_reg[instance_cls]
            except KeyError:
                self._empty_listeners = self._empty_listener_reg[
                    instance_cls
                ] = {
                    ls.name: _EmptyListener(ls, instance_cls)
                    for ls in parent._event_descriptors
                }
        else:
            self._empty_listeners = {}

    def __getattr__(self, name: str) -> _InstanceLevelDispatch[_ET]:
        # Assign EmptyListeners as attributes on demand
        # to reduce startup time for new dispatch objects.
        try:
            ls = self._empty_listeners[name]
        except KeyError:
            raise AttributeError(name)
        else:
            setattr(self, ls.name, ls)
            return ls

    @property
    def _event_descriptors(self) -> Iterator[_ClsLevelDispatch[_ET]]:
        for k in self._event_names:
            # Yield _ClsLevelDispatch related
            # to relevant event name.
            yield getattr(self, k)

    def _listen(self, event_key: _EventKey[_ET], **kw: Any) -> None:
        return self._events._listen(event_key, **kw)

    def _for_class(self, instance_cls: Type[_ET]) -> _Dispatch[_ET]:
        return self.__class__(self, instance_cls)

    def _for_instance(self, instance: _ET) -> _Dispatch[_ET]:
        instance_cls = instance.__class__
        return self._for_class(instance_cls)

    def _join(self, other: _DispatchCommon[_ET]) -> _JoinedDispatcher[_ET]:
        """Create a 'join' of this :class:`._Dispatch` and another.

        This new dispatcher will dispatch events to both
        :class:`._Dispatch` objects.

        """
        if "_joined_dispatch_cls" not in self.__class__.__dict__:
            cls = type(
                "Joined%s" % self.__class__.__name__,
                (_JoinedDispatcher,),
                {"__slots__": self._event_names},
            )
            self.__class__._joined_dispatch_cls = cls
        return self._joined_dispatch_cls(self, other)

    def __reduce__(self) -> Union[str, Tuple[Any, ...]]:
        return _UnpickleDispatch(), (self._instance_cls,)

    def _update(
        self, other: _Dispatch[_ET], only_propagate: bool = True
    ) -> None:
        """Populate from the listeners in another :class:`_Dispatch`
        object."""
        for ls in other._event_descriptors:
            if isinstance(ls, _EmptyListener):
                continue
            getattr(self, ls.name).for_modify(self)._update(
                ls, only_propagate=only_propagate
            )

    def _clear(self) -> None:
        for ls in self._event_descriptors:
            ls.for_modify(self).clear()

def _remove_dispatcher(cls: Type[_HasEventsDispatch[_ET]]) -> None:
    for k in cls.dispatch._event_names:
        _registrars[k].remove(cls)
        if not _registrars[k]:
            del _registrars[k]

class _HasEventsDispatch(Generic[_ET]):
    _dispatch_target: Optional[Type[_ET]]
    """class which will receive the .dispatch collection"""

    dispatch: _Dispatch[_ET]
    """reference back to the _Dispatch class.

    Bidirectional against _Dispatch._events

    """

    if typing.TYPE_CHECKING:

        def __getattr__(self, name: str) -> _InstanceLevelDispatch[_ET]:
            ...

    def __init_subclass__(cls) -> None:
        """Intercept new Event subclasses and create associated _Dispatch
        classes."""

        cls._create_dispatcher_class(cls.__name__, cls.__bases__, cls.__dict__)

    @classmethod
    def _accept_with(
        cls, target: Union[_ET, Type[_ET]], identifier: str
    ) -> Optional[Union[_ET, Type[_ET]]]:
        raise NotImplementedError()

    @classmethod
    def _listen(
        cls,
        event_key: _EventKey[_ET],
        *,
        propagate: bool = False,
        insert: bool = False,
        named: bool = False,
        asyncio: bool = False,
    ) -> None:
        raise NotImplementedError()

    @staticmethod
    def _set_dispatch(
        klass: Type[_HasEventsDispatch[_ET]],
        dispatch_cls: Type[_Dispatch[_ET]],
    ) -> _Dispatch[_ET]:
        # This allows an Events subclass to define additional utility
        # methods made available to the target via
        # "self.dispatch._events.<utilitymethod>"
        # @staticmethod to allow easy "super" calls while in a metaclass
        # constructor.
        klass.dispatch = dispatch_cls(None)
        dispatch_cls._events = klass
        return klass.dispatch

    @classmethod
    def _create_dispatcher_class(
        cls, classname: str, bases: Tuple[type, ...], dict_: Mapping[str, Any]
    ) -> None:
        """Create a :class:`._Dispatch` class corresponding to an
        :class:`.Events` class."""

        # there's all kinds of ways to do this,
        # i.e. make a Dispatch class that shares the '_listen' method
        # of the Event class, this is the straight monkeypatch.
        if hasattr(cls, "dispatch"):
            dispatch_base = cls.dispatch.__class__
        else:
            dispatch_base = _Dispatch

        event_names = [k for k in dict_ if _is_event_name(k)]
        dispatch_cls = cast(
            "Type[_Dispatch[_ET]]",
            type(
                "%sDispatch" % classname,
                (dispatch_base,),
                {"__slots__": event_names},
            ),
        )

        dispatch_cls._event_names = event_names
        dispatch_inst = cls._set_dispatch(cls, dispatch_cls)
        for k in dispatch_cls._event_names:
            setattr(dispatch_inst, k, _ClsLevelDispatch(cls, dict_[k]))
            _registrars[k].append(cls)

        for super_ in dispatch_cls.__bases__:
            if issubclass(super_, _Dispatch) and super_ is not _Dispatch:
                for ls in super_._events.dispatch._event_descriptors:
                    setattr(dispatch_inst, ls.name, ls)
                    dispatch_cls._event_names.append(ls.name)

        if getattr(cls, "_dispatch_target", None):
            dispatch_target_cls = cls._dispatch_target
            assert dispatch_target_cls is not None
            if (
                hasattr(dispatch_target_cls, "__slots__")
                and "_slots_dispatch" in dispatch_target_cls.__slots__
            ):
                dispatch_target_cls.dispatch = slots_dispatcher(cls)
            else:
                dispatch_target_cls.dispatch = dispatcher(cls)

class Events(_HasEventsDispatch[_ET]):
    """Define event listening functions for a particular target type."""

    @classmethod
    def _accept_with(
        cls, target: Union[_ET, Type[_ET]], identifier: str
    ) -> Optional[Union[_ET, Type[_ET]]]:
        def dispatch_is(*types: Type[Any]) -> bool:
            return all(isinstance(target.dispatch, t) for t in types)

        def dispatch_parent_is(t: Type[Any]) -> bool:
            return isinstance(
                cast("_JoinedDispatcher[_ET]", target.dispatch).parent, t
            )

        # Mapper, ClassManager, Session override this to
        # also accept classes, scoped_sessions, sessionmakers, etc.
        if hasattr(target, "dispatch"):
            if (
                dispatch_is(cls.dispatch.__class__)
                or dispatch_is(type, cls.dispatch.__class__)
                or (
                    dispatch_is(_JoinedDispatcher)
                    and dispatch_parent_is(cls.dispatch.__class__)
                )
            ):
                return target

        return None

    @classmethod
    def _listen(
        cls,
        event_key: _EventKey[_ET],
        *,
        propagate: bool = False,
        insert: bool = False,
        named: bool = False,
        asyncio: bool = False,
    ) -> None:
        event_key.base_listen(
            propagate=propagate, insert=insert, named=named, asyncio=asyncio
        )

    @classmethod
    def _remove(cls, event_key: _EventKey[_ET]) -> None:
        event_key.remove()

    @classmethod
    def _clear(cls) -> None:
        cls.dispatch._clear()

class _JoinedDispatcher(_DispatchCommon[_ET]):
    """Represent a connection between two _Dispatch objects."""

    __slots__ = "local", "parent", "_instance_cls"

    local: _DispatchCommon[_ET]
    parent: _DispatchCommon[_ET]
    _instance_cls: Optional[Type[_ET]]

    def __init__(
        self, local: _DispatchCommon[_ET], parent: _DispatchCommon[_ET]
    ):
        self.local = local
        self.parent = parent
        self._instance_cls = self.local._instance_cls

    def __getattr__(self, name: str) -> _JoinedListener[_ET]:
        # Assign _JoinedListeners as attributes on demand
        # to reduce startup time for new dispatch objects.
        ls = getattr(self.local, name)
        jl = _JoinedListener(self.parent, ls.name, ls)
        setattr(self, ls.name, jl)
        return jl

    def _listen(self, event_key: _EventKey[_ET], **kw: Any) -> None:
        return self.parent._listen(event_key, **kw)

    @property
    def _events(self) -> Type[_HasEventsDispatch[_ET]]:
        return self.parent._events

class dispatcher(Generic[_ET]):
    """Descriptor used by target classes to
    deliver the _Dispatch class at the class level
    and produce new _Dispatch instances for target
    instances.

    """

    def __init__(self, events: Type[_HasEventsDispatch[_ET]]):
        self.dispatch = events.dispatch
        self.events = events

    @overload
    def __get__(
        self, obj: Literal[None], cls: Type[Any]
    ) -> Type[_Dispatch[_ET]]:
        ...

    @overload
    def __get__(self, obj: Any, cls: Type[Any]) -> _DispatchCommon[_ET]:
        ...

    def __get__(self, obj: Any, cls: Type[Any]) -> Any:
        if obj is None:
            return self.dispatch

        disp = self.dispatch._for_instance(obj)
        try:
            obj.__dict__["dispatch"] = disp
        except AttributeError as ae:
            raise TypeError(
                "target %r doesn't have __dict__, should it be "
                "defining _slots_dispatch?" % (obj,)
            ) from ae
        return disp

class slots_dispatcher(dispatcher[_ET]):
    def __get__(self, obj: Any, cls: Type[Any]) -> Any:
        if obj is None:
            return self.dispatch

        if hasattr(obj, "_slots_dispatch"):
            return obj._slots_dispatch

        disp = self.dispatch._for_instance(obj)
        obj._slots_dispatch = disp
        return disp


import matplotlib.pyplot as plt
import numpy as np
import datetime
from dragen.utilities.InputInfo import RveInfo
from dragen.utilities.Helpers import HelperFunctions

class Tesselation3D(HelperFunctions):

    def __init__(self, grains_df):
        super().__init__()

        self.grains_df = grains_df
        self.a = grains_df['a'].tolist()
        self.b = grains_df['b'].tolist()
        self.c = grains_df['c'].tolist()
        self.alpha = grains_df['alpha'].tolist()
        self.x_0 = grains_df['x_0'].tolist()
        self.y_0 = grains_df['y_0'].tolist()
        self.z_0 = grains_df['z_0'].tolist()
        self.final_volume = grains_df['final_discrete_volume'].tolist()
        self.n_grains = len(self.a)
        self.a_max = max(self.a)
        self.b_max = max(self.b)
        self.c_max = max(self.c)

        self.x_grid, self.y_grid, self.z_grid = super().gen_grid()

    def grow(self, iterator, a, b, c):

        alpha = self.alpha[iterator-1]
        x_0 = self.x_0[iterator-1]
        y_0 = self.y_0[iterator-1]
        z_0 = self.z_0[iterator-1]
        a_i = a[iterator - 1]
        b_i = b[iterator - 1]
        c_i = c[iterator - 1]
        a_i = a_i + a_i / self.a_max * RveInfo.bin_size
        b_i = b_i + b_i / self.b_max * RveInfo.bin_size
        c_i = c_i + c_i / self.c_max * RveInfo.bin_size
        a[iterator - 1] = a_i
        b[iterator - 1] = b_i
        c[iterator - 1] = c_i

        ellipsoid = super().ellipsoid(a_i, b_i, c_i, x_0, y_0, z_0, alpha=alpha)

        return ellipsoid, a, b, c

    def tesselation_plotter(self, array, epoch):
        t_0 = datetime.datetime.now()
        n_grains = self.n_grains
        rve_x, rve_y, rve_z = np.where((array >= 1) | (array == -200))

        grain_tuples = [*zip(rve_x, rve_y, rve_z)]

        grains_x = [self.x_grid[grain_tuples_i[0]][grain_tuples_i[1]][grain_tuples_i[2]]
                    for grain_tuples_i in grain_tuples]
        grains_y = [self.y_grid[grain_tuples_i[0]][grain_tuples_i[1]][grain_tuples_i[2]]
                    for grain_tuples_i in grain_tuples]
        grains_z = [self.z_grid[grain_tuples_i[0]][grain_tuples_i[1]][grain_tuples_i[2]]
                    for grain_tuples_i in grain_tuples]

        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')
        ax.scatter(grains_x, grains_y, grains_z, c=array[np.where((array >= 1) | (array == -200))], s=1, vmin=-20,
                   vmax=n_grains, cmap='seismic')

        rve_x, rve_y, rve_z = np.where((array == 0))

        free_space_tuples = [*zip(rve_x, rve_y, rve_z)]

        free_space_x = [self.x_grid[free_space_tuples_i[0]][free_space_tuples_i[1]][free_space_tuples_i[2]]
                    for free_space_tuples_i in free_space_tuples]
        free_space_y = [self.y_grid[free_space_tuples_i[0]][free_space_tuples_i[1]][free_space_tuples_i[2]]
                    for free_space_tuples_i in free_space_tuples]
        free_space_z = [self.z_grid[free_space_tuples_i[0]][free_space_tuples_i[1]][free_space_tuples_i[2]]
                    for free_space_tuples_i in free_space_tuples]
        ax.scatter(free_space_x, free_space_y, free_space_z, color='grey', alpha=0.01)

        ax.set_xlim(-5, RveInfo.box_size + 5)
        ax.set_ylim(-5, RveInfo.box_size + 5)
        ax.set_zlim(-5, RveInfo.box_size + 5)
        ax.set_xlabel('x (µm)')
        ax.set_ylabel('y (µm)')
        ax.set_zlabel('z (µm)')
        # ax.view_init(90, 270) #facing against z-direction (counterclockwise rotation)
        # plt.show()
        plt.savefig(RveInfo.store_path + '/Figs/3D_Tesselation_Epoch_{}.png'.format(epoch))
        plt.close(fig)
        time_elapse = datetime.datetime.now() - t_0
        if RveInfo.debug:
            RveInfo.LOGGER.info('time spent on plotter for epoch {}: {}'.format(epoch, time_elapse.total_seconds()))

    def run_tesselation(self, rsa, grain_df=None, band_idx_start=None):
        if RveInfo.gui_flag:
            RveInfo.infobox_obj.emit('starting Tesselation')
            RveInfo.progress_obj.emit(0)

        # set some variables
        status = False
        repeat = False
        packingratio = 0
        epoch = 0
        band_vol_0 = np.count_nonzero(rsa == -200)  # This volume is already affected by the First band ratio
        # So total Band ratio is band_ratio_rsa * band_ratio_tesselator

        # load some variables
        a = self.a
        b = self.b
        c = self.c
        n_grains = len(self.a)
        rve = rsa
        if band_idx_start is None:
            band_idx = []
        else:
            band_idx = [i for i in range(band_idx_start, n_grains+1)]
            #print(band_idx)

        # define boundaries and empty rve array
        empty_rve = super().gen_array()
        empty_rve = super().gen_boundaries_3D(empty_rve)
        rve_boundaries = empty_rve.copy()  # empty rve grid with defined boundaries
        vol_0 = np.count_nonzero(empty_rve == 0)

        freepoints = np.count_nonzero(rve == 0)
        grain_idx = [i for i in range(1, n_grains + 1)]
        grain_idx_backup = grain_idx.copy()
        while freepoints > 0:
            freepoints_old = freepoints   # Zum Abgleich
            i = 0
            np.random.shuffle(grain_idx)
            while i < len(grain_idx):
                idx = grain_idx[i]
                ellipsoid, a, b, c = self.grow(idx, a, b, c)
                grain = rve_boundaries.copy()
                grain[(ellipsoid <= 1) & (grain == 0)] = idx
                periodic_grain = super().make_periodic_3D(grain, ellipsoid, iterator=idx)
                band_vol = np.count_nonzero(rve == -200)

                if band_vol_0 > 0:
                    band_ratio = band_vol / band_vol_0
                    if band_ratio > RveInfo.band_ratio_final:
                        rve[((periodic_grain == idx) & (rve == 0)) | ((periodic_grain == idx) & (rve == -200))] = idx
                    else:
                        rve[((periodic_grain == idx) & (rve == 0))] = idx
                else:
                    rve[((periodic_grain == idx) & (rve == 0))] = idx

                freepoints = np.count_nonzero(rve == 0)
                grain_vol = np.count_nonzero(rve == idx) * RveInfo.bin_size ** 3
                if freepoints == 0:
                    break

                '''
                Grow control:
                1.) If a grain reaches Maximum Volume, the index gets deleted
                2.) If a grain is not growing in reality (difference between freepoints and freepoints_old), the
                grain is deleted. This avoids background growing and dumb results
                Counting (i = i + 1) up only if no deletion happens
                '''
                # TODO: Aus irgendeinem Grund funktioniert das immer noch nicht mit den Bandpunkten.
                # Als Workaround werden alle Bandpunkte nach 8 Epochen gelöscht, damit funktioniert es
                delta_grow = freepoints_old - freepoints
                if (idx in band_idx) and (epoch == 8):
                    #print('Del because of epoch')
                    grain_idx.remove(idx)
                    grain_idx_backup.remove(idx)
                elif (grain_vol > self.final_volume[idx-1]) and not repeat:
                    grain_idx.remove(idx)
                    if idx in band_idx:
                        #print('Del from Backup')
                        grain_idx_backup.remove(idx)
                elif delta_grow == 0: # and not repeat:    # and not repeat beobachten
                    grain_idx.remove(idx)
                    #grain_idx_backup.remove(idx)
                else:
                    i += 1

            if not grain_idx:
                repeat = True
                if RveInfo.gui_flag:
                    RveInfo.infobox_obj.emit('grain growth had to be reset at {}% of volume filling'.format(packingratio))
                if packingratio < 90:
                    if RveInfo.gui_flag:
                        RveInfo.infobox_obj.emit('your microstructure data does not contain \n'
                                              'enough data to fill this boxsize\n'
                                              'please decrease the boxsize for reasonable results')
                grain_idx = grain_idx_backup.copy()
            if RveInfo.anim_flag:
                self.tesselation_plotter(rve, epoch)
            epoch += 1
            packingratio = (1 - freepoints / vol_0) * 100
            # print('packingratio:', packingratio, '%')
            if RveInfo.gui_flag:
                RveInfo.progress_obj.emit(packingratio)
            else:
                if RveInfo.gui_flag:
                    RveInfo.progress_obj.emit('packingratio: ', packingratio)
                else:
                    RveInfo.LOGGER.info('packingratio: {}'.format(packingratio))

        if packingratio == 100:
            status = True

        # Save for further usage
        np.save(RveInfo.store_path + '/' + 'RVE_Numpy.npy', rve)
        return rve, status

if __name__ == '__main__':
    a = [10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10]
    b = [5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5]
    c = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
    # Define Box-dimension
    box_size = 100
    # Define resolution of Grid
    n_pts = 100

    shrinkfactor = 0.01

    x0_path = './3D_x0_list.npy'
    y0_path = './3D_y0_list.npy'
    z0_path = './3D_z0_list.npy'
    x_0 = np.load(x0_path)
    y_0 = np.load(y0_path)
    z_0 = np.load(z0_path)

    tesselation_obj = Tesselation3D(box_size, n_pts, a, b, c, x_0, y_0, z_0, shrinkfactor)
    tesselation_obj.run_tesselation()


# --------------------------------------------------------------------
# async.py
#
# Author: Lain Musgrove (lain.proliant@gmail.com)
# Date: Thursday February 16, 2023
#
# Distributed under terms of the MIT license.
# --------------------------------------------------------------------

import asyncio
import base64
import inspect
from dataclasses import dataclass, field
from datetime import datetime
from io import StringIO
from pathlib import Path
from ssl import SSLContext
from typing import Optional

from bivalve.datatypes import ArgV, ArgVQueue, AtomicValue, ThreadAtomicCounter
from bivalve.logging import LogManager

# --------------------------------------------------------------------
log = LogManager().get(__name__)

SERVER_AUTO_ID = ThreadAtomicCounter()
STREAM_AUTO_ID = ThreadAtomicCounter()

# --------------------------------------------------------------------
@dataclass
class SocketParams:
    """
    Encapsulates and validates the range of parameters available
    when connecting to or starting a server via sockets.
    """

    host: Optional[str] = None
    port: Optional[int] = None
    path: Optional[Path | str] = None
    ssl: Optional[SSLContext] = None

    def __post_init__(self):
        if not self._validate():
            raise ValueError("Invalid socket params.")

    def _validate(self) -> bool:
        if self.host and self.port:
            return True
        elif self.path:
            return True
        return False

    @property
    def is_tcp(self):
        return self.host is not None

    @property
    def is_unix_path(self):
        return self.path is not None

    def __str__(self):
        sb = StringIO()

        if self.host and self.port:
            sb.write(f"{self.host}:{self.port}")
        elif self.path:
            sb.write(f"file={self.path}")
        else:
            sb.write("INVALID")

        return sb.getvalue()

    def __repr__(self):
        sb = StringIO()
        sb.write(f"<{self.__class__.__qualname__} ")
        sb.write(str(self))
        sb.write(">")
        return sb.getvalue()

# --------------------------------------------------------------------
@dataclass
class Server:
    """
    Class encapsulating an asyncio.Server and the connection details
    that were used to establish it.
    """

    params: SocketParams
    asyncio_server: asyncio.Server
    id: int = field(default_factory=SERVER_AUTO_ID.next)

    @classmethod
    def _wrap_callback(self, params: SocketParams, callback):
        async def connected_callback(reader, writer):
            stream = Stream(reader, writer, params)
            if inspect.iscoroutinefunction(callback):
                await callback(stream)
            else:
                callback(stream)

        return connected_callback

    @classmethod
    async def serve(cls, callback, **kwargs) -> "Server":
        """
        Used to start a server on a TCP port or UNIX named socket path which
        will be fed Stream objects for connected clients via the provided
        `callback` function or coroutine.
        """

        params = SocketParams(**kwargs)
        callback = cls._wrap_callback(params, callback)
        if params.is_tcp:
            asyncio_server = await asyncio.start_server(
                callback, host=params.host, port=params.port, ssl=params.ssl
            )

        else:  # if params.is_unix_path
            asyncio_server = await asyncio.start_unix_server(
                callback, path=params.path, ssl=params.ssl
            )

        return Server(params, asyncio_server)

    def close(self):
        self.asyncio_server.close()

    def __repr__(self):
        sb = StringIO()
        sb.write(f"<{self.__class__.__qualname__} ")
        sb.write(f"id={self.id} ")
        sb.write(f"{self.params}")
        sb.write(">")
        return sb.getvalue()

# --------------------------------------------------------------------
@dataclass
class Stream:
    """
    Class encapsulating an asyncio StreamReader/StreamWriter pair
    for an open connection and the params used to establish it.
    """

    reader: asyncio.StreamReader
    writer: asyncio.StreamWriter
    params: SocketParams
    id: int = field(default_factory=STREAM_AUTO_ID.next)

    async def close(self):
        try:
            self.writer.close()
            await self.writer.wait_closed()
        except Exception:
            log.exception(f"Failed to close stream: {self}.")

    @classmethod
    async def connect(cls, **kwargs) -> "Stream":
        params = SocketParams(**kwargs)
        if params.is_tcp:
            reader, writer = await asyncio.open_connection(
                params.host, params.port, ssl=params.ssl
            )
        else:  # if params.is_unix_path
            reader, writer = await asyncio.open_unix_connection(
                params.path, ssl=params.ssl
            )

        return Stream(reader, writer, params)

    def __repr__(self):
        sb = StringIO()
        sb.write(f"<{self.__class__.__qualname__} ")
        sb.write(f"id={self.id} ")
        sb.write(f"{self.params}")
        sb.write(">")
        return sb.getvalue()

# --------------------------------------------------------------------
class Connection:
    """
    Abstract base type for connections which send and receive
    shell-style commands.
    """

    def __init__(self, id: int):
        self.id = id
        self.alive = AtomicValue(True)

    @classmethod
    async def connect(cls, **kwargs) -> "Connection":
        stream = Stream.connect(**kwargs)
        return StreamConnection(stream)

    @classmethod
    def bridge(
        cls, send_queue: ArgVQueue, recv_queue: ArgVQueue, poll_timeout=1.0
    ) -> "Connection":
        return BridgeConnection(send_queue, recv_queue, poll_timeout)

    async def close(self):
        if await self.alive():
            await self.alive.set(False)

    async def send(self, *argv):
        assert argv
        assert len(argv) > 0
        argv = [*argv]
        await self._send(*argv)

        log.debug(f"Sent {argv} to {self}")

    async def recv(self) -> ArgV:
        argv = await self._recv()
        assert argv
        log.debug(f"Received {argv} from {self}")
        return argv

    async def try_send(self, *argv):
        assert argv
        assert len(argv) > 0

        try:
            await self.send(*argv)

        except ConnectionError:
            log.warning(f"Could not send `{argv[0]}`, connection was lost.")

        except Exception as e:
            log.warning(f"Could not send `{argv[0]}`, unexpected error occurred.", e)

    def _encode_word(self, s: str) -> str:
        return base64.a85encode(s.encode("utf-8")).decode("utf-8")

    def _decode_word(self, word: str) -> str:
        return base64.a85decode(word.encode("utf-8")).decode("utf-8")

    def _join(self, argv: list[str]) -> str:
        return ' '.join([self._encode_word(str(s)) for s in argv])

    def _split(self, argv: str) -> ArgV:
        return [self._decode_word(s) for s in argv.split(' ')]

    async def _send(self, *argv):
        raise NotImplementedError()

    async def _recv(self) -> ArgV:
        raise NotImplementedError()

    async def __aenter__(self) -> "Connection":
        return self

    async def __aexit__(self, exc_t, exc_v, exc_tb):
        await self.try_send("bye")
        await self.close()

# --------------------------------------------------------------------
class StreamConnection(Connection):
    """
    Connection using a Stream to send and receive shell-style commands.

    Used to connect to a BivalveAgent over a socket connection.
    """

    def __init__(self, stream: Stream):
        super().__init__(stream.id)
        self.stream = stream
        self.syn_at = datetime.min
        self.ack_ttl: Optional[datetime] = None

    async def close(self):
        if await self.alive():
            await self.stream.close()
            await self.alive.set(False)

    async def _recv(self) -> ArgV:
        out = await self.stream.reader.readline()
        if not out or not await self.alive():
            raise ConnectionAbortedError()
        return self._split(out.decode())

    async def _send(self, *argv):
        self.stream.writer.write((self._join([str(s) for s in argv]) + "\n").encode())
        await self.stream.writer.drain()

    def __repr__(self):
        return repr(self.stream)
        sb = StringIO()
        sb.write(f"<{self.__class__.__qualname__} ")
        sb.write(f"{self.stream.params}")
        sb.write(">")
        return sb.getvalue()

# --------------------------------------------------------------------
class BridgeConnection(Connection):
    """
    Connection using queues to send and receive shell-style commands.

    Used to connect to a BivalveAgent in the same process, or to bridge a
    BivalveAgent connection across another medium.
    """

    def __init__(
        self,
        send_queue: ArgVQueue,
        recv_queue: ArgVQueue,
        poll_timeout: float = 1.0,
    ):
        super().__init__(STREAM_AUTO_ID.next())
        self.send_queue = send_queue
        self.recv_queue = recv_queue
        self.poll_timeout = poll_timeout

    async def _recv(self) -> ArgV:
        while await self.alive():
            result = await self.recv_queue.get()
            self.recv_queue.task_done()
            return result
        raise ConnectionAbortedError()

    async def _send(self, *argv):
        await self.send_queue.put(argv)


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

# In[1]:

import pandas as pd
import numpy as np
from keras.datasets import mnist
(X_train, _), (X_test, _) = mnist.load_data()

X_train = X_train.reshape(-1,784)
X_train = X_train/255

# In[2]:

from sklearn.neural_network import BernoulliRBM
rbm = BernoulliRBM(n_components=100, learning_rate=0.01, random_state=42, verbose=True)
rbm.fit(X_train)

# In[3]:

rbm.n_components

# In[4]:

rbm.components_.shape

# In[5]:

rbm.intercept_hidden_.shape

# In[6]:

rbm.intercept_visible_.shape

# In[7]:

X_train[:1].shape

# In[8]:

rbm.transform(X_train[:1])

# In[ ]:


import numpy as np
import rdkit.Chem as Chem
from rdkit import DataStructs
from rdkit.Chem import rdMolDescriptors
import copy

class CandidatePool():
    def __init__(self,
                 candidate_pool_size=50):
        self.candidate_pool_size = candidate_pool_size
        self.pool = []

    def _calculate_softmax(self, pool_life_time):
        exp_sum = 0
        for i in pool_life_time:
            exp_sum += np.exp(i)
        res = [i/exp_sum for i in pool_life_time]
        return res

    def get_length(self):
        return len(self.pool)

    def add_molecule(self, molecule):
        pool_length = len(self.pool)
        if pool_length < self.candidate_pool_size:
            self.pool.append(molecule)
        elif pool_length == self.candidate_pool_size:
            pool_life_time = [i.pool_life_time for i in self.pool if i.prior_flag == False]
            sorted_life_time = np.argsort(pool_life_time)
            index = sorted_life_time[0]
            self.pool.pop(index)
            molecule.pool_life_time = 0
            self.pool.append(molecule)

    def _smilarity_between_two_smiles(self, smi1, smi2):
        mol1, mol2 = Chem.MolFromSmiles(smi1), Chem.MolFromSmiles(smi2)

        vec1 = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol1, 4, nBits=512)
        vec2 = rdMolDescriptors.GetMorganFingerprintAsBitVect(mol2, 4, nBits=512)

        tani = DataStructs.TanimotoSimilarity(vec1, vec2)
        return tani

    def _calculate_similarity(self, molecule):
        similarity = []
        for i in self.pool:
            similarity.append(self._smilarity_between_two_smiles(molecule.smiles, i.smiles))
        return similarity

    def extract_molecules(self, molecule_list):
        res_mols = []
        length = len(molecule_list)
        if length > len(self.pool):
            res_mols = self.pool
            return res_mols
        else:
            for mol in molecule_list:
                similarity = self._calculate_similarity(mol)
                max_index = np.argmax(similarity)
                self.pool[max_index].pool_life_time = 0
                res_mols.append(self.pool[max_index])
                self.pool.pop(max_index)
            return res_mols

    def unload_molecules(self, num_molecules):
        pool = self.pool
        pool = sorted(pool, key=lambda x: x.pool_life_time, reverse=True)
        temp_mols = pool[:num_molecules]
        for i in range(num_molecules):
            pool.pop(0)
        return temp_mols

    def update_candidate_pool(self):
        for molecule in self.pool:
            molecule.pool_life_time += 1

from a_molecule import *

if __name__ == '__main__':
    candidate_pool = CandidatePool()

    smiles_pool = ['C']

    for i in range(10):
        smiles_pool.append(smiles_pool[-1]+'C')

    for i in range(10):
        candidate_pool.add_molecule(Molecule(smiles_pool[i]))

    standard_mol = Molecule(smiles_pool[-1])
    print(len(candidate_pool.pool))
    print(candidate_pool.extract_molecules([standard_mol])[0].smiles)


#pylint: disable=no-member
import tcod
from random import randint
from game_messages import Message

class Brute:
    def take_turn(self, target, fov_map, game_map, entities):
        results=[]
        monster=self.owner
        if tcod.map_is_in_fov(fov_map, monster.x, monster.y):
            if monster.distance_to(target)>=2:
                monster.move_astar(target, entities, game_map)
            elif target.combatant.health>0:
                results.extend(monster.combatant.attack_physical(target))
        return results

class ConfusedLad:
    def __init__(self, prev_ai, nof_turns=5):
        self.prev_ai=prev_ai
        self.nof_turns=nof_turns

    def take_turn(self, target, fov_map, game_map, entities):
        results=[]
        if self.nof_turns:
            dx=self.owner.x+randint(-1, 1)
            dy=self.owner.y+randint(-1, 1)
            if dx!=self.owner.x and dy!=self.owner.y:
                self.owner.move_towards(dx, dy, game_map, entities)
            self.nof_turns-=1
        else:
            self.owner.ai=self.prev_ai
            results.append({'message': Message('The {0} regains self-control.'.format(self.owner.name), tcod.red)})
        return results


from mmcv.runner import HOOKS, Hook
from mmcv.runner import EpochBasedRunner

from mpa.utils.logger import get_logger

logger = get_logger()

@HOOKS.register_module()
class CancelInterfaceHook(Hook):
    def __init__(self, init_callback: callable, interval=5):
        self.on_init_callback = init_callback
        self.runner = None
        self.interval = interval

    def cancel(self):
        logger.info('CancelInterfaceHook.cancel() is called.')
        if self.runner is None:
            logger.warning('runner is not configured yet. ignored this request.')
            return

        if self.runner.should_stop:
            logger.warning('cancel already requested.')
            return

        if isinstance(self.runner, EpochBasedRunner):
            epoch = self.runner.epoch
            self.runner._max_epochs = epoch  # Force runner to stop by pretending it has reached it's max_epoch
        self.runner.should_stop = True  # Set this flag to true to stop the current training epoch
        logger.info('requested stopping to the runner')

    def before_run(self, runner):
        self.runner = runner
        self.on_init_callback(self)


'''
 # @ Author: Andrew Hossack
 # @ Create Time: 2022-05-28 13:56:14
'''

import configparser
from typing import List, Union

def _get_config() -> List[str]:
    """
    Get the config file
    """
    config = configparser.ConfigParser()
    config.read('config.ini')
    return config

def get_config_value(key: str, section: str = "DEFAULT") -> Union[str, None]:
    """
    Get the value of a key in a section or None if not found
    """
    config = _get_config()
    try:
        value = config[section][key]
        if len(value) > 0:
            return value
        return None
    except KeyError:
        return None

def set_config_value(key: str, value: str, section: str = "DEFAULT") -> None:
    """
    Set the value of a key in a section
    """
    config = _get_config()
    config.set(section, key, value)
    with open('config.ini', 'w') as configfile:
        config.write(configfile)


from helper.help_functions import extract_list

def solve():
    lines = extract_list("inputs/input_04")
    points = 0
    points2 = 0
    for line in lines:
        try:
            pairs = line.split(",")
            code_a = pairs[0].split("-")
            code_b = pairs[1].split("-")
            if contained(int(code_a[0]), int(code_a[1]), int(code_b[0]), int(code_b[1])):
                points += 1

            if overlap(int(code_a[0]), int(code_a[1]), int(code_b[0]), int(code_b[1])):
                points2 += 1
        except:
            pass

    print(f"case 1: {points}")
    print(f"case 2: {points2}")

def contained(a_first: int, a_second: int, b_first: int, b_second: int) -> bool:
    result = False
    if (a_first <= b_first and a_second >= b_second) or (b_first <= a_first and b_second >= a_second):
        result = True

    return result

def overlap(a_first: int, a_second: int, b_first: int, b_second: int) -> bool:
    result = False
    if (a_first <= b_first <= a_second) or (b_first <= a_first <= b_second) or (b_first <= a_second <= b_second)\
            or (a_first <= b_second <= a_second):
        result = True

    return result

solve()


from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn import metrics
from sklearn.experimental import enable_halving_search_cv
from sklearn.model_selection import RepeatedStratifiedKFold, GridSearchCV, HalvingGridSearchCV
from functions import *
import numpy as np

#%%

with open('feature_matrix.pkl', 'rb') as f:
    feat_matrix = pickle.load(f)

rawTruth = read_truth_data()
truths = truthimport(rawTruth)
labels = truths['same']
results = []
#%%

#deal with Nan values, normalize data #split into train and test sets
cv = RepeatedStratifiedKFold(n_splits=5)
imputer = SimpleImputer(missing_values=np.nan, strategy='mean')
input_matrix = normalize(imputer.fit_transform(feat_matrix))
X_train, X_test, y_train, y_test = split_data(input_matrix, labels)

#%%
lg = LogisticRegression(C=0.01, penalty='l2', solver='newton-cg', max_iter=1000)
#lg = LogisticRegression()

lg.fit(X_train, y_train)
lg_pred = lg.predict(X_test)
lg_acc = metrics.accuracy_score(y_test, lg_pred)
lg_auc = metrics.roc_auc_score(y_test, lg_pred)
lg_rep = metrics.classification_report(y_test, lg_pred)

#%%
svm = SVC(C=5, gamma=0.01, kernel='rbf')
#svm = SVC()

svm.fit(X_train, y_train)
svm_pred = svm.predict(X_test)
svm_acc = metrics.accuracy_score(y_test, svm_pred)
svm_auc = metrics.roc_auc_score(y_test, svm_pred)
svm_rep = metrics.classification_report(y_test, svm_pred)

#%%

rf = RandomForestClassifier(max_depth=90, max_features=2, min_samples_leaf=3, min_samples_split=10, n_estimators=200)
#rf = RandomForestClassifier()

rf.fit(X_train, y_train)
rf_pred = rf.predict(X_test)
rf_acc = metrics.accuracy_score(y_test, rf_pred)
rf_auc = metrics.roc_auc_score(y_test, rf_pred)
rf_rep = metrics.classification_report(y_test, rf_pred)

#%%
mlp = MLPClassifier(activation='relu', alpha=0.0001, hidden_layer_sizes=(100,), learning_rate='constant', solver='adam', max_iter=1000)
#mlp = MLPClassifier(max_iter=1000)

mlp.fit(X_train, y_train)
mlp_pred = mlp.predict(X_test)
mlp_acc = metrics.accuracy_score(y_test, mlp_pred)
mlp_auc = metrics.roc_auc_score(y_test, mlp_pred)
mlp_rep = metrics.classification_report(y_test, mlp_pred)

#%%#Leave-one_out method to see which features contribute

# acc_scores = []
# for i in range(len(feat_matrix.T)):
#     print(i)
#     lg_i = LogisticRegression()
#     X_train_i = np.delete(X_train, obj = i, axis = 1 )
#     X_test_i = np.delete(X_test, obj = i, axis = 1 )
#     lg_i.fit(X_train_i, y_train)
#     lg_pred_i = lg_i.predict(X_test_i)
#     lg_acc_i = metrics.accuracy_score(y_test, lg_pred_i)
#     acc_scores.append(lg_acc_i)


"""hotjar_tracking

Revision ID: 5cdf7f1bbd6f
Revises: d788fb44fa0e
Create Date: 2022-04-01 11:02:46.044381

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '5cdf7f1bbd6f'
down_revision = 'd788fb44fa0e'
branch_labels = None
depends_on = None

def upgrade():
    op.create_table('hotjar_tracking',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('nr_id', sa.Integer(), nullable=True),
    sa.Column('hotjar_user', sa.VARCHAR(length=20), nullable=True),
    sa.Column('last_update_dt', sa.DateTime(timezone=True), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('hotjar_tracking')