import pickle
import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat
from seawater.eos80 import ptmp
from MyNumpyTools import minmax
from MySysUtils import preall
from MyMapFunctions import haversines
from MyFunctions import central_diff_gradient
from MyInterp import smooth1d_with_holes as smooth

def cast_number_to_filename(cast_number):
    data_loc = '/home/hugke729/PhD/Data/Shipboard/CTD/avg/'
    file_name_base = '1503'
    file_in = data_loc + file_name_base + str(cast_number).zfill(3) + 'avg.cnv'
    return file_in

def read_cnv(cast_number):
    """Take cnv file for specified cast and return only the outputs I want

    Output
    ------
    Pressure (db)
    Temperature (deg C)
    Salinity (PSU)
    sigma-t (kg/m^3)"""
    file_in = cast_number_to_filename(cast_number)

    # Data always starts on line 295
    p, T, S, sigma, lat, lon = np.genfromtxt(
        file_in, skip_header=294, unpack=True, usecols=[0, 1, 2, 13, 15, 16])

    return p, T, S, sigma

def get_xyt(cast_numbers):
    """Get position, location, and depth information from summary

    Returns lat, lon, utc_time (maybe not), and maximum pressure without
    having to read all the data in"""

    data_loc = '/home/hugke729/PhD/Data/Shipboard/CTD/processed/'
    xyt_file = data_loc + 'xyt_summary.csv'

    casts, lats, lons, utc_time, depths = np.genfromtxt(
        xyt_file, skip_header=1, unpack=True, delimiter=',')
    D = {cast: (lat, lon, depth)
         for cast, lat, lon, depth in zip(casts, lats, lons, depths)}
    # Return tuple or list of types depending on what cast_numbers is
    try:
        return D[cast_numbers]
    except TypeError:
        return [D[i] for i in cast_numbers]

def ctd_map():
    m = pickle.load(open('/home/hugke729/PhD/Maps/penny_strait.pickle', 'rb'))
    m.fillcontinents(color='grey')
    casts = np.r_[92:123]
    xyts = get_xyt(casts)
    for i, (lat, lon, _, _) in enumerate(xyts):
        plt.annotate(xy=m(lon, lat), s=str(casts[i] % 100).zfill(2),
                     ha='center', fontsize=9)

def p_to_z(p, sigma):
    """Make the minor change from pressure (dbar) to depth (m)"""
    dbar_to_pa = 10000
    z = np.cumsum(1/((sigma+1000)*9.81)*central_diff_gradient(p))*dbar_to_pa
    # ...avg.cnv files all start at p = 2 db
    # for reasons I can't think how to explain, need to add 1 to z
    # To do with constant of integration in z = int dp/ρg
    z = z + 1
    return z

def T_to_theta(S, T, p):
    """Make the minor change from in situ temperature to potential temp"""
    return ptmp(S, T, p)

def get_theta_S_sigma_z(cast_number):
    """Read .cnv file, and convert p to z and T to theta"""
    p, T, S, sigma = read_cnv(cast_number)
    z = p_to_z(p, sigma)
    theta = T_to_theta(S, T, p)
    return theta, S, sigma, z

def calc_N2(sigma, z):
    """Get buoyancy frequency squared from sigma"""
    N2 = (1000+sigma)/np.mean(sigma)*central_diff_gradient(sigma, z)
    N2 = smooth(N2, 6)
    return N2

def grid_section(cast_list):
    """Put all casts in cast_list into grids for each quantity"""
    lats, lons, pmaxs = preall(len(cast_list), copies=3)
    # Create vector of positions and convert to distance
    for i, (lat, lon, _, pmax) in enumerate(get_xyt(cast_list)):
        lats[i], lons[i], pmaxs[i] = lat, lon, pmax

    # Maximum depth among all casts
    pmax_all = np.ceil(max(pmaxs)).astype(int)

    # Create 2d depth array
    dist = haversines(lons, lats)[0]
    dist = np.outer(np.ones(pmax_all), dist)

    # g for grid
    pg, Tg, Sg, sigmag = preall((pmax_all, len(cast_list)),
                                copies=4, initial_value=np.nan)

    for i, cast_number in enumerate(cast_list):
        p, T, S, sigma = read_cnv(cast_number)
        x = len(p)
        pg[:x, i], Tg[:x, i], Sg[:x, i], sigmag[:x, i] = p, T, S, sigma

    return pg, Tg, Sg, sigmag, dist

def get_velocity_profile(cast_number):
    """Read in preprocessed (I believe) lowered-ADCP data"""
    fname = ('/home/hugke729/PhD/Data/Shipboard/CTD/LADCP/Traitement/stn' +
             str(cast_number).zfill(3) + '/processed/stn' +
             str(cast_number).zfill(3) + '.mat')
    D = loadmat(fname)
    # Not sure why the indexing or the key being dr actually means,
    # but these values correspond to the plots in the CTD folder
    u = D['dr']['u'][0][0][:, 0]
    v = D['dr']['v'][0][0][:, 0]
    z = D['dr']['z'][0][0][:, 0]
    return u, v, z

def plot_cross_section(cast_list):
    pg, Tg, Sg, sigmag, dist = grid_section(cast_list)
    fig, ax = plt.subplots()
    cax = plt.contourf(dist, pg, sigmag, levels=np.arange(24.5, 27, 0.25),
                       cmap='afmhot_r')
    cbar = plt.colorbar(cax)
    cbar.ax.invert_yaxis()
    ax.set(xlim=(-1, np.max(dist) + 1), ylim=minmax(pg)[::-1])
    for d in dist[0, :]:
        ax.plot((d,  d), (2, np.nanmax(pg)), color='grey', zorder=5)
        plt.draw()


"""
STATEMNET:
Given: array, int K
Want: find min length
of subarray
that has sum greater than or equal to K

OBSERVATIONS:
- when finding length things in sliding window
    - stretch until meet length requirement
    - when meet, shrink window
    - if shrink too much, add new
"""

import math

def smallest_subarray_given_sum(s, arr):
    """
    TIME: O(n+n)
    outer: n loops
    while loop: process each element once

    SPACE: O(1)

    """
    window_sum = 0
    min_length = math.inf
    window_start = 0

    for window_end in range(0, len(arr)):

        #add next element
        window_sum += arr[window_end]

        #shrink window to smallest
        #until window_sum smaller than s
        #(violate requirement)

        while window_sum >= s:
            curr_len = window_end - window_start +1
            min_length = min(min_length, curr_len)
            window_sum -= arr[window_start]
            window_start += 1

    if min_length == math.inf:
        return 0
    return min_length


import os
import json
from simple_websocket.ws import ConnectionClosed
from math import ceil
from random import randint
import random
import threading
from time import sleep
import traceback
from flask import Flask, render_template, request
from flask_sock import Sock
from threading import Timer, Thread
import logging

log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)
app = Flask(__name__)
sock = Sock(app)
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.jinja_env.auto_reload = True
app.config.from_object(__name__)
if os.path.isdir("/home/runner") == True:
    # on replit
    serverHost = 'ws${location.protocol.replace("http","").replace(":","")}://${location.hostname}/sock'  # ${location.hostname}
    startCode = os.environ["startKey"]
    loginKey = os.environ["loginKey"]
else:
    # not on replit
    serverHost = "ws://${location.hostname}/sock"  # ${location.hostname}
    startCode = "pls"
    loginKey = "pls"

cards = {
    "red": {
        2: [
            "2+", "aussetzen", "richtungswechsel", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "komunist"
        ]
    },
    "green": {
        2: [
            "2+", "aussetzen", "richtungswechsel", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "komunist"
        ]
    },
    "yellow": {
        2: [
            "2+", "aussetzen", "richtungswechsel", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "komunist"
        ]
    },
    "blue": {
        2: [
            "2+", "aussetzen", "richtungswechsel", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "komunist"
        ]
    },
    "": {
        4: ["farbwechsel4+", "farbwechsel"]
    }
}

deck = []
for color in cards.keys():
    for numbers in cards[color].keys():
        for i in range(numbers):
            for cardType in cards[color][numbers]:
                deck.append(color + "_" + cardType)
allDeck = []

for x in range(40):
    allDeck += deck

noCardLimit = True
startCardCount = 2

class gameMaster:

    def __init__(self):

        self.twoPlusAdder = 0

        self.scoreboard = {}

        self.events = {}

        self.specialAction = ""
        self.nextPlayerEvents = []

        self.status = "waiting_for_players"
        self.currentPlayer = 0
        self.direction = 1

        self.tables = []
        self.otherTables = []

        self.players = []
        self.playerNames = {}
        self.playerClasses = {}
        self.watchers = []
        self.wonPlayers = []

        self.playerDeck = {}
        self.availableCards = allDeck
        self.lyingCards = []

        self.cardCache = ""

    def sendTable(self, dat):
        for value in self.tables:
            value.send_message(json.dumps(dat))
        for value in self.otherTables:
            value.send_message(json.dumps(dat))

    def sendWatcher(self, dat):
        for x in self.watchers:
            x.send_message(json.dumps(dat))

    def setCards(self):
        self.availableCards = allDeck
        iteration = 0
        for playerId in self.players:
            self.playerDeck[playerId] = []
            for i in range(startCardCount):
                choice = random.randint(0, len(self.availableCards) - 1)
                self.playerDeck[playerId].append(self.availableCards[choice])
                self.availableCards.pop(choice)
                iteration += 1

            self.addEvent(
                playerId, {
                    "type": "stats",
                    "dat": {
                        "type": "deck",
                        "dat": self.playerDeck[playerId]
                    }
                })
        self.status = "playing"
        self.addEvents({"type": "status", "dat": "playing"})

    def start(self):
        self.setCards()
        choice = random.randint(0, len(self.availableCards) - 1)
        self.lyingCards = []
        self.addCard(self.availableCards[choice])
        self.availableCards.pop(choice)

        self.addEvents({"type": "action", "dat": "startGame"})

        self.easyEvents("cardRedo")
        self.askPlayer()

    def playerHas2x(self, playerId):
        for x in self.playerDeck[playerId]:
            if x.endswith("2+"):
                return True

    def askPlayer(self):
        playerId = self.players[self.currentPlayer]
        self.addEvents({
            "type": "stats",
            "dat": {
                "type": "currentPlayer",
                "dat": self.currentPlayer
            }
        })
        self.sendTable({"type": "currentPlayer", "dat": self.currentPlayer})
        self.sendWatcher({
            "type": "stats",
            "dat": {
                "type": "currentPlayer",
                "dat": self.currentPlayer
            }
        })
        # run next Player events
        if self.nextPlayerEvents != [] and self.specialAction == "":
            # doing 4+ and 2+
            for x in self.nextPlayerEvents:
                if x == "4+":
                    self.drawCard(playerId)
                    self.drawCard(playerId)
                    self.drawCard(playerId)
                    self.drawCard(playerId)
                    self.playerClasses[playerId].send_message(
                        json.dumps({
                            "type": "message",
                            "dat": "Du Hast 4 Karten gezogen"
                        }))
                if x == "2+":
                    self.drawCard(playerId)
                    self.drawCard(playerId)
                    self.playerClasses[playerId].send_message(
                        json.dumps({
                            "type": "message",
                            "dat": "Du Hast 2 Karten gezogen"
                        }))

            # doing Aussetzen
            np = 0
            for x in self.nextPlayerEvents:
                if x == "aussetzen":
                    np += 1
            self.nextPlayerEvents = []
            if np != 0:
                print("next player")
                self.nextPlayer(np)
                return

            # while self.nextPlayerEvents != []:
            #    if self.nextPlayerEvents[0] == "4+":
            #        self.drawCard(playerId)
            #        self.drawCard(playerId)
            #        self.drawCard(playerId)
            #        self.drawCard(playerId)
            #        self.playerClasses[playerId].send_message(
            #            json.dumps({
            #                "type": "message",
            #                "dat": "Du Hast 4 Karten gezogen"
            #            }))
            #    elif self.nextPlayerEvents[0] == "2+":
            #        self.twoPlusAdder += 2
            #        if self.playerHas2x(playerId):
            #            self.addEvent(playerId, {
            #                "type": "status",
            #                "dat": "2+_Desicion"
            #            })
            #            self.addEvent(
            #                playerId, {
            #                    "type": "action",
            #                    "dat": "slect2+Card",
            #                    "dat2": self.twoPlusAdder
            #                })
            #            self.nextPlayerEvents = self.nextPlayerEvents[1:]
            #            return
            #        else:
            #            for x in range(self.twoPlusAdder):
            #                self.drawCard(playerId)
            #            self.playerClasses[playerId].send_message(
            #                json.dumps({
            #                    "type":
            #                    "message",
            #                    "dat":
            #                    "Du Hast " + str(self.twoPlusAdder) +
            #                    " Karten gezogen"
            #                }))
            #            self.twoPlusAdder = 0
            #    elif self.nextPlayerEvents[0] == "aussetzen":
            #        print("aussetzen")
            #        self.nextPlayerEvents = self.nextPlayerEvents[1:]
            #        self.nextPlayer()
            #        return
            #    else:
            #        print("error: unknown playerEvent   : " +
            #              self.nextPlayerEvents[0])
            #    self.nextPlayerEvents = self.nextPlayerEvents[1:]
        # special cards appied to the current player (after the next player is allready selected so actually to the next player)
        if self.lyingCards[-1].endswith("4+"):
            if self.specialAction.startswith("winAussetzen"):
                self.nextPlayerEvents.append("4+")
            else:
                self.drawCard(playerId)
                self.drawCard(playerId)
                self.drawCard(playerId)
                self.drawCard(playerId)
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type": "message",
                        "dat": "Du Hast 4 Karten gezogen"
                    }))
        if self.lyingCards[-1].endswith("2+"):
            if self.specialAction.startswith("winAussetzen"):
                self.nextPlayerEvents.append("2+")
            else:
                self.twoPlusAdder += 2
                if self.playerHas2x(playerId):
                    self.addEvent(playerId, {
                        "type": "status",
                        "dat": "2+_Desicion"
                    })
                    self.addEvent(
                        playerId, {
                            "type": "action",
                            "dat": "slect2+Card",
                            "dat2": self.twoPlusAdder
                        })
                else:
                    for x in range(self.twoPlusAdder):
                        self.drawCard(playerId)
                    self.playerClasses[playerId].send_message(
                        json.dumps({
                            "type":
                            "message",
                            "dat":
                            "Du Hast " + str(self.twoPlusAdder) +
                            " Karten gezogen"
                        }))
                    self.twoPlusAdder = 0
                    self.addEvent(playerId, {
                        "type": "status",
                        "dat": "slectCard"
                    })
                    self.addEvent(playerId, {
                        "type": "action",
                        "dat": "slectCard"
                    })
        else:
            self.twoPlusAdder = 0
            self.addEvent(playerId, {"type": "status", "dat": "slectCard"})
            self.addEvent(playerId, {"type": "action", "dat": "slectCard"})

    def withdraw2x(self, playerId):
        for x in range(self.twoPlusAdder):
            self.drawCard(playerId)
        self.playerClasses[playerId].send_message(
            json.dumps({
                "type":
                "message",
                "dat":
                "Du Hast " + str(self.twoPlusAdder) + " Karten gezogen"
            }))
        self.twoPlusAdder = 0
        # if self.nextPlayerEvents == []:
        self.addEvent(playerId, {"type": "status", "dat": "slectCard"})
        self.addEvent(playerId, {"type": "action", "dat": "slectCard"})

    def playerWin(self, playerId):
        print(f"player {playerId} Finished!")

        name = "[" + str(playerId) + "]"
        if (playerId in self.playerNames):
            name = self.playerNames[playerId]
        self.wonPlayers.append(name)

        # message everyone that the player has won/finished
        if len(self.tables) > 0:
            self.sendTable({"type": "playerFinished", "dat": name})
        else:
            self.addEvents({
                "type": "action",
                "dat": {
                    "type": "playerFinished",
                    "dat": name
                }
            })
            self.sendWatcher({
                "type": "action",
                "dat": {
                    "type": "playerFinished",
                    "dat": name
                }
            })

        # tell player he won. Automatically reconnects thus automatically removing all informations of the player in the game
        self.playerClasses[playerId].send_message(
            json.dumps({
                "type": "action",
                "dat": {
                    "type": "won",
                    "dat": str(len(self.wonPlayers))
                }
            }))
        self.playerClasses[playerId].handle_close(True)

    def isThisCombo(self,
                    cards,
                    comboCards,
                    playerId,
                    specificColour=False,
                    topCardColor="green"):
        if len(comboCards) != len(cards):
            return False
        for x in range(len(cards)):
            if specificColour:
                if cards[x] not in comboCards:
                    return False
                comboCards.remove(cards[x])
            else:
                if cards[x].split("_")[1] not in comboCards:
                    return False
                # remove one crad that has the same type ignoring color
                i = 0
                while True:
                    if comboCards[i] == cards[x].split("_")[1]:
                        comboCards.remove(comboCards[i])
                        break
                    i += 1

        if not specificColour and cards[0].split(
                "_")[0] != self.lyingCards[-1].split("_")[0]:
            self.addEvent(playerId, {
                "type": "message",
                "dat": "du darfst diese Karte nicht legen!"
            })
            return False

        coulourCard = ""
        if specificColour:
            for x in cards:
                if x.split("_")[0] == topCardColor:
                    coulourCard = x
        for x in cards:
            if specificColour:
                if x != coulourCard:
                    self.easyEvents("cardAdd", x)
                    self.addCard(x)
                    self.playerDeck[playerId].remove(x)
            else:
                i = 0
                while True:
                    if self.playerDeck[playerId][i].split("_")[0] == x.split(
                            "_")[0]:
                        self.easyEvents("cardAdd",
                                        self.playerDeck[playerId][i])
                        self.addCard(self.playerDeck[playerId][i])
                        self.playerDeck[playerId].remove(
                            self.playerDeck[playerId][i])
                        break
                    i += 1
                pass
            sleep(0.1)
        if specificColour:
            self.easyEvents("cardAdd", coulourCard)
            self.addCard(coulourCard)
            self.playerDeck[playerId].remove(coulourCard)
        return True

    def layCard(self, card, playerId, topCardColor=None):
        if playerId != self.players[self.currentPlayer]:
            self.playerClasses[playerId].send_message(
                json.dumps({
                    "type": "message",
                    "dat": "Du bist zurzeit nicht am zug!"
                }))
            return
        if len(card) == 1:
            if (card[0] in self.playerDeck[playerId]):
                c = card[0].split("_")
                lc = self.lyingCards[-1].split("_")

                # if card is the right type
                ok = True
                if c[0] == "":
                    pass
                else:
                    if c[0] != lc[0] and c[1] != lc[1]:
                        ok = False

                # then
                if ok:
                    if c[1] == "richtungswechsel":
                        self.direction = self.direction * -1
                    if c[1] == "komunist":
                        self.playerDeck[playerId].remove(card[0])
                        self.addEvent(playerId, {
                            "type": "action",
                            "dat": "removeCard",
                            "dat2": card[0]
                        })

                        if len(self.playerDeck[playerId]) == 0:
                            self.playerWin(playerId)

                        self.addCard(card[0])
                        self.easyEvents("cardAdd", card[0])

                        self.messageAll("Karten werden neu gemischt.....")

                        threading.Thread(target=self.komunist,
                                         daemon=True).start()
                        return
                    if c[1] == "farbwechsel" or c[1] == "farbwechsel4+":
                        self.cardCache = card[0]
                        self.playerClasses[playerId].send_message(
                            json.dumps({
                                "type": "status",
                                "dat": "waiting_for_color"
                            }))
                        self.playerClasses[playerId].send_message(
                            json.dumps({
                                "type": "action",
                                "dat": "select_color"
                            }))
                    else:
                        self.addCard(card[0])
                        self.playerDeck[playerId].remove(card[0])
                        self.addEvent(playerId, {
                            "type": "action",
                            "dat": "removeCard",
                            "dat2": card[0]
                        })
                        self.easyEvents("cardAdd", card[0])

                        if len(self.playerDeck[playerId]) == 0:
                            self.playerWin(playerId)

                        if self.specialAction.startswith("winAussetzen"):
                            if card[0].endswith("_aussetzen"):
                                self.nextPlayerEvents.append("aussetzen")
                            self.nextPlayer()
                            return

                        if (card[0].endswith("_aussetzen")):
                            self.nextPlayer(2)

                        elif (card[0].endswith("_richtungswechsel")):
                            self.askPlayer()
                        else:
                            self.nextPlayer()

                else:
                    self.playerClasses[playerId].send_message(
                        json.dumps({
                            "type": "message",
                            "dat": "Du darfst diese Karte nicht legen!"
                        }))
            else:
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type": "message",
                        "dat": "Du hast diese Karte garnicht"
                    }))
                self.addEvent(
                    playerId, {
                        "type": "stats",
                        "dat": {
                            "type": "deck",
                            "dat": self.playerDeck[playerId]
                        }
                    })
        else:
            # check if has card
            for x in card:
                if x not in self.playerDeck[playerId]:
                    self.playerClasses[playerId].send_message(
                        json.dumps({
                            "type":
                            "message",
                            "dat":
                            "Fehler! Du besitzt mindestens eine dieser Karten nicht!"
                        }))
                    return

            if self.isThisCombo(card, [
                    "yellow_richtungswechsel", "blue_richtungswechsel",
                    "green_richtungswechsel", "red_richtungswechsel"
            ], playerId, True, topCardColor):
                if len(self.playerDeck[playerId]) == 0:
                    self.playerWin(playerId)
                    return
                self.playerClasses[
                    playerId].currentAction = "specificSelect_winRichtungswechsel"

                allPlayers = {}
                for x in self.players:
                    if x != playerId:
                        allPlayers[self.playerNames[x]] = x
                if len(allPlayers) == 0:
                    self.playerWin(playerId)
                    return
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type": "action",
                        "dat": {
                            "type": "specificSelect",
                            "dat": {
                                "title":
                                "Mit welchem spieler willst du tauschen?",
                                "options": allPlayers,
                                "type": "text"
                            }
                        }
                    }))
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type": "message",
                        "dat": "Karten-combo gelegt"
                    }))
            elif (not self.specialAction.startswith("winAussetzen")
                  ) and self.isThisCombo(card, [
                      "yellow_aussetzen", "blue_aussetzen", "green_aussetzen",
                      "red_aussetzen"
                  ], playerId, True, topCardColor):
                if len(self.playerDeck[playerId]) == 0:
                    self.playerWin(playerId)
                    return
                self.specialAction = "winAussetzen_4"
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type":
                        "message",
                        "dat":
                        "Karten-combo gelegt du kannst nun 4 mal Karten hintereinander legen"
                    }))
                self.nextPlayer()
            elif self.specialAction.startswith("winAussetzen"):
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type":
                        "message",
                        "dat":
                        "Windows-aussetzen-combo kann nicht addiert/kombiniert werden"
                    }))
            elif self.isThisCombo(card, ["9", "6"], playerId, False):
                if len(self.playerDeck[playerId]) == 0:
                    self.playerWin(playerId)
                    return
                self.playerClasses[
                    playerId].currentAction = "specificSelect_96"
                self.playerClasses[playerId].i = 4
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type": "action",
                        "dat": {
                            "type": "specificSelect",
                            "dat": {
                                "title":
                                f"Welche karte möchtest du haben (noch 4 versuch(e))",
                                "options": "_deck",
                                "type": "card",
                                "cancel": True
                            }
                        }
                    }))
            elif self.isThisCombo(card, ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], playerId, False):
                self.playerWin(playerId)
                return
            elif self.isThisCombo(card, [
                    "yellow_komunist", "blue_komunist", "green_komunist",
                    "red_komunist"
            ], playerId, True, topCardColor):
                if len(self.playerDeck[playerId]) == 0:
                    self.playerWin(playerId)
                    self.komunist()
                    return
                self.komunist(True, playerId)
            else:
                print(card)
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type": "message",
                        "dat": "Karten-combo zurzeit nicht verfügbar!"
                    }))

            master.addEvent(
                playerId, {
                    "type": "stats",
                    "dat": {
                        "type": "deck",
                        "dat": master.playerDeck[playerId]
                    }
                })

    def messageAll(self, msg):
        if len(self.tables) > 0:
            self.sendTable({"type": "message", "dat": msg})
        else:
            self.addEvents({"type": "message", "dat": msg})
            self.sendWatcher({"type": "message", "dat": msg})

    def komunist(self, windows=False, windowsPlayer=-1):
        toMix = []
        for key, value in self.playerDeck.items():
            self.playerDeck[key] = []
            toMix += value
            sleep(0.2)
            self.addEvent(key, {
                "type": "stats",
                "dat": {
                    "type": "deck",
                    "dat": []
                }
            })
            self.easyEvents("playerCardCount")

        for s in range(5):
            sleep(0.2)
            self.messageAll(f"mischen... {s+1}/5")

        cards = ceil(len(toMix) / len(self.players))

        if windows == False:
            for x in range(cards):
                for player in self.players:
                    if len(toMix) != 0:
                        choice = random.randint(0, len(toMix) - 1)
                        self.playerDeck[player].append(toMix[choice])
                        toMix.pop(choice)
                    else:
                        choice = random.randint(0,
                                                len(self.availableCards) - 1)
                        self.playerDeck[player].append(
                            self.availableCards[choice])
                        self.availableCards.pop(choice)
        else:
            toggle = True
            for x in range(cards):
                for player in self.players:
                    if windowsPlayer != player or toggle:
                        if len(toMix) != 0:
                            choice = random.randint(0, len(toMix) - 1)
                            self.playerDeck[player].append(toMix[choice])
                            toMix.pop(choice)
                        else:
                            choice = random.randint(
                                0,
                                len(self.availableCards) - 1)
                            self.playerDeck[player].append(
                                self.availableCards[choice])
                            self.availableCards.pop(choice)
                    toggle = not toggle
        # update players of their deck
        for player in self.players:
            self.addEvent(
                player, {
                    "type": "stats",
                    "dat": {
                        "type": "deck",
                        "dat": self.playerDeck[player]
                    }
                })
        self.easyEvents("playerCardCount")
        self.nextPlayer()

    def selectColor(self, playerId, color):
        if self.players[self.currentPlayer] == playerId:
            self.addCard(color + self.cardCache)
            self.playerDeck[playerId].remove(self.cardCache)
            self.addEvent(playerId, {
                "type": "action",
                "dat": "removeCard",
                "dat2": self.cardCache
            })
            self.easyEvents("cardAdd", color + self.cardCache)

            if len(self.playerDeck[playerId]) == 0:
                self.playerWin(playerId)

            self.cardCache = ""
            self.nextPlayer()
        else:
            self.playerClasses[playerId].send_message(
                json.dumps({
                    "type": "message",
                    "dat": "Du bist zurzeit nicht am zug!"
                }))

    def nextPlayerWithoutAsking(self):
        self.currentPlayer += self.direction
        if self.currentPlayer >= len(self.players):
            self.currentPlayer = 0
        if self.currentPlayer < 0:
            self.currentPlayer = len(self.players) - 1

    def nextPlayer(self, count=1):
        if len(self.players) <= 1:
            self.gameEnd()
            return
        self.addEvent(self.players[self.currentPlayer], {
            "type": "status",
            "dat": "playing"
        })
        if self.specialAction.startswith("winAussetzen"):
            if int(self.specialAction.split("_")[1]) <= 0:
                self.specialAction = ""
                self.nextPlayer()
                return
            self.specialAction = self.specialAction.split("_")[0] + "_" + str(
                int(self.specialAction.split("_")[1]) - 1)
        else:
            for x in range(count):
                self.nextPlayerWithoutAsking()
        self.askPlayer()

    def easyEvents(self, etype, dat=None):
        if etype == "playerlist":
            self.addEvents({
                "type": "stats",
                "dat": {
                    "type": "players",
                    "dat": {
                        "id": self.players,
                        "name": self.playerNames
                    }
                }
            })
            self.sendTable({
                "type": "players",
                "dat": {
                    "id": self.players,
                    "name": self.playerNames
                }
            })
            self.sendWatcher({
                "type": "stats",
                "dat": {
                    "type": "players",
                    "dat": {
                        "id": self.players,
                        "name": self.playerNames
                    }
                }
            })
        if etype == "cardList":
            for playerId in self.players:
                self.addEvent(
                    playerId, {
                        "type": "stats",
                        "dat": {
                            "type": "deck",
                            "dat": self.playerDeck[playerId]
                        }
                    })
        if etype == "disconnect":
            self.easyEvents("playerlist")
        if etype == "cardRedo":
            self.addEvents({
                "type": "stats",
                "dat": {
                    "type": "lyingCards",
                    "dat": self.lyingCards
                }
            })
            self.sendTable({"type": "lyingCards", "dat": self.lyingCards})
            self.sendWatcher({
                "type": "stats",
                "dat": {
                    "type": "lyingCards",
                    "dat": self.lyingCards
                }
            })

            counts = {}
            for x in self.playerDeck.keys():
                counts[x] = len(self.playerDeck[x])
            self.addEvents({
                "type": "stats",
                "dat": {
                    "type": "playerCardCount",
                    "dat": counts
                }
            })

            self.updateTable()
        if etype == "cardAdd":
            self.addEvents({
                "type": "action",
                "dat": {
                    "type": "lyingCards",
                    "dat": dat
                }
            })
            self.sendTable({"type": "lyingCardsAdd", "dat": dat})
            self.sendWatcher({
                "type": "action",
                "dat": {
                    "type": "lyingCards",
                    "dat": dat
                }
            })

            counts = {}
            for x in self.playerDeck.keys():
                counts[x] = len(self.playerDeck[x])
            self.addEvents({
                "type": "stats",
                "dat": {
                    "type": "playerCardCount",
                    "dat": counts
                }
            })
            self.updateTable()
        if etype == "playerCardCount":
            counts = {}
            for x in self.playerDeck.keys():
                counts[x] = len(self.playerDeck[x])
            self.addEvents({
                "type": "stats",
                "dat": {
                    "type": "playerCardCount",
                    "dat": counts
                }
            })
            self.sendTable({"type": "playerCardCount", "dat": counts})
            self.sendWatcher({
                "type": "stats",
                "dat": {
                    "type": "playerCardCount",
                    "dat": counts
                }
            })

    def updateTable(self):
        counts = {}
        for x in self.playerDeck.keys():
            counts[x] = len(self.playerDeck[x])
        self.sendTable({"type": "playerCardCount", "dat": counts})
        self.sendWatcher({
            "type": "stats",
            "dat": {
                "type": "playerCardCount",
                "dat": counts
            }
        })

    def addEvents(self, dat):
        for x in self.players:
            if not x in self.events:
                self.events[x] = []
            self.events[x].append(dat)

        self.runEvents()

    def drawCard(self, playerId):
        if playerId != self.players[self.currentPlayer]:
            self.playerClasses[playerId].send_message(
                json.dumps({
                    "type": "message",
                    "dat": "Du bist zurzeit nicht am zug!"
                }))
            return
        if len(self.playerDeck[playerId]) < 25 or noCardLimit:
            choice = random.randint(0, len(self.availableCards) - 1)
            self.playerDeck[playerId].append(self.availableCards[choice])
            self.addEvent(
                playerId, {
                    "type": "action",
                    "dat": {
                        "type": "addCard",
                        "dat": self.availableCards[choice]
                    }
                })
            self.availableCards.pop(choice)
            self.easyEvents("playerCardCount")
        else:
            self.playerClasses[playerId].send_message(
                json.dumps({
                    "type": "message",
                    "dat": "Du hast schon zu viele Karten"
                }))

            # check if player has any card to lay down
            has = False
            maybeCard = None
            for card in self.playerDeck[playerId]:
                c = card.split("_")
                lc = self.lyingCards[-1].split("_")
                ok = True
                if c[0] == "":
                    pass
                else:
                    if c[0] != lc[0] and c[1] != lc[1]:
                        ok = False
                if ok:
                    has = True
                    maybeCard = card
            if not has:
                self.playerClasses[playerId].send_message(
                    json.dumps({
                        "type":
                        "message",
                        "dat":
                        "Da du keine Karten legen kannst wurde der nächste Spieler aufgerufen."
                    }))
                self.nextPlayer()
            else:
                pass

    def addEvent(self, player, dat):
        if not player in self.events:
            self.events[player] = []
        self.events[player].append(dat)

        self.runEvents()

    def runEvents(self):
        for x in self.players:
            for i in self.events[x]:
                self.playerClasses[x].send_message(json.dumps(i))
            self.events[x] = []

    def addCard(self, dat):
        self.lyingCards.append(dat)

        # remove old cards
        if len(self.lyingCards) > 10:
            self.addEvents({"type": "action", "dat": "removeLastLyingCard"})
            self.sendWatcher({"type": "action", "dat": "removeLastLyingCard"})
            self.sendTable({"type": "removeLastLyingCard"})

            card = self.lyingCards[0]
            if card.split("_")[1] in ["farbwechsel", "farbwechsel4+"]:
                card = "_" + card.split("_")[1]
            self.availableCards.append(card)
            self.lyingCards = self.lyingCards[1:]

    def gameEnd(self):
        threading.Thread(target=self.threadedEnd, daemon=True).start()

    def threadedEnd(self):
        if self.status == "gameEnd":
            return
        self.status = "gameEnd"
        sleep(1)
        self.messageAll("Spiel vorbei. Nur noch ein Spieler übrig!")
        self.messageAll("Ein neues Spiel wird gestarted.")
        sleep(3)
        self.status = "waiting_for_players"

        for x in self.watchers:
            # x.send_message(json.dumps(
            #    {"type": "message", "dat": "Neues Spiel!"}))
            x.send_message(json.dumps({"type": "action", "dat": "reconnect"}))

        for y in self.players:
            x = self.playerClasses[y]
            # x.send_message(json.dumps(
            #    {"type": "message", "dat": "Neues Spiel!"}))
            x.send_message(json.dumps({"type": "action", "dat": "reconnect"}))

        # for x in self.tables:
        #    x.send_message(json.dumps(
        #        {"type": "message", "dat": "Neues Spiel!"}))
        # for x in self.otherTables:
        #    x.send_message(json.dumps(
        #        {"type": "message", "dat": "Neues Spiel!"}))

        self.wonPlayers = []

    def sendTableDat(self, table):
        #table.send_message(json.dumps({"type": "", "dat": ""}))
        table.send_message(
            json.dumps({
                "type": "players",
                "dat": {
                    "id": self.players,
                    "name": self.playerNames
                }
            }))
        table.send_message(
            json.dumps({
                "type": "lyingCards",
                "dat": self.lyingCards
            }))

        counts = {}
        for x in self.playerDeck.keys():
            counts[x] = len(self.playerDeck[x])
        table.send_message(
            json.dumps({
                "type": "playerCardCount",
                "dat": counts
            }))
        table.send_message(
            json.dumps({
                "type": "currentPlayer",
                "dat": self.currentPlayer
            }))

        # won players
        for x in self.wonPlayers:
            table.send_message(json.dumps({
                "type": "playerFinished",
                "dat": x
            }))

    def sendWatcherDat(self, watcher):
        # players
        watcher.send_message(
            json.dumps({
                "type": "stats",
                "dat": {
                    "type": "players",
                    "dat": {
                        "id": self.players,
                        "name": self.playerNames
                    }
                }
            }))

        # card counts
        counts = {}
        for x in self.playerDeck.keys():
            counts[x] = len(self.playerDeck[x])
        watcher.send_message(
            json.dumps({
                "type": "stats",
                "dat": {
                    "type": "playerCardCount",
                    "dat": counts
                }
            }))
        watcher.send_message(
            json.dumps({
                "type": "stats",
                "dat": {
                    "type": "lyingCards",
                    "dat": self.lyingCards
                }
            }))
        watcher.send_message(
            json.dumps({
                "type": "stats",
                "dat": {
                    "type": "currentPlayer",
                    "dat": self.currentPlayer
                }
            }))

        # won players
        for x in self.wonPlayers:
            watcher.send_message(
                json.dumps({
                    "type": "action",
                    "dat": {
                        "type": "playerFinished",
                        "dat": x
                    }
                }))

master = gameMaster()

class Player:

    def __init__(self, client, idet) -> None:
        self.type = ""
        self.currentAction = ""
        self.client = client
        self.idet = idet
        self.i = -1

    def send_message(self, dat):
        try:
            if self.idet in self.client.clients:
                if self.client.clients[self.idet].connected:
                    self.client.clients[self.idet].send(dat)
            else:
                self.client.close_connection(self.idet)
        except ConnectionClosed:
            self.client.close_connection(self.idet)

    def handle(self, data):
        try:
            global master
            data = json.loads(data)
            # print(self.data)

            if data["type"] == "start_game":
                if data["dat"] == startCode:
                    master.start()
                else:
                    self.send_message(
                        json.dumps({
                            "type": "message",
                            "dat": "Error: Wrong start code"
                        }))
            elif data["type"] == "allCards":
                self.send_message(
                    json.dumps({
                        "type": "stats",
                        "dat": {
                            "type": "allCards",
                            "dat": deck
                        }
                    }))

            elif self.currentAction.startswith(
                    "specificSelect_") and data["type"] == "selectResponse":
                if self.currentAction == "specificSelect_winRichtungswechsel":
                    if data["dat"] in master.players:
                        p = master.playerDeck[self.playerId]
                        master.playerDeck[self.playerId] = master.playerDeck[
                            data["dat"]]
                        master.playerDeck[data["dat"]] = p

                        master.easyEvents("playerCardCount")
                        master.addEvent(
                            data["dat"], {
                                "type": "stats",
                                "dat": {
                                    "type": "deck",
                                    "dat": master.playerDeck[data["dat"]]
                                }
                            })
                        master.addEvent(
                            self.playerId, {
                                "type": "stats",
                                "dat": {
                                    "type": "deck",
                                    "dat": master.playerDeck[self.playerId]
                                }
                            })

                        self.send_message(
                            json.dumps({
                                "type": "message",
                                "dat": "Karten getauscht"
                            }))
                        master.addEvent(
                            data["dat"], {
                                "type":
                                "message",
                                "dat":
                                master.playerNames[self.playerId] +
                                " hat deine Karten getauscht"
                            })
                        master.nextPlayer()
                    else:
                        self.send_message(
                            json.dumps({
                                "type": "message",
                                "dat": "Dieser Spieler existiert nicht!"
                            }))
                        return
                elif self.currentAction == "specificSelect_96":
                    if data["dat"] == "_cancel":
                        self.currentAction = ""
                        master.nextPlayer()
                        self.send_message(
                            json.dumps({
                                "type": "action",
                                "dat": "closeSpecificSelect"
                            }))
                        return

                    # get all users that have the specified card and select a random user
                    availableUsers = []
                    for x in master.players:
                        if x != self.playerId:
                            for y in master.playerDeck[x]:
                                if y == data["dat"]:
                                    if x not in availableUsers:
                                        availableUsers.append(x)
                    if len(availableUsers) == 0:
                        # no cards available! send message and return
                        self.send_message(
                            json.dumps({
                                "type":
                                "message",
                                "dat":
                                "Es gibt keinen Spieler mit dieser Karte!"
                            }))
                        self.send_message(
                            json.dumps({
                                "type": "action",
                                "dat": {
                                    "type": "specificSelect",
                                    "dat": {
                                        "title":
                                        f"Welche karte möchtest du haben, die vorherige gibt es nicht. (noch {self.i} versuch(e))",
                                        "options": "_deck",
                                        "type": "card",
                                        "cancel": True
                                    }
                                }
                            }))
                        return
                    else:
                        # select random user
                        selectedUser = availableUsers[randint(
                            0,
                            len(availableUsers) - 1)]

                        # remove card from player and add to self player
                        master.playerDeck[selectedUser].remove(data["dat"])
                        if len(master.playerDeck[selectedUser]) == 0:
                            master.playerWin(selectedUser)
                        master.addEvent(
                            selectedUser, {
                                "type": "action",
                                "dat": "removeCard",
                                "dat2": data["dat"]
                            })

                        master.playerDeck[self.playerId].append(data["dat"])
                        master.addEvent(
                            self.playerId, {
                                "type": "action",
                                "dat": {
                                    "type": "addCard",
                                    "dat": data["dat"]
                                }
                            })

                        # tell all players to update the player card count
                        master.easyEvents("playerCardCount")

                        self.i -= 1
                        if self.i <= 0:
                            self.currentAction = ""
                            self.send_message(
                                json.dumps({
                                    "type": "action",
                                    "dat": "closeSpecificSelect"
                                }))
                            master.nextPlayer()
                        else:
                            self.send_message(
                                json.dumps({
                                    "type": "action",
                                    "dat": {
                                        "type": "specificSelect",
                                        "dat": {
                                            "title":
                                            f"Welche karte möchtest du haben? (noch {self.i} versuch(e))",
                                            "options": "_deck",
                                            "type": "card",
                                            "cancel": True
                                        }
                                    }
                                }))
                        return

                self.send_message(
                    json.dumps({
                        "type": "action",
                        "dat": "closeSpecificSelect"
                    }))
            elif self.currentAction == "get_name" and data[
                    "type"] == "get_name":
                if (data["dat"] != ""
                        and data["dat"] not in master.playerNames.values()):
                    self.playerName = data["dat"]
                    master.playerNames[self.playerId] = self.playerName
                    self.currentAction = ""
                    master.easyEvents("playerlist")
                    # self.send_message(json.dumps(
                    #    {"type": "message", "dat": "ok :)"}))
                    self.send_message(
                        json.dumps({
                            "type": "status",
                            "dat": "waiting_for_beginning"
                        }))
                else:
                    self.send_message(
                        json.dumps({
                            "type": "action",
                            "dat": "get_name"
                        }))
            elif data["type"] == "lay_card":
                d2 = None
                if "dat2" in data.keys():
                    d2 = data["dat2"]
                master.layCard(data["dat"], self.playerId, d2)
            elif data["type"] == "drawCard":
                master.drawCard(self.playerId)
            elif data["type"] == "select_color":
                master.selectColor(self.playerId, data["dat"])
                pass
            elif data["type"] == "getCardsStat":
                self.send_message(
                    json.dumps({
                        "type": "stats",
                        "dat": {
                            "type": "cardsStat",
                            "dat": master.playerDeck[self.playerId]
                        }
                    }))
            elif data["type"] == "resend_cards_deck":
                master.addEvent(
                    self.playerId, {
                        "type": "stats",
                        "dat": {
                            "type": "deck",
                            "dat": master.playerDeck[self.playerId]
                        }
                    })
            elif data["type"] == "withdraw2x":
                master.withdraw2x(self.playerId)
            elif data["type"] == "typeStatus":
                if ("dat3" not in data
                        or data["dat3"] != loginKey) and loginKey != "":
                    self.send_message('{"type":"wrongPass"}')
                    sleep(0.5)
                    self.handle_close()
                    self.client.close_connection(self.idet)
                    return
                if data["dat"] == "player":
                    secondTime = False
                    if "dat2" in data:
                        if data["dat2"] == "secondTime":
                            secondTime = True

                    self.type = "player"
                    if len(master.tables) != 0:
                        self.send_message(
                            json.dumps({
                                "type": "action",
                                "dat": "hasTable"
                            }))

                    self.currentAction = ""
                    r = 0  # random.randint(0, 9999999999999)
                    while r in master.players:
                        r = r + 1  # random.randint(0, 9999999999999)
                    self.playerId = r

                    self.send_message(
                        json.dumps({
                            "type": "stats",
                            "dat": {
                                "type": "yourId",
                                "dat": self.playerId
                            }
                        }))

                    if master.status == "waiting_for_players":
                        master.players.append(self.playerId)
                        master.playerClasses[self.playerId] = self

                        if not secondTime:
                            self.send_message(
                                json.dumps({
                                    "type": "message",
                                    "dat": "Mit UNU-Supreme Verbunden!"
                                }))
                            self.send_message(
                                json.dumps({
                                    "type":
                                    "message",
                                    "dat":
                                    "Spieler anzahl zurzeit: " +
                                    str(len(master.players))
                                }))

                        # select name
                        self.playerName = "[" + str(self.playerId) + "]"
                        self.send_message(
                            json.dumps({
                                "type": "status",
                                "dat": "waiting_for_name"
                            }))
                        self.send_message(
                            json.dumps({
                                "type": "action",
                                "dat": "get_name"
                            }))
                        self.currentAction = "get_name"
                        master.easyEvents("playerlist")
                    else:
                        self.send_message(
                            json.dumps({
                                "type": "message",
                                "dat": "Du bist als zuschauer verbunden"
                            }))
                        self.send_message(
                            json.dumps({
                                "type": "status",
                                "dat": "watcher"
                            }))
                        master.watchers.append(self)
                        master.sendWatcherDat(self)
                elif data["dat"] == "watcher":
                    self.type = "watcher"
                    if len(master.tables) != 0:
                        self.send_message(
                            json.dumps({
                                "type": "action",
                                "dat": "hasTable"
                            }))
                    self.send_message(
                        json.dumps({
                            "type": "message",
                            "dat": "Du bist als zuschauer verbunden"
                        }))
                    self.send_message(
                        json.dumps({
                            "type": "status",
                            "dat": "watcher"
                        }))
                    master.watchers.append(self)
                    master.sendWatcherDat(self)
                elif data["dat"] == "table":
                    self.type = "table"
                    self.send_message(
                        json.dumps({
                            "type": "message",
                            "dat": "Als Tisch verbunden"
                        }))
                    master.sendTableDat(self)
                    if (data["dat2"] == "normal"):
                        master.tables.append(self)
                        master.addEvents({"type": "action", "dat": "hasTable"})
                        master.sendWatcher({
                            "type": "action",
                            "dat": "hasTable"
                        })
                    else:
                        master.otherTables.append(self)
                elif data["dat"] == "console":
                    self.type = "console"
                    self.send_message("connected!")
            elif self.type == "console" and data["type"] == "eval":
                self.send_message(consoleEval(data["dat"]))
            else:
                self.send_message(
                    json.dumps({
                        "type":
                        "message",
                        "dat":
                        "action " + data["type"] +
                        " could not be used! (wanted action: " +
                        self.currentAction + ")"
                    }))
        except:
            traceback.print_exc()

    def connected(self):
        print(self.idet, 'connected')

    def handle_close(self, notReal=False):
        try:
            global master
            if self.type == "player":
                if self.playerId in master.players:
                    master.players.remove(self.playerId)

                if self.playerId in master.playerClasses.keys():
                    master.playerClasses.pop(self.playerId)

                if self.playerId in master.playerNames.keys():
                    master.playerNames.pop(self.playerId)

                if self.playerId in master.playerDeck.keys():
                    for x in master.playerDeck[self.playerId]:
                        master.availableCards.append(x)
                    master.playerDeck.pop(self.playerId)

                # check if currentPlayer id needs to change, because the player has disconnected
                if master.status == "playing" and master.currentPlayer >= len(
                        master.players):
                    print("toBig currentPlayer")
                    master.currentPlayer = len(master.players) - 1
                    if master.currentPlayer < 0:
                        master.currentPlayer = 0
                        master.gameEnd()
                        print("no Players anymore!")
                    else:
                        master.askPlayer()
                if master.status == "playing" and len(master.players) <= 1:
                    master.gameEnd()
                    return
            elif self.type == "table":
                if self in master.tables:
                    master.tables.remove(self)
                if self in master.otherTables:
                    master.otherTables.remove(self)
            elif self.type == "watcher":
                master.watchers.remove(self)
            master.easyEvents("disconnect")
            if not notReal:
                #print(self.idet, 'closed')
                pass
        except:
            if not notReal:
                print(self.idet, 'closed')
            traceback.print_exc()

def genPlayerId(inp):
    global master
    inp = str(inp)

    if (inp.startswith("{")):
        inp = master.players[int(inp[1:-1])]

    return int(inp)

def consoleEval(dat):
    global master
    command = dat.split(" ")
    try:
        if command[0] == "players":
            returnDat = []
            for player in master.players:
                if player in master.playerNames.keys():
                    name = master.playerNames[player]
                else:
                    name = "undefined"
                returnDat.append(name + ": " + str(player))
                pass
            return ",\n".join(returnDat)
        elif command[0] == "give":
            playerId = genPlayerId(command[2])
            if playerId not in master.playerDeck.keys():
                return "player doesn't exist"
            master.playerDeck[playerId].append(command[1])
            master.addEvent(playerId, {
                "type": "action",
                "dat": {
                    "type": "addCard",
                    "dat": command[1]
                }
            })
            master.easyEvents("playerCardCount")
            return "ok!"
        elif command[0] == "get":
            playerId = genPlayerId(command[1])
            if playerId in master.playerDeck:
                cards = ", ".join(master.playerDeck[playerId])
            else:
                cards = "player does not exist!"
            return cards
        elif command[0] == "setCurrentPlayer":
            playerId = genPlayerId(command[1])
            master.currentPlayer = playerId
            return "set to " + str(master.currentPlayer)
        elif command[0] == "getCurrentPlayer":
            return "CurrentPlayer: " + str(master.currentPlayer)
        elif command[0] == "win":
            playerId = genPlayerId(command[1])
            master.easyEvents("playerCardCount")
            master.addEvent(playerId, {
                "type": "stats",
                "dat": {
                    "type": "deck",
                    "dat": []
                }
            })

            master.playerWin(playerId)
            master.nextPlayer()

            return f"player {playerId} has now finished!"
        elif command[0] == "start":
            master.start()
            return "started"
        elif command[0] == "nextPlayer":
            master.nextPlayer()
            return "ok"
    except Exception as e:
        return traceback.format_exc()
        # return "error: " + str(e)
    return "command did not return! Does it realy exist?"

@app.route('/')
def projects():
    return render_template("index.html",
                           title='UNU Supreme',
                           serverHost=serverHost)

@app.route('/many')
def many():
    return render_template("many.html",
                           title='UNU Supreme',
                           serverHost=serverHost)

@app.route('/wsT')
def wsT():
    return render_template("websocketTester.html",
                           title='UNU Supreme',
                           serverHost=serverHost)

@app.route('/console')
def console():
    # serverHost: ${location.hostname}
    return render_template("console.html",
                           title='Debug console',
                           serverHost=serverHost)

@app.route("/table")
def desk():
    # serverHost: ${location.hostname}
    return render_template("table.html",
                           title="Table Stuff",
                           serverHost=serverHost)

def flaskServer():
    app.run("0.0.0.0", 80)

def run():
    threading.Thread(target=flaskServer, daemon=True).start()

@app.after_request
def add_header(r):
    """
    Add headers to both force latest IE rendering engine or Chrome Frame,
    and also to cache the rendered page for 10 minutes.
    """
    r.headers["Cache-Control"] = "max-age=604800"
    #r.headers["Pragma"] = "no-cache"
    r.headers["Expires"] = "9999"
    r.headers['Cache-Control'] = 'public, max-age=9999'
    return r

class Client():

    def __init__(self):
        self.clients = {}
        self.rec = {}

    def add_client(self, ws):
        idet = randint(0, 100000)
        self.clients[idet] = ws
        self.rec[idet] = Player(self, idet)
        #Timer(10, self.close_connection, (ws,)).start()
        Thread(target=self.check_disconnect, args=(idet, )).start()
        return idet

    def run(self, idet):
        data = self.clients[idet].receive()
        self.rec[idet].handle(data)

    def active_client(self, idet):
        if idet in self.clients.keys():
            return True
        #print("not active_client")
        return False

    def check_disconnect(self, idet):
        try:
            while self.clients[idet].connected:
                #print("Is connected")
                sleep(0.05)
            self.close_connection(idet)
        except Exception as e:
            #print("check disconnected errored")
            # print(e)
            pass

    def close_connection(self, idet):
        if idet in self.rec.keys():
            self.rec[idet].handle_close()
            self.rec.pop(idet)
        if idet in self.clients.keys():
            self.clients.pop(idet)

client = Client()

@sock.route('/sock')
def echo(ws):
    idet = client.add_client(ws)
    while client.active_client(idet):
        client.run(idet)
    ws.close(1006, "Invalid Message")
    return
    print("Client disconnected because of an error")

if __name__ == "__main__":
    run()
    while True:
        sleep(100)


######### UNION AND INTERSECTION #########
#We cna use unoions and intersections to combine sets
#notice blue is in there twice, but doesnt mean it's going to be in the output
#twice, it will only be there once, since sets don't allow duplicates.

my_fav = {"red", "green", "blue", "black", "purple"}
her_fav = {"blue", "orange", "purple", "green"}

#union (|) - combines everything between two sets, (theres also a .union() method)
# You may see + (concatenation) to combine lists, in which there are repeats.
all_favs = my_fav | her_fav
print(all_favs)

#intersections (&) - elements shared between two sets
wedding_colors = my_fav & her_fav
print(wedding_colors)

#there are also methods that we can use such as, .intersection() in order to
#get the seame results as above
wedding_colors = my_fav.intersection(her_fav)
print(wedding_colors)


# Faça um programa que calcule o valor total investido por um colecionador em sua coleção de CDs e o valor médio gasto em cada um deles. O usuário deverá informar
# a quantidade de CDs e o valor para em cada um.

qnt = int(input("Digite a quantidade CDs: "))
soma = 0

for i in range(qnt):
    preco = float(input("Digite o preço pago no dvd %d: " % (i+1)))
    soma += preco

media = soma/qnt
print("Foi pago em média R$: %.2f por dvd " % media)

Lista de exercícios pybrasil/Repetição/28.py


import logging
import os
import vtk, qt, ctk, slicer
import warnings
from slicer.ScriptedLoadableModule import *

from SubjectHierarchyPlugins import AbstractScriptedSubjectHierarchyPlugin

class FormatMarkups(ScriptedLoadableModule):
    def __init__(self, parent):
        ScriptedLoadableModule.__init__(self, parent)
        parent.title = "SlicerMorph Format Markups Plugin"
        parent.categories = [""]
        parent.contributors = [" Sara Rolfe (UW), Murat Maga (UW)"]
        parent.helpText = ""
        parent.hidden = not slicer.util.settingsValue('Developer/DeveloperMode', False, converter=slicer.util.toBool)

        #
        # register subject hierarchy plugin once app is initialized
        #
        def onStartupCompleted():
            import SubjectHierarchyPlugins
            from FormatMarkups import FormatMarkupsSubjectHierarchyPlugin
            scriptedPlugin = slicer.qSlicerSubjectHierarchyScriptedPlugin(None)
            scriptedPlugin.name = "FormatMarkups"
            scriptedPlugin.setPythonSource(FormatMarkupsSubjectHierarchyPlugin.filePath)
        slicer.app.connect("startupCompleted()", onStartupCompleted)

class FormatMarkupsSubjectHierarchyPlugin(AbstractScriptedSubjectHierarchyPlugin):
  """ Scripted subject hierarchy plugin for the formatMarkups extension.
  """

  # Necessary static member to be able to set python source to scripted subject hierarchy plugin
  filePath = __file__

  def __init__(self, scriptedPlugin):
    AbstractScriptedSubjectHierarchyPlugin.__init__(self, scriptedPlugin)

    pluginHandlerSingleton = slicer.qSlicerSubjectHierarchyPluginHandler.instance()
    self.subjectHierarchyNode = pluginHandlerSingleton.subjectHierarchyNode()
    self.formatMarkupsAction = qt.QAction(f"Apply formatting to siblings", scriptedPlugin)
    self.menu = qt.QMenu("Plugin Menu")
    self.formatMarkupsAction.setMenu(self.menu)

  #
  # item context menus are what happens when you right click on a selected line
  #
  def itemContextMenuActions(self):
    """the actions that could be shown for any of this plugins items"""
    return [self.formatMarkupsAction]

  def showContextMenuActionsForItem(self, itemID):
    """Set actions visible that are valid for this itemID"""
    # reset all menus
    self.formatMarkupsAction.visible = False
    self.formatMarkupsAction.enabled = False
    self.menu.clear()
    # check if this is parent or leaf
    siblingIDList = vtk.vtkIdList()
    itemDataNode = self.subjectHierarchyNode.GetItemDataNode(itemID)
    parentID = self.subjectHierarchyNode.GetItemParent(itemID)
    parentIsScene = bool(self.subjectHierarchyNode.GetItemName(parentID) == "Scene")
    markupNodeTypes=["vtkMRMLMarkupsFiducialNode", "vtkMRMLMarkupsNode"]
    if itemDataNode is not None and itemDataNode.IsA("vtkMRMLMarkupsFiducialNode") and not parentIsScene:
      self.subjectHierarchyNode.GetItemChildren(parentID, siblingIDList)
      if siblingIDList.GetNumberOfIds()>0:
        copyDisplayAction = self.menu.addAction(f"Copy display properties to siblings")
        copyVisibilityAction = self.menu.addAction(f"Copy point visibility to siblings")
        uniqueColorAction = self.menu.addAction(f"Apply unique node color to siblings")
        copyDisplayAction.triggered.connect(lambda: self.copyDisplayProperties(itemDataNode, itemID, siblingIDList))
        copyVisibilityAction.triggered.connect(lambda: self.copyPointVisibility(itemDataNode, itemID, siblingIDList))
        uniqueColorAction.triggered.connect(lambda: self.applyUniqueColors(siblingIDList))
        # enable all menu options
        self.formatMarkupsAction.visible = True
        self.formatMarkupsAction.enabled = True
        copyDisplayAction.visible = True
        copyDisplayAction.enabled = True
        copyVisibilityAction.visible = True
        copyVisibilityAction.enabled = True

  def copyDisplayProperties(self, itemDataNode, itemID, siblingIDList):
    markupsLogic = slicer.modules.markups.logic()
    nodeLocked = itemDataNode.GetLocked()
    controlPointNumberLocked = itemDataNode.GetFixedNumberOfControlPoints()
    for i in range(siblingIDList.GetNumberOfIds()):
      siblingID = siblingIDList.GetId(i)
      if(itemID != siblingID):
        siblingNode = self.subjectHierarchyNode.GetItemDataNode(siblingID)
        markupsLogic.CopyBasicDisplayProperties(itemDataNode.GetDisplayNode(), siblingNode.GetDisplayNode())
        siblingNode.SetFixedNumberOfControlPoints(controlPointNumberLocked)
        siblingNode.SetLocked(nodeLocked)
        if(itemDataNode.GetNumberOfControlPoints() != siblingNode.GetNumberOfControlPoints()):
          logging.debug("Cannot apply control point lock flags to sibling with unequal point number: %s" % siblingNode.GetName())
        else:
          for j in range(itemDataNode.GetNumberOfControlPoints()):
            siblingNode.SetNthControlPointLocked(j, itemDataNode.GetNthControlPointLocked(j))

  def copyPointVisibility(self, itemDataNode, itemID, siblingIDList):
    for i in range(siblingIDList.GetNumberOfIds()):
      siblingID = siblingIDList.GetId(i)
      if(itemID == siblingID):
        next
      siblingNode = self.subjectHierarchyNode.GetItemDataNode(siblingID)
      if(itemDataNode.GetNumberOfControlPoints() != siblingNode.GetNumberOfControlPoints()):
        logging.debug("Cannot apply control point visibility to sibling with unequal point number: %s" % siblingNode.GetName())
      else:
        for j in range(itemDataNode.GetNumberOfControlPoints()):
          siblingNode.SetNthControlPointVisibility(j, itemDataNode.GetNthControlPointVisibility(j))

  def applyUniqueColors(self, siblingIDList):
    colorTableList = slicer.mrmlScene.GetNodesByClassByName('vtkMRMLColorTableNode', 'GenericColors')
    colorTable = colorTableList.GetItemAsObject(0)
    color = [0,0,0,0]
    colorIndex = 1
    for i in range(siblingIDList.GetNumberOfIds()):
      siblingID = siblingIDList.GetId(i)
      siblingNode = self.subjectHierarchyNode.GetItemDataNode(siblingID)
      colorTable.GetColor(colorIndex,color)
      siblingNode.GetDisplayNode().SetSelectedColor(color[0:3])
      if colorIndex < 307:
        colorIndex+=1
      else:
        colorIndex=1


import csv
import pandas
# with open("Book1.csv") as data_file:
#     data = csv.reader(data_file)
#     tempeature = []
#     for row in data:
#         if row[1] !="temp":
#             tempeature.append(int(row[1]))
#     print(tempeature)

#################################################

# data = pandas.read_csv("Book1.csv")
# alpha = (data["temp"])
# #print(alpha)

# temp_list = data["temp"].to_list()
# #print(temp_list)

# add = int(sum(temp_list))
# tot = int(len(temp_list))

# #print(f"The average tempis {add/tot}")

# maxx = data["temp"].max()
# #print(maxx)

# #print(data[data.temp == int(maxx)])

# monday = data[data.day == "Monday"]
# print(f"{((monday.temp/5)*9)+32}")
# #((x/5)*9) + 32

#####################################
# Create a CSV file with the data
#####################################
# data_dict = {
#     "students": ["A","B","C"],
#     "score": [1,6,0]
# }

# data = pandas.DataFrame(data_dict)
# data.to_csv("testtt_data.csv")

########################################

data = pandas.read_csv("Central_Park.csv")
#print(data.PColor)

G = 0
C = 0
B = 0

for color in data.PColor:
    if color == "Gray":
        G += 1
    elif color == "Black":
        B += 1
    elif color == "Cinnamon":
        C += 1

print(G)
print(B)
print(C)

data_dict = {
    "Fur Colour": ["Gray","Black","Cinnamon"],
    "Count": [G,B,C]
}

data = pandas.DataFrame(data_dict)
data.to_csv("testt_data.csv")


from django.test import TestCase

from faker import Faker

from api.users.models import User

from ...models import Context

class ContextModelTests(TestCase):
    def setUp(self):
        self.faker = Faker()
        self.user = User.objects.create(username=self.faker.name())

    def test_str(self):
        name = self.faker.name()
        context = Context.objects.create(
            owner=self.user,
            name=name,
        )
        self.assertEqual(str(context), name)


# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

# กระดองเต่า
plt.figure(figsize=[8,4])
for j in [0,1]:
    ax = plt.axes([0.5*j,0,0.5,1],xlim=[-50,50],ylim=[-50,50],aspect=1)
    for i in range(11):
        plt.plot([-50,50],[i*20-150,i*20-50],'#a46529',lw=5,zorder=0)
        plt.plot([-50,50],[i*20-50,i*20-150],'#a46529',lw=5,zorder=0)
        plt.plot([-50,50],[i*20-150,i*20-50],'#7e4b1b',lw=2,zorder=1)
        plt.plot([-50,50],[i*20-50,i*20-150],'#7e4b1b',lw=2,zorder=1)
    plt.axis('off')
plt.savefig('langtao.png',dpi=128,facecolor='k')
plt.close()

# ใบหน้าเต่า
plt.figure(figsize=[4,4])
ax = plt.axes([1./3,1./3,1./3,1./3],xlim=[-5,5],ylim=[-5,5],aspect=1)
ax.add_patch(mpl.patches.Ellipse([-2,1.8],0.4,0.9,color='#ddeeee'))
ax.add_patch(mpl.patches.Ellipse([2,1.8],0.4,0.9,color='#ddeeee'))
theta = np.radians(np.linspace(-60,60,21))
x = np.sin(theta)*2
y = -np.cos(theta)*1.5
plt.plot(x,y,'#ddeeee',lw=3)
plt.axis('off')
plt.savefig('natao.png',dpi=128,facecolor='k')
plt.close()


def ex(input):
    print(f"\n{' Exemple {} ':=^30}\n".format(input))
ex(1)
def mensagem(msg):
    print('-' * 30)
    print(f"{msg:^30}")
    print('-' * 30)
mensagem("Teste")
ex(2)
def título(txt):
    print('-' * 30)
    print(txt)
    print('-' * 30)
título(f"{'CURSO EM VÍDEO':^30}")
título(f"{'PYTHON É UMA DELÍCIA!':^30}")
título(f"{'GABRIEL ELIAS':^30}")
ex(3)
def soma(a, b):
    s = a + b
    print(s)

soma(4, 5)
soma(8, 9)
soma(2, 1)
ex(4)
def som(a, b):
    print(f'A = {a} e B = {b}')
    s = a + b
    print(f'A soma A + B = {s}\n')

som(4, 5)
som(b=4, a=5)
ex(5)
def contador(* núm):
    tam = len(núm)
    print(f"Recebi os valores {núm} e são ao todo {tam} números.")
    for valor in núm:
        print(f"{valor} ", end="")
    print("FIM!\n")

contador(2, 1, 7)
contador(8, 0)
contador(4, 4, 7, 6, 2)
ex(6)
def dobra(lst):
    pos = 0
    while pos < len(lst):
        lst[pos] *= 2
        pos += 1

valores = [6, 3, 9, 1, 0, 2]
dobra(valores)
print(valores)
ex(7)
def sm(* valores):
    s = 0
    for num in valores:
        s += num
    print(f"Somando os valores {valores} temos {s}")
sm(5, 2)
sm(2, 9, 4)


"""
Given an array of integers arr, return true if the number of occurrences of each value in the array
is unique, or false otherwise.

Example 1:
Input: arr = [1,2,2,1,1,3]
Output: true
Explanation: The value 1 has 3 occurrences, 2 has 2 and 3 has 1.
             No two values have the same number of occurrences.

Constraints:
1 <= arr.length <= 1000
-1000 <= arr[i] <= 1000
"""

import collections
from typing import List

class Solution:
    """
    Runtime: 40 ms, faster than 71.91% of Python3
    Memory Usage: 14.1 MB, less than 69.98% of Python3

    Time/Space complexity: O(n)
    """
    def uniqueOccurrences(self, arr: List[int]) -> bool:
        ctr = collections.Counter(arr)
        freq = ctr.values()
        # if there're non-unique counters, they will be omitted by set
        return len(freq) == len(set(freq))

if __name__ == '__main__':
    sol = Solution()
    test_cases = (
        ([1,2,2,1,1,3], True),
        ([1,2], False),
        ([-3,0,1,-3,1,1,1,-3,10,0], True))
    for inp_arr, exp_res in test_cases:
        assert sol.uniqueOccurrences(inp_arr) is exp_res


N, Q = (int(i) for i in input().split())
S = input()
LR = [list(map(int, input().split())) for _ in range(Q)]

accum = [0] * N

def countAC():
    count = 0
    for i in range(1, N):
        if(S[i-1] == "A" and S[i] == "C"):
            count += 1
        accum[i] = count

countAC()

for i in range(Q):
    print(accum[LR[i][1]-1] - accum[LR[i][0]-1])


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# automatic haze removal as described in
#
# Kaiming He, Jian Sun, and Xiaoou Tang, »Single Image Haze Removal
# Using Dark Channel Prior« in IEEE Transactions on Pattern Analysis
# and Machine Intelligence, vol. 33, no. 12, 2341--2353 (2010)
# DOI:  10.1109/TPAMI.2010.168
#
# He K., Sun J., Tang X. (2010) Guided Image Filtering. In: Daniilidis K.,
# Maragos P., Paragios N. (eds) Computer Vision – ECCV 2010. ECCV 2010.
# Lecture Notes in Computer Science, vol 6311. Springer, Berlin, Heidelberg

import os
import numpy as np
from pylab import *
from skimage import data, io, exposure, img_as_float
#
# first compile dehaze module with
# python3 setup.py build_ext --inplace
import dehaze

image='images/forrest.jpg'
image='images/city.jpg'
image='images/landscape.jpg'

# parameters

# window size (positive integer) for determing the dark channel and the transition map
w=4
# window size (positive integer) for guided filter
w2=3*w
# strength of the dahazing effect 0 <= stength <= 1 (is 0.95 in the original paper)
strength=0.99

# gamma correction, algorithm should work in linear RGB space
gamma=2.2

close('all')

I=img_as_float(io.imread(image))

io.imshow(I)
title('original image')
xticks([])
yticks([])
tight_layout()
show(False)

I=I**gamma

dark=dehaze.dark_channel(I, w)
figure()
imshow(dark, cmap='Greys')
title('dark channel')
xticks([])
yticks([])
tight_layout()
show(False)

haze_pixel=dark>=percentile(dark, 98)
In=sum(I, axis=2)/3
# figure()
# io.imshow(haze_pixel*In)
# title('most hazy regions')
# xticks([])
# yticks([])
# tight_layout()
# show(False)

#k0, k1=np.unravel_index(argmax(haze_pixel*In), In.shape)
#A0=I[k0, k1, :]
bright_pixel=In>=percentile(In[haze_pixel], 98)
A0=mean(I[logical_and(haze_pixel, bright_pixel), :], axis=0)

t=dehaze.transition_map(I, A0, w, strength)
figure()
imshow(t, cmap='Greys')
title('transition map')
xticks([])
yticks([])
tight_layout()
show(False)

t=dehaze.box_min(t, w)
t=dehaze.guidedfilter(I, t, w2, 0.001)
t[t<0.025]=0.025
figure()
imshow(t, cmap='Greys')
title('refined transition map')
xticks([])
yticks([])
tight_layout()
show(False)

J=I/t[:, :, np.newaxis] - A0[np.newaxis, np.newaxis, :]/t[:, :, np.newaxis] + A0
# J=empty_like(I)
# J[:, :, 0]=(I[:, :, 0]/t-A0[0]/t+A0[0])
# J[:, :, 1]=(I[:, :, 1]/t-A0[1]/t+A0[1])
# J[:, :, 2]=(I[:, :, 2]/t-A0[2]/t+A0[2])
J[J<0]=0
J[J>1]=1

J=J**(1/gamma)

figure()
imshow(J)
title('haze-free image')
xticks([])
yticks([])
tight_layout()
show(False)

name, ext=os.path.splitext(image)
io.imsave(name+'_haze_free'+ext, J)


kron = input('Eto zakaz dlya vedmaka...')
if kron.isdigit():
    kron = int(kron)
    kron25 = 0
    kron10 = 0
    kron5 = 0
    kron1 = 0
    while kron > 0:
        if (kron - 25) >= 0:
            kron25 += 1
            kron -= 25
        elif kron - 10 >= 0:
            kron10 += 1
            kron -= 10
        elif kron - 5 >= 0:
            kron5 += 1
            kron -= 5
        elif kron - 1 >= 0:
            kron1 += 1
            kron -= 1
    print(kron)
    print(kron25 + kron1 + kron5 + kron10)
else:
    print('никак вы бл"34дь не научитесь')

#5_Цикл_WHILE/Дополнительные задания/task_4_2_3.py


#   Author:     Brandon Lyon
#
#   Purpose:    Create a script that recreates this GUI
#
#
#
import tkinter
from tkinter import *
from tkinter import filedialog

window = Tk()

class ParentWindow(Frame):
    def __init__ (self, master):
        Frame.__init__(self)

        self.varBrowse = StringVar()
        self.varBrowse2 = StringVar()

        self.master = master
        self.master.resizable(width=True, height=True)
        self.master.geometry('{}x{}'.format(700, 400))
        self.master.title('Check Files')
        self.master.config(bg='lightgray')

##        self.btnBrowse = Button(self.master, text="Browse...", width=10, height=2)
##        self.btnBrowse.grid(row=1, column=0, padx=(10,40), pady=(30,0), sticky=NE)

        self.button_Browse = Button(window, text = "Browse...", command = browseFiles, width=10, height=2)
        self.btnBrowse.grid(row=1, column=0, padx=(10,40), pady=(30,0), sticky=NE)

        self.btnBrowse2 = Button(self.master, text="Browse...", width=10, height=2)
        self.btnBrowse2.grid(row=2, column=0, padx=(10,40), pady=(30,0), sticky=NE)

        self.txtBrowse = Entry(self.master,text=self.varBrowse, font=("Helvetica", 16), fg='black', bg='white')
        self.txtBrowse.grid(row=1, column=1, padx=(10,0), pady=(10,0))

        self.txtBrowse2 = Entry(self.master,text=self.varBrowse2, font=("Helvetica", 16), fg='black', bg='white')
        self.txtBrowse2.grid(row=2, column=1, padx=(10,0), pady=(10,0))

        self.btnCheck4 = Button(self.master, text="Check for files...", width=10, height=2)
        self.btnCheck4.grid(row=3, column=0, padx=(10,40), pady=(30,0), sticky=NW)

        self.btnClosePro = Button(self.master, text="Close Program", width=10, height=2)
        self.btnClosePro.grid(row=4, column=2, padx=(10,40), pady=(30,0), sticky=SE)

            #Define the listbox with a scrollbar and grid them
        self.scrollbar1 = Scrollbar(self.master,orient=VERTICAL)
        self.lstList1 = Listbox(self.master,exportselection=0,yscrollcommand=self.scrollbar1.set)
        self.lstList1.bind('<<ListboxSelect>>',lambda event: askDir_func.onSelect(self,event))
        self.scrollbar1.config(command=self.lstList1.yview)
        self.scrollbar1.grid(row=1,column=6,rowspan=7,columnspan=1,padx=(0,0),pady=(0,0),sticky=N+E+S)
        self.lstList1.grid(row=1,column=4,rowspan=7,columnspan=3,padx=(0,0),pady=(0,0),sticky=N+E+S+W)

def browseFiles():
    filename = filedialog.askopenfilename(initialdir = "/",
                                          title = "Select a File",
                                          filetypes = (("Text files",
                                                        "*.txt*"),
                                                       ("all files",
                                                        "*.*")))

if __name__ == "__main__":
    root = Tk()
    App = ParentWindow(root)
    root.mainloop()
    window.mainloop()


from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///hatchways'
app.config['SQLALCHEMY_ECHO'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db)
db.init_app(app)

@app.route("/")
def hello():
    return {"yo": "world"}

class Student(db.Model):
    __tablename__ = 'students'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())

    marks = db.relationship('Mark', backref='students')

    def __repr__(self):
        return f"<Student {self.name}>"

class Course(db.Model):
    __tablename__ = 'courses'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    teacher = db.Column(db.String())

    tests = db.relationship('Test', backref='courses')

    def __repr__(self):
        return f"<Course {self.name}>"

class Test(db.Model):
    __tablename__ = 'tests'

    id = db.Column(db.Integer, primary_key=True)
    weight = db.Column(db.Integer())
    course_id = db.Column(db.Integer(), db.ForeignKey('courses.id'))

    marks = db.relationship('Mark', backref='tests')

class Mark(db.Model):
    __tablename__ = 'marks'

    id = db.Column(db.Integer, primary_key=True)
    mark = db.Column(db.Integer())
    test_id = db.Column(db.Integer(), db.ForeignKey('tests.id'))
    student_id = db.Column(db.Integer(), db.ForeignKey('students.id'))

if __name__ == "__main__":
    app.run(debug=True)


import re
import time
import base64
import random
from io import BytesIO
from PIL import Image, ImageDraw
from typing import Optional, List, Tuple, Dict
from playwright.async_api import Page
from playwright.async_api import Cookie

def get_user_agent() -> str:
    user_agent = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.79 Safari/537.36",
        "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36"
    ]

    return random.choice(user_agent)

async def get_login_qrcode(page: Page, selector: str) -> str:
    """
    从目标选择其中找到登录二维码
    :param page:
    :param selector:
    :return:
    """
    try:
        elements = await page.wait_for_selector(
            selector=selector
        )
        login_qrcode_img = await elements.get_property("src")
        return str(login_qrcode_img)
    except Exception as e:
        print(e)
        return ""

def convert_cookies(cookies: Optional[List[Cookie]]) -> Tuple[str, Dict]:
    if not cookies:
        return "", {}
    cookies_str = ";".join([f"{cookie.get('name')}={cookie.get('value')}" for cookie in cookies])
    cookie_dict = dict()
    for cookie in cookies:
        cookie_dict[cookie.get('name')] = cookie.get('value')
    return cookies_str, cookie_dict

def show_qrcode(qr_code: str):
    """
    解析base64编码qrcode图像并显示它
    :param qr_code:
    :return:
    """
    qr_code = qr_code.split(",")[1]
    qr_code = base64.b64decode(qr_code)
    image = Image.open(BytesIO(qr_code))

    # 在二维码周围添加方形边框，显示在边框内，提高扫描精度.
    width, height = image.size
    new_image = Image.new('RGB', (width + 20, height + 20), color=(255, 255, 255))
    new_image.paste(image, (10, 10))
    draw = ImageDraw.Draw(new_image)
    draw.rectangle((0, 0, width + 19, height + 19), outline=(0, 0, 0), width=1)
    # new_image.show()
    # time.sleep(3)
    # image.close()
    return new_image

def get_current_timestamp():
    return int(time.time() * 1000)

def match_interact_info_count(count_str: str) -> int:
    if not count_str:
        return 0

    match = re.search(r'\d+', count_str)
    if match:
        number = match.group()
        return int(number)
    else:
        return 0