import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from matplotlib.offsetbox import OffsetImage, AnnotationBbox

import matplotlib.colors as mcolors

# Define efficiency ranges for each nose cone type
efficiency_ranges = {
    'LV-Haack': [(0.8, 1, 1), (1, 1.2, 3)],
    'Von Karman': [(0.8, 1, 1), (1, 1.2, 2), (1.2, 1.4, 1), (1.4, 1.75, 3), (1.75, 1.9, 2), (1.9, 2.2, 1)],
    'Parabola': [(0.8, 0.9, 1), (0.9, 1.1, 3)],
    '3/4 Parabola': [(0.8, 0.9, 3), (1.3, 1.45, 2), (1.45, 1.95, 1), (1.95, 2.2, 2)],
    '1/2 Parabola': [(1.4, 1.55, 3), (1.55, 2.2, 2)],
    'x^(3/4)': [(0.8, 0.9, 2), (1.45, 1.6, 3), (1.6, 2.2, 2)],
    'x^(1/2)': [(0.8, 0.95, 1), (0.95, 1.05, 2), (1.05, 1.15, 1), (1.15, 1.35, 2), (1.35, 1.45, 1), (1.45, 1.8, 2), (1.8, 1.95, 3)],
    'Cone': [(0.8, 2.2, 4)],
    'Ogive': [(0.8, 2.2, 4)]
}

# Read the SVG images
#images = {
#    'Ogive': mpimg.imread('NoseconeImages/ogive.svg'),
#    'Cone': mpimg.imread('NoseconeImages/cone.svg'),
#    'LV-Haack': mpimg.imread('NoseconeImages/lv_haack.svg'),
#    'Von Karman': mpimg.imread('NoseconeImages/von_karman.svg'),
#    'Parabola': mpimg.imread('NoseconeImages/parabola.svg'),
#    '3/4 Parabola': mpimg.imread('NoseconeImages/three_quarters_parabola.svg'),
#    '1/2 Parabola': mpimg.imread('NoseconeImages/half_parabola.svg'),
#    'x^(3/4)': mpimg.imread('NoseconeImages/x_three_quarters.svg'),
#    'x^(1/2)': mpimg.imread('NoseconeImages/x_half.svg')
#}

# Define colors for efficiencies
colors = {
    1: 'green',
    2: 'yellow',
    3: 'orange',
    4: 'red'
}

plt.figure(figsize=(12, 8))

# Plot efficiency ranges for each nose cone type
for i, (cone_type, ranges) in enumerate(efficiency_ranges.items()):
    for start, end, efficiency in ranges:
        plt.hlines(i, start, end, colors=colors[efficiency], lw=15)

plt.yticks(range(len(efficiency_ranges)), list(efficiency_ranges.keys()))
plt.xlabel('Mach number')
plt.ylabel('Nose cone type')

# Create a custom legend
handles = [plt.Line2D([0], [0], color=color, lw=4) for color in colors.values()]
labels = [f'{efficiency}' for efficiency in colors.keys()]
plt.legend(handles, labels)

# Add the images to the graph
#for i, (cone_type, image) in enumerate(images.items()):
#    ax = plt.gca()
#    imagebox = OffsetImage(image, zoom=0.2)
#    ab = AnnotationBbox(imagebox, (0.8, i))
#    ax.add_artist(ab)

plt.grid(True)
plt.title('Efficiencies of Different Nose Cone Types')
plt.tight_layout()
plt.show()


import cNo
class cCartas:
    def __init__(self):
        self.__inicio__ = cNo.cNo()
        self.monte = []
        self.numCartas = 0
        self.numMonte = 0

    def __str__(self):
        outStr = "[ "

        noAtual = self.__inicio__
        elementos = []

        while noAtual.__prox__ is not None:
            noAtual = noAtual.__prox__
            dado = [noAtual.__dado__]
            elementos += dado
        if self.numCartas == 0:
            outStr += "Jogador Sem Cartas ]"
        else:
            for i in range(0, self.numCartas - 1):
                outStr += f'{elementos[i]} , '
            outStr += f'{elementos[self.numCartas - 1]} ]'
        return outStr

    def removeNo(self, n: cNo):
        """Remove das cartas recebidas a primeira carta que faz par com a carta passada como parametro"""

        noAnterior = n
        noAtual = noAnterior.__prox__
        while noAtual:
            # Compara o primeiro caracter da string.
            if noAtual.__dado__[0] == n.__dado__[0]:
                noAnterior.__prox__ = noAtual.__prox__

                # Adiciona a carta a lista monte e deleta o No Atual.
                self.monte += [n.__dado__]
                self.monte += [noAtual.__dado__]
                del(noAtual)

                # Incrementa o monte e decrementa a quantidade de cartas.
                self.numMonte += 2
                self.numCartas -= 1
                return True
            noAnterior = noAtual
            noAtual = noAtual.__prox__
        return False

    def separaPar(self):
        """Separa os pares de cartas."""

        # Percorre a lista
        noAnterior = self.__inicio__
        noAtual = noAnterior.__prox__
        while noAtual is not None:
            if self.removeNo(noAtual):
                noAnterior.__prox__ = noAtual.__prox__

                # Deleta o No atual se for encontrado um par para a carta.
                del(noAtual)
                self.numCartas -= 1
                noAtual = noAnterior.__prox__
            else:
                noAnterior = noAtual
                noAtual = noAtual.__prox__

        if self.numCartas == 0:
            return True
        return False

    def insereNoFim(self, n):
        """Insere um novo No no fim da lista"""

        novoNo = cNo.cNo(n)
        if self.__inicio__ == None:
            self.__inicio__ = novoNo
            self.numCartas += 1
            return True
        noAtual = self.__inicio__
        while noAtual.__prox__ is not None:
            noAtual = noAtual.__prox__
        noAtual.__prox__ = novoNo
        self.numCartas += 1
        return True

    def removeCartaEscolhida(self, n):
        """Remove a carta no Indice escolhido."""

        noAnterior = self.__inicio__
        noAtual = noAnterior.__prox__
        for c in range(n):
            if noAtual.__prox__ is not None:
                noAnterior = noAtual
                noAtual = noAtual.__prox__

        noAnterior.__prox__ = noAtual.__prox__
        no = noAtual
        del(noAtual)
        self.numCartas -= 1
        return no.__dado__


# PyTorch imports
import torch
from torch.utils.data import Dataset, DataLoader, Subset
from torch.utils.data import random_split
import torchvision
from torchvision import transforms

# python imports
import os
import numpy as np

# additional libraries
import pytorch_lightning as pl

# custom imports
from data_management.data_tools import DefaultDataset

class FashionMNISTDataModule(pl.LightningDataModule):
    """PyTorch-Lightning datamodule class for the FashionMNIST dataset."""
    def __init__(self, data_dir, batch_size, Nsamples=None, valid_frac=None,
                 random_state=42, transform=None, test_transform=None ):
        super().__init__()
        self.data_dir   = data_dir      # directory to contain this and other eventual datasets
        self.batch_size = batch_size    # training batch size
        self.Nsamples   = Nsamples      # number of samples to load from the train dataset
        self.frac       = valid_frac    # fraction of samples to use as validation set
        self.seed       = random_state

        # set up transformations
        if transform is None:
            self.transform = transforms.ToTensor()
        else:
            self.transform = transform

        if test_transform is None:
            self.test_transform = transforms.ToTensor()
        else:
            self.test_transform = test_transform

        ### label names
        self.label_names = ['T-shirt/top','Trouser','Pullover','Dress','Coat'      ,
                            'Sandal'     ,'Shirt'  ,'Sneaker' ,'Bag'  ,'Ankle boot']

    def prepare_data(self):
        ### train dataset
        self.full = torchvision.datasets.FashionMNIST(self.data_dir,
                                                      train    = True,
                                                      download = True, ##
                                                     )
        ### test dataset
        self.test_data = torchvision.datasets.FashionMNIST(self.data_dir,
                                                           train     = False,
                                                           download  = True, ##
                                                           transform = self.test_transform,
                                                          )

    def setup(self, stage=None):
        # eventually restricting to Nsamples
        if self.Nsamples is not None:
            self.full = Subset(self.full, np.random.choice(range(self.full.__len__()), size=self.Nsamples))

        # split into train and validation
        split_tr  = round( self.full.__len__()*(1-self.frac) )
        split_val = round( self.full.__len__()*self.frac )

        train, val = random_split(self.full,
                                  [split_tr, split_val],
                                  generator=torch.Generator().manual_seed(self.seed),
                                 )
        # add transforms
        self.train_data = DefaultDataset(data=train, transform=self.transform     )
        self.val_data   = DefaultDataset(data=val  , transform=self.test_transform)

    def train_dataloader(self):
        return DataLoader(self.train_data,
                          batch_size = self.batch_size,
                          shuffle    = True,
                          pin_memory = True,
                         )

    def val_dataloader(self):
        return DataLoader(self.val_data,
                          batch_size = self.batch_size,
                          shuffle    = False,
                          pin_memory = True,
                         )

    def test_dataloader(self):
        return DataLoader(self.test_data,
                          batch_size = 500, #self.batch_size,
                          shuffle    = False,
                          pin_memory = True,
                         )

    def predict_dataloader(self):
        return DataLoader(self.test_data,
                          batch_size = 500, #self.batch_size,
                          shuffle    = False,
                          pin_memory = True,
                         )

    def get_label_names(self):
        return self.label_names

    def get_sample_size(self):
        return torch.Size([1,28,28])


# -*- coding:utf-8 -*-
# 问题：一个5位数，判断它是不是回文数。即12321是回文数，个位与万位相同，十位与千位相同。

def palindromic_number(s):
    if len(s) != 5:
        print('You have entered a incorrect number')
    else:
        if s[0] == s[-1] and s[1] == s[-2]:
            print('Yes')
        else:
            print('No')

while True:
    num = input('please input a integer:')
    if num == 'q':
        break
    else:
        palindromic_number(num)


from typing import Dict
import uuid
import time
import json
import logging
import copy
import subprocess
import os

from . import widgets, messagebus, util, tagpoints, persist, directories, alerts, workers
from . import jackmanager, gstwrapper, mixerfx

import threading

global_api = widgets.APIWidget()
global_api.require("/users/mixer.edit")

# Configured list of mixer channel strips
channels: Dict[str, Dict] = {}

log = logging.getLogger("system.mixer")

presetsDir = os.path.join(directories.mixerdir, "presets")

recorder = None

# Try to import a cython extension that only works on Linux
try:
    from . import threadpriority
    setPriority = threadpriority.setThreadPriority
except Exception:
    log.exception(
        "Cython import failed, gstreamer realtime priority is disabled")

    def setPriority(p, po): return None

gstwrapper.Pipeline.setCurrentThreadPriority = setPriority

def replaceClientNameForDisplay(i):
    x = i.split(':')[0]
    if x in jackmanager.portJackNames:
        return i.replace(x, jackmanager.portJackNames[x])

    return i

def onPortAdd(t, m):
    # m[1] is true of input
    global_api.send(['newport', m.name, {}, m.isInput])

def onPortRemove(t, m):
    # m[1] is true of input
    global_api.send(['rmport', m.name])

messagebus.subscribe("/system/jack/newport/", onPortAdd)
messagebus.subscribe("/system/jack/rmport/", onPortRemove)

def logReport():
    if not util.which("jackd"):
        log.error("Jackd not found. Mixing will not work")
    if not util.which("a2jmidid"):
        log.error("A2jmidid not found, MIDI may not work")
    if not util.which("fluidsynth"):
        log.error("Fluidsynth not found. MIDI playing will not work,")
    try:
        if not gstwrapper.doesElementExist("tee"):
            log.error(
                "Gstreamer or python bindings not installed properly. Mixing will not work")
    except Exception:
        log.exception(
            "Gstreamer or python bindings not installed properly. Mixing will not work")
    if not gstwrapper.doesElementExist("jackaudiosrc"):
        log.error("Gstreamer JACK plugin not found. Mixing will not work")

    for i in effectTemplates:
        e = effectTemplates[i]
        if 'gstElement' in e:
            if not gstwrapper.doesElementExist(e['gstElement']):
                log.warning(
                    "GST element " + e['gstElement'] + " not found. Some effects in the mixer will not work.")
        if 'gstMonoElement' in e:
            if not gstwrapper.doesElementExist(e['gstMonoElement']):
                log.warning("GST element " + e['gstMonoElement'] +
                            " not found. Some effects in the mixer will not work.")
        if 'gstStereoElement' in e:
            if not gstwrapper.doesElementExist(e['gstStereoElement']):
                log.warning("GST element " + e['gstStereoElement'] +
                            " not found. Some effects in the mixer will not work.")

effectTemplates_data = mixerfx.effectTemplates_data

effectTemplates = effectTemplates_data

def cleanupEffectData(fx):
    x = effectTemplates.get(fx['type'], {})
    for i in x:
        if i not in fx:
            fx[i] == x[i]

    if not 'help' in fx:
        fx['help'] = ''
    if 'displayName' not in fx:
        fx['displayName'] = fx['type']
    if 'gstSetup' not in fx:
        fx['gstSetup'] = {}

channelTemplate = {"type": "audio", "effects": [
    effectTemplates['fader']], "input": '', 'output': '', "fader": -60, "soundFuse": 3}

specialCaseParamCallbacks = {}

# Returning true enables the default param setting action
def beq3(e, p, v):
    if p == "2:freq":
        e.set_property("2:bandwidth", v * 0.3)
    return True

def echo(e, p, v):
    if p == "delay":
        e.set_property("delay", v * 1000000)
        return False
    return True

def queue(e, p, v):
    if p == "min-threshold-time":
        e.set_property("min-threshold-time", v * 1000000)
        # Set to something short to clear the already buffered crap through leakage
        e.set_property("max-size-time", v * 1000000)
        # We should be able to depend on JACK not to let us get horribly out of sync,
        # The read rate should be exactly the write rate, so we give
        # As much buffer as you can before delay sounds worse than dropouts.
        e.set_property("max-size-time", v * 1000000 + 50 * 1000 * 1000)

        return False
    return True

# with open("/dev/shm/kaithem_kw_file",'w') as f:
#     f.write('hello /1e-30/\n')

# with open("/dev/shm/kaithem_dummy_kw_file",'w') as f:
#     f.write('hello /1e-30/\n')

# def tmpKwFile(e,p,v,c):
#     if p=='kws':
#

specialCaseParamCallbacks['3beq'] = beq3
specialCaseParamCallbacks['echo'] = echo
specialCaseParamCallbacks['queue'] = queue

# Returning true enables the default param setting action

def send(e, p, v):
    if v > -60:
        e.set_property('volume', 10**(float(v) / 20))
    else:
        e.set_property('volume', 0)

specialCaseParamCallbacks['send'] = send

class BaseChannel():
    pass

class FluidSynthChannel(BaseChannel):
    "Represents one MIDI connection with a single plugin that remaps all channels to one"

    def __init__(self, board, name, input, output, mapToChannel=0):
        self.name = name

        self.input = jackmanager.MonoAirwire(input, self.name + "-midi:*")
        self.output = jackmanager.airwire(self.name, output)

    def start(self):
        if self.map:
            self.process = subprocess.Popen(["fluidsynth",
                                             '-a', 'jack', '-m', 'jack', "-c", "0", "-r", "0",
                                             "-o", "audio.jack.id", self.name,
                                             "-o", "audio.jack.multi", "True",
                                             "-o", "midi.jack.id", self.name + "-midi",
                                             ],

                                            stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
            self.input.connect()
            self.output.connect()
        else:
            self.aiirwire.connect()

class Recorder(gstwrapper.Pipeline):
    def __init__(self, name="krecorder", channels=2, pattern="mixer_"):
        gstwrapper.Pipeline.__init__(self, name, realtime=70)

        self.src = self.addElement("jackaudiosrc",
                                   port_pattern="fgfcghfhftyrtw5ew453xvrt", client_name="krecorder", connect=0, slave_method=0)
        self.capsfilter = self.addElement(
            "capsfilter", caps="audio/x-raw,channels=" + str(channels))

        filename = os.path.join(directories.vardir, "recordings", "mixer",
                                pattern + time.strftime("%Y%b%d%a%H%M%S", time.localtime()) + ".ogg")

        if not os.path.exists(os.path.join(directories.vardir, "recordings", "mixer")):
            os.makedirs(os.path.join(
                directories.vardir, "recordings", "mixer"))

        self.addElement("queue")
        self.addElement("audioconvert")
        self.addElement("opusenc", bitrate=96000)

        self.addElement("oggmux")
        self.addElement("filesink", location=filename)

class ChannelStrip(gstwrapper.Pipeline, BaseChannel):

    def __init__(self, name, board=None, channels=2, input=None, outputs=[], soundFuse=3):
        self.name = name
        gstwrapper.Pipeline.__init__(self, name, realtime=70)
        self.board = board
        self.levelTag = tagpoints.Tag("/jackmixer/channels/" + name + ".level")
        self.levelTag.min = -90
        self.levelTag.max = 3
        self.levelTag.hi = -3
        self.levelTag.unit = 'dB'
        self.levelTag.writable = False

        # Set default
        self.levelTag.value = -90
        self.lastLevel = 0
        self.lastRMS = 0
        self.lastNormalVolumeLevel = time.monotonic()
        # Limit how often we can clear the alert
        self.alertRatelimitTime = time.monotonic() + 10
        self.soundFuseSetting = soundFuse
        self.lastPushedLevel = time.monotonic()
        self.effectsById = {}
        self.effectDataById = {}
        self.faderLevel = -60
        self.channels = channels

        # Are we already doing a loudness cutoff?
        self.doingFeedbackCutoff = False

        if not input or not input.startswith("rtplisten://"):

            self.src = self.addElement("jackaudiosrc", buffer_time=10, latency_time=10,  do_timestamp=True,
                                       port_pattern="fgfcghfhftyrtw5ew453xvrt", client_name=name + "_in", connect=0, slave_method=2)
            self.capsfilter = self.addElement(
                "capsfilter", caps="audio/x-raw,channels=" + str(channels))
        else:
            self.src = self.addElement(
                "udpsrc", port=int(input.split("://")[1]))
            self.capsfilter = self.addElement(
                "capsfilter", caps="application/x-rtp, media=(string)audio, clock-rate=(int)48000, encoding-name=(string)X-GST-OPUS-DRAFT-SPITTKA-00, payload=(int)96, ssrc=(uint)950073154, clock-base=(uint)639610336, seqnum-base=(uint)55488")
            self.addElement("rtpjitterbuffer")
            self.addElement("rtpopusdepay")
            self.addElement("opusdec")
            self.addElement("audioconvert")
            self.addElement("audioresample")

        self.input = input
        self._input = None
        self.outputs = outputs
        self._outputs = []
        self.sends = []
        self.sendAirwires = {}

        self.faderTag = tagpoints.Tag("/jackmixer/channels/" + name + ".fader")
        self.faderTag.subscribe(self._faderTagHandler)
        self.faderTag.max = 20
        self.faderTag.min = -60
        self.faderTag.lo = -59
        self.faderTag.hi = 3
        self.faderTag.unit = 'dB'

        self.mute = False

        self.effectParamTags = {}

        self.usingJack = True

        # General good practice to use this when creating a tag,
        # If we don't know who else may have assigned alerts.
        self.levelTag.clearDynamicAlarms()
        self.levelTag.setAlarm(
            "volume", "value>soundFuseSetting", tripDelay=0.3)

        # self.loudnessAlert = alerts.Alert(self.name+".abnormalvolume", priority='info')

    @property
    def soundFuseSetting(self):
        return self._soundFuseSetting

    @soundFuseSetting.setter
    def soundFuseSetting(self, v):
        self._soundFuseSetting = v
        self.levelTag.evalContext['soundFuseSetting'] = v

    def finalize(self,wait=3):
        with self.lock:
            # self.addElement("audioconvert")
            # self.capsfilter2= self.addElement("capsfilter", caps="audio/x-raw,channels="+str(channels))

            self.addElement("audiorate")

            # We can connect faster letting gstreamer do the work where possible
            # and maybe avoid some timeout oddness
            pattern = "fgfcghfhftyrtw5ew453xvrt"
            if self.outputs:
                pattern=self.outputs[0]

            # TODO maybe we can use the low latency flag if we implement feature detection
            self.sink = self.addElement("jackaudiosink", buffer_time=10, latency_time=10, sync=False,
                                        slave_method=2, port_pattern=pattern, client_name=self.name + "_out", connect=0, blocksize=self.channels * 128)

            # I think It doesn't like it if you start without jack
            if self.usingJack:
                t = time.time()
                while(time.time() - t) < 3:
                    if jackmanager.getPorts():
                        break
                if not jackmanager.getPorts():
                    return
        self.start(timeout=wait)

    def connect(self, restore=[]):
        self._outputs = []

        # wait till it exists for real
        for i in range(8):
            p = [i.name for i in jackmanager.getPorts()]
            p2 = [i.clientName for i in jackmanager.getPorts()]
            p = p+p2
            if (self.name + "_out" in p) and (self.name + "_in") in p:
                break
            else:
                time.sleep(0.5)

        for i in self.outputs:
            x = jackmanager.Airwire(
                self.name + "_out", i, forceCombining=(self.channels == 1))
            x.connect()
            self._outputs.append(x)

        self._input = jackmanager.Airwire(
            self.input, self.name + "_in", forceCombining=(self.channels == 1))
        self._input.connect()
        for i in restore:
            for j in i[1]:
                try:
                    jackmanager.connect(i[0], j)
                except Exception:
                    logging.exception("Failed to conneect airwire")
        for i in self.sendAirwires:
            try:
                self.sendAirwires[i].connect()
            except Exception:
                logging.exception("Failed to conneect airwire")

    def stop(self):
        with self.lock:
            for i in self.sendAirwires:
                self.sendAirwires[i].disconnect()
            if self._input:
                self._input.disconnect()
            for i in self._outputs:
                i.disconnect()
        gstwrapper.Pipeline.stop(self)

        # wait till jack catches up
        for i in range(15):
            p = [i.name for i in jackmanager.getPorts()]
            p2 = [i.clientName for i in jackmanager.getPorts()]
            p = p+p2
            if (self.name + "_out" in p) or (self.name + "_in") in p:
                time.sleep(0.5)
            else:
                break

    def backup(self):
        c = []

        for i in jackmanager.getPorts(self.name + "_in:"):
            c.append((i, jackmanager.getConnections(i)))
        for i in jackmanager.getPorts(self.name + "_out:"):
            c.append((i, jackmanager.getConnections(i)))
        return c

    def setInput(self, input):
        if "://" in input:
            return
        with self.lock:
            self.input = input
            if self._input:
                self._input.disconnect()
            self._input = jackmanager.Airwire(
                self.input, self.name + "_in", forceCombining=(self.channels == 1))
            self._input.connect()

    def setOutputs(self, outputs):
        with self.lock:
            self.outputs = outputs
            for i in self._outputs:
                i.disconnect()

            self._outputs = []
            for i in self.outputs:
                x = jackmanager.Airwire(
                    self.name + "_out", i, forceCombining=(self.channels == 1))
                x.connect()
                self._outputs.append(x)

    def addSend(self, target, id, volume=-60):
        # I hate doing things like this...
        # But senda apparently use a lot of low level pipeline manipulation that has to be done on
        # the other side.
        with self.lock:
            if not isinstance(target, str):
                raise ValueError("Target must be string")
            l, e2 = self.addJackMixerSendElements(target, id, volume)

            self.effectsById[id] = l
            self.effectsById[id + "*destination"] = e2

            d = effectTemplates['send']
            d['params']['*destination']['value'] = target
            d['params']['volume']['value'] = volume
            self.effectDataById[id] = d

            # Sequentially number the sends
            cname = self.name + "_send" + str(len(self.sends))
            e2.set_property("client-name", cname)

            self.sendAirwires[id] = jackmanager.Airwire(
                cname, target, forceCombining=(self.channels == 1))
            self.sends.append(e2)

    def loadData(self, d):

        self.mute = d.get('mute', False)

        for i in d['effects']:
            if d.get('bypass', False):
                continue
            if not "id" in i or not i['id']:
                i['id'] = str(uuid.uuid4())
            if i['type'] == "fader":
                self.fader = self.addElement("volume")
                # We have to set it here and can't rely on the tagpoint, it only does anything on *changes*
                self.fader.set_property('volume', 10**(float(d['fader']) / 20))
            # Special case this, it's made of multiple gstreamer blocks and also airwires
            elif i['type'] == "send":
                self.addSend(i['params']['*destination']['value'],
                             i['id'], i['params']['volume']['value'])

            else:

                # Sidechain lets us split off a whole effect chain that does not
                # feed the main chain, such as fir the speech recognition effect
                if i.get('sidechain', 0):
                    linkTo = self.addElement("tee")
                    self.addElement("queue")
                    linkTo = self.addElement(
                        "queue", sidechain=True, connectToOutput=linkTo)
                    sidechain = True

                else:
                    # Default link to prev
                    linkTo = None
                    sidechain = False

                supports = []
                if "preSupportElements" in i:
                    for j in i['preSupportElements']:
                        linkTo = self.addElement(j['gstElement'], **j['gstSetup'], sidechain=sidechain,
                                                 connectToOutput=linkTo if (not j.get("noConnectInput", False)) else False)
                        supports.append(linkTo)

                # Prioritize specific mono or stereo version of elements
                if self.channels == 1 and 'monoGstElement' in i:
                    linkTo = self.effectsById[i['id']] = self.addElement(
                        i['monoGstElement'], **i['gstSetup'], sidechain=sidechain,
                        connectToOutput=linkTo if (not i.get("noConnectInput", False)) else False,connectWhenAvailable=i.get("connectWhenAvailable", None))
                elif self.channels == 2 and 'stereoGstElement' in i:
                    linkTo = self.effectsById[i['id']] = self.addElement(
                        i['stereoGstElement'], **i['gstSetup'], sidechain=sidechain,
                        connectToOutput=linkTo if (not i.get("noConnectInput", False)) else False, connectWhenAvailable=i.get("connectWhenAvailable", None))
                else:
                    linkTo = self.effectsById[i['id']] = self.addElement(
                        i['gstElement'], **i['gstSetup'], sidechain=sidechain,
                        connectToOutput=linkTo if (not i.get("noConnectInput", False)) else False, connectWhenAvailable=i.get("connectWhenAvailable", None))

                elmt = linkTo
                linkTo.preSupports = supports

                self.effectDataById[i['id']] = i

                supports = []
                if "postSupportElements" in i:
                    for j in i['postSupportElements']:
                        linkTo = self.addElement(j['gstElement'], **j['gstSetup'], sidechain=sidechain,
                                                 connectToOutput=linkTo if (not j.get("noConnectInput", False)) else False)
                        supports.append(linkTo)
                elmt.postSupports = supports

                for j in i['params']:
                    if j == 'bypass':
                        continue
                    if i['type'] in specialCaseParamCallbacks:
                        x = specialCaseParamCallbacks[i['type']]
                        if x(self.effectsById[i['id']], j, i['params'][j]['value']):
                            self.setProperty(
                                self.effectsById[i['id']], j, i['params'][j]['value'])
                    else:
                        self.setEffectParam(
                            i['id'], j, i['params'][j]['value'])

                # Sidechain FX have a bug where they always cause an output to the system channel, we have to work around
                # That with a hack.  Basically sidechains only exist to let us to alternative outputs anyway.
                if i.get('silenceMainChain', False):
                    self.addElement('volume', volume=0)

        self.faderTag.value = d['fader']
        self.setFader(self.faderTag.value)

        self.setInput(d['input'])
        self.setOutputs(d['output'].split(","))

    def setEffectParam(self, effectId, param, value):
        "Set val after casting, and return properly typed val"
        with self.lock:

            paramData = self.effectDataById[effectId]['params'][param]
            paramData['value'] = value
            t = self.effectDataById[effectId]['type']

            if t == 'float':
                value = float(value)

            if t == 'bool':
                value = value > 0.5

            if t == 'enum':
                value = int(value)

            if t == 'int':
                value = int(value)
            if t == 'string.int':
                try:
                    value = int(value)
                except Exception:
                    value = 0

            # One type of special case
            if param[0] == '*':
                if param == "*destination":
                    # Keep the old origin, just swap the destination
                    if effectId in self.sendAirwires:
                        self.sendAirwires[effectId].disconnect()
                        self.sendAirwires[effectId] = jackmanager.Airwire(
                            self.sendAirwires[effectId].orig, value, forceCombining=(self.channels == 1))
                        try:
                            self.sendAirwires[effectId].connect()
                        except Exception:
                            pass
            elif param == "bypass":
                pass
            else:
                if t in specialCaseParamCallbacks:
                    r = specialCaseParamCallbacks[t](
                        self.effectsById[effectId], param, value)
                    if r:
                        if r == "pause":
                            self.pause()
                        self.setProperty(
                            self.effectsById[effectId], param, value)
                        if r == "pause":
                            self.play()

                else:
                    fx = self.effectsById[effectId]
                    if param.startswith("preSupport."):
                        v = param.split(".", 2)
                        fx = fx.preSupports[int(v[1])]
                        param = v[2]

                    if param.startswith("postSupport."):
                        v = param.split(".", 2)
                        fx = fx.postSupports[int(v[1])]
                        param = v[2]

                    self.setProperty(fx, param, value)
        return value

    def addLevelDetector(self):
        self.levelDetector = self.addElement("level", post_messages=True,
                                             peak_ttl=300 * 1000 * 1000, peak_falloff=60, interval=10**9 / 24)

    def onLevelMessage(self, src, rms, level):
        if self.board:
            rms = max(rms, -90)
            self.levelTag.value = rms
            self.doSoundFuse(rms)
            if level < -45 or abs(level - self.lastLevel) < 6:
                if time.monotonic() - self.lastPushedLevel < 0.3:
                    return True
            else:
                if time.monotonic() - self.lastPushedLevel < 0.07:
                    return True
            level = max(round(level, 2), -99)
            self.board.channels[self.name]['level'] = level
            self.lastPushedLevel = time.monotonic()
            self.lastLevel = level
            self.board.pushLevel(self.name, level)

    def onSTTMessage(self, v):
        messagebus.postMessage("/system/mixer/channels/" + self.name + "/stt/hypothesis",
                               (v,))

    def onSTTMessageFinal(self, v):
        messagebus.postMessage("/system/mixer/channels/" +
                               self.name + "/stt/final", (v,))

    def doSoundFuse(self, rms):
        # Highly dynamic stuff is less likely to be feedback.
        # Don't count feedback if there's any decrease
        # Aside from very small decreases, there's likely other stuff happening too.
        # But if we are close to clipping ignore that
        if not (((self.lastRMS < (rms + 1.5)) or rms > -2) and rms > self.soundFuseSetting):
            self.lastNormalVolumeLevel = time.monotonic()
        self.lastRMS = rms
        if time.monotonic() - self.lastNormalVolumeLevel > 0.2:
            # self.loudnessAlert.trip()
            self.alertRatelimitTime = time.monotonic()

            if not self.doingFeedbackCutoff:
                self.doingFeedbackCutoff = True

                def f():
                    "Greatly reduce volume, then slowly increase it, till the user does something about it"
                    try:
                        print("FEEDBACK DETECTED!!!!")
                        l = self.faderTag.value
                        self.setFader(l - 18)
                        c = self.faderTag.value
                        time.sleep(0.25)
                        # Detect manual action
                        if not c == self.faderTag.value:
                            return
                        t = 18

                        time.sleep(2.5)
                        # Detect manual action
                        if not c == self.faderTag.value:
                            return

                        # Slowly go back up

                        while t:
                            t -= 1
                            self.setFader(l - t)
                            c = self.faderTag.value

                            # Wait longer on that last one before we exit,
                            # To ensure it worked
                            if(t == 1):
                                time.sleep(1)
                            time.sleep(0.1)
                            # Detect manual action
                            if not c == self.faderTag.value:
                                return

                            if(self.levelTag.value > self.soundFuseSetting - 3):
                                t += 3
                                self.setFader(l - t)
                                c = self.faderTag.value

                                time.sleep(1)
                                # Detect manual action
                                if not c == self.faderTag.value:
                                    return

                        # Return to normal
                        self.setFader(l)
                    finally:
                        print("FEEDBACK HANDLER EXIT")
                        self.doingFeedbackCutoff = False
                workers.do(f)

        elif time.monotonic() - self.alertRatelimitTime > 10:
            # self.loudnessAlert.release()
            self.alertRatelimitTime = time.monotonic()

    def _faderTagHandler(self, level, t, a):
        # Note: We don't set the configured data fader level here.
        if self.fader:
            if self.mute:
                self.fader.set_property('volume', 0)
            else:
                if level > -60:
                    self.fader.set_property('volume', 10**(float(level) / 20))
                else:
                    self.fader.set_property('volume', 0)

        self.board.api.send(['fader', self.name, level])

    def setFader(self, level):
        # Let the _faderTagHandler handle it.
        self.faderTag.value = level

    def setMute(self, m):
        self.mute = m
        self._faderTagHandler(self.faderTag.value, 0, 0)
        self.board.api.send(['mute', self.name, self.mute])

class ChannelInterface():
    def __init__(self, name, effectData={}, mixingboard=None):
        if not mixingboard:
            mixingboard = board
        self.channel = board.createChannel(name, effectData)

    def fader(self):
        return self.board.getFader()

    def __del__(self):
        board.deleteChannel(self.name)

def checkIfProcessRunning(processName):
    '''
    Check if there is any running process that contains the given name processName.
    '''
    try:
        import psutil
    except Exception:
        return False

    # Iterate over the all the running process
    for proc in psutil.process_iter():
        try:
            # Check if process name contains the given name string.
            if processName.lower() in proc.name().lower():
                return True
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            pass
    return False

lastAutoReload = [0]

actionLockout = {}

class MixingBoard():
    def __init__(self, *args, **kwargs):
        self.api = widgets.APIWidget()
        self.api.require("/users/mixer.edit")
        self.api.attach(self.f)
        self.channels = {}
        self.channelObjects = {}
        self.channelAlerts = {}
        self.lock = threading.RLock()
        self.channelStatus = {}
        self.running = checkIfProcessRunning("pipewire") or checkIfProcessRunning("jackd")

        def f(t, v):
            self.running = True
            self.reload()

        messagebus.subscribe("/system/jack/started", f)
        self.reloader = f
        self.loadedPreset = "default"

    def loadData(self, d):
        with self.lock:
            self._loadData(d)

    def reload(self):
        self.loadData(self.channels)

    def _loadData(self, x):
        # Raise an error if it can't be serialized
        json.dumps(x)
        if not isinstance(x, dict):
            raise TypeError("Data must be a dict")

        self.channels = x
        if not self.running:
            return
        for i in self.channels:
            log.info("Creating mixer channel " + i)
            try:
                self._createChannel(i, self.channels[i])
            except Exception as e:
                messagebus.postMessage(
                    "/system/notifications/errors", "Failed to create mixer channel " + i + " see logs.")
                log.exception("Could not create channel " + i)
                self.pushStatus(i, "error "+str(e))

    def sendState(self):
        with self.lock:
            inPorts = jackmanager.getPortNamesWithAliases(
                is_audio=True, is_input=True)
            outPorts = jackmanager.getPortNamesWithAliases(
                is_audio=True, is_output=True)
            midiOutPorts = jackmanager.getPortNamesWithAliases(
                is_midi=True, is_output=True)
            midiInPorts = jackmanager.getPortNamesWithAliases(
                is_midi=True, is_input=True)

            self.api.send(['inports', {i: {} for i in inPorts}])
            self.api.send(['outports', {i: {} for i in outPorts}])
            self.api.send(['midiinports', {i: {} for i in midiInPorts}])
            self.api.send(['midioutports', {i: {} for i in midiOutPorts}])

            self.api.send(['channels', self.channels])

            for i in self.channels:
                self.pushStatus(i)

            self.api.send(['effectTypes', effectTemplates])

            self.api.send(['loadedPreset', self.loadedPreset])

            self.sendPresets()

            if recorder:
                self.api.send(['recordingStatus', "recording"])
            else:
                self.api.send(['recordingStatus', "off"])

    def sendPresets(self):
        if os.path.isdir(presetsDir):
            x = [i[:-len('.yaml')]
                 for i in os.listdir(presetsDir) if i.endswith('.yaml')]
        else:
            x = []
        self.api.send(['presets', x])

    def createChannel(self, name, data={}):
        with self.lock:
            if name in self.channelObjects:
                self.channelObjects[name].stop()
            self._createChannel(name, data)

    def _createChannel(self, name,data=channelTemplate):

        self.pushStatus(name, "loading")

        if not name in self.channelAlerts:
            self.channelAlerts[name] = alerts.Alert("Mixer channel "+name, priority='error', tripDelay=35, autoAck=True)

        for i in range(3):
            try:
                self._createChannelAttempt(name, data, wait=(3**i + 4))
                self.channelAlerts[name].release()
                self.pushStatus(name, "running")
                break
            except Exception as e:
                self.channelAlerts[name].trip("Failed to load channel")
                logging.exception("Failed to create channel, retrying")
                self.pushStatus(name, "failed " +str(e))
                time.sleep(1)

    def _createChannelAttempt(self, name, data=channelTemplate,wait=3):
        "Create a channel given the name and the data for it"
        self.channels[name] = data
        self.api.send(['channels', self.channels])
        for i in self.channels:
            self.pushStatus(i)
        self.pushStatus(name,'loading')

        if not self.running:
            if not (checkIfProcessRunning("pipewire") or checkIfProcessRunning("jackd")):
                return
            else:
                self.running = True

        if 'type' not in data or data['type'] == "audio":
            backup = []
            if name in self.channelObjects:
                self.pushStatus(name,'stopping old')

                backup = self.channelObjects[name].backup()
                try:
                    self.channelObjects[name].stop()
                    time.sleep(1)
                except Exception:
                    log.exception(
                        "Error stopping old channel with that name, continuing")

            self.pushStatus(name,'loading')

            time.sleep(0.01)
            time.sleep(0.01)
            time.sleep(0.01)
            time.sleep(0.01)
            time.sleep(0.01)

            p = ChannelStrip(name, board=self, channels=data.get(
                'channels', 2), soundFuse=data.get('soundFuse', 3), input=data.get('input'))
            self.channelObjects[name] = p
            p.fader = None
            p.loadData(data)
            p.addLevelDetector()
            p.finalize(wait)
            p.connect(restore=backup)
            self.pushStatus(name,'running')

    def deleteChannel(self, name):
        with self.lock:
            self._deleteChannel(name)

    def _deleteChannel(self, name):
        if name in self.channels:
            del self.channels[name]
        if name in self.channelAlerts:
            self.channelAlerts[name].release()
            del self.channelAlerts[name]
        if name in self.channelObjects:
            self.channelObjects[name].stop()
            del self.channelObjects[name]
        self.api.send(['channels', self.channels])
        for i in self.channels:
            self.pushStatus(i)

    def pushLevel(self, cn, d):
        self.api.send(['lv', cn, round(d, 2)])

    def pushStatus(self, cn, s=None):
        if s:
            self.channelStatus[cn] = s
        else:
            s = self.channelStatus.get(cn, "not running")

        self.api.send(['status', cn, s])

    def setFader(self, channel, level):
        "Set the fader of a given channel to the given level"
        if not self.running:
            return
        with self.lock:
            c = self.channelObjects[channel]
            c.setFader(level)

            # Push the real current value, not just repeating what was sent
            self.api.send(['fader', channel, c.faderTag.value])

            if c.faderTag.currentSource == 'default':
                self.channels[channel]['fader'] = float(level)

    def savePreset(self, presetName):
        if not presetName:
            raise ValueError("Empty preset name")
        with self.lock:
            util.disallowSpecialChars(presetName)
            persist.save(self.channels, os.path.join(
                presetsDir, presetName + ".yaml"))

            self.loadedPreset = presetName
            self.api.send(['loadedPreset', self.loadedPreset])

    def deletePreset(self, presetName):
        if os.path.exists(os.path.join(presetsDir, presetName + ".yaml")):
            os.remove(os.path.join(presetsDir, presetName + ".yaml"))

    def loadPreset(self, presetName: str):
        with self.lock:
            x = list(self.channels)
            for i in x:
                self._deleteChannel(i)

            if os.path.isfile(os.path.join(presetsDir, presetName + ".yaml")):
                self._loadData(persist.load(
                    os.path.join(presetsDir, presetName + ".yaml")))
            else:
                logging.error("No such preset "+ str(presetName))

            self.loadedPreset = presetName
            self.api.send(['loadedPreset', self.loadedPreset])

    def f(self, user, data):
        def f2():
            global recorder
            if data[0] == 'refresh':
                self.sendState()

            if data[0] == 'test':
                from . import sound
                sound.oggSoundTest(output=data[1])

            if data[0] == 'addChannel':
                # No overwrite
                if data[1] in self.channels:
                    return
                # No empty names
                if not data[1]:
                    return
                util.disallowSpecialChars(data[1])
                c = copy.deepcopy(channelTemplate)
                c['channels'] = data[2]
                self.createChannel(data[1], c)

            if data[0] == 'setEffects':
                "Directly set the effects data of a channel"
                with self.lock:
                    self.channels[data[1]]['effects'] = data[2]
                    self.api.send(['channels', self.channels])
                    for i in self.channels:
                        self.pushStatus(i)
                    self._createChannel(data[1], self.channels[data[1]])

            if data[0] == 'setFuse':
                self.channels[data[1]]['soundFuse'] = float(data[2])
                self.channelObjects[data[1]].soundFuseSetting = float(data[2])

            if data[0] == 'setInput':
                self.channels[data[1]]['input'] = data[2]
                if not self.running:
                    return
                self.channelObjects[data[1]].setInput(data[2])

            if data[0] == 'setMute':
                self.channels[data[1]]['mute'] = bool(data[2])
                if not self.running:
                    return
                self.channelObjects[data[1]].setMute(data[2])

            if data[0] == 'setOutput':
                self.channels[data[1]]['output'] = data[2]

                if not self.running:
                    return
                self.channelObjects[data[1]].setOutputs(data[2].split(","))

            if data[0] == 'setFader':
                "Directly set the effects data of a channel"
                self.setFader(data[1], data[2])

            if data[0] == 'setParam':
                "Directly set the effects data of a channel. Packet is channel, effectID, paramname, val"

                for i in self.channels[data[1]]['effects']:
                    if i['id'] == data[2]:
                        i['params'][data[3]]['value'] = data[4]
                if not self.running:
                    return
                self.channelObjects[data[1]].setEffectParam(
                    data[2], data[3], data[4])
                self.api.send(['param', data[1], data[2], data[3], data[4]])

                if[data[3]] == "bypass":
                    self._createChannel(data[1], self.channels[data[1]])

            if data[0] == 'addEffect':
                with self.lock:
                    fx = copy.deepcopy(effectTemplates[data[2]])

                    fx['id'] = str(uuid.uuid4())
                    self.channels[data[1]]['effects'].append(fx)
                    self.api.send(['channels', self.channels])
                    for i in self.channels:
                        self.pushStatus(i)
                    self._createChannel(data[1], self.channels[data[1]])

            if data[0] == 'refreshChannel':
                # disallow spamming
                if not actionLockout.get(data[1],0) > time.monotonic() -10:
                    actionLockout[data[1]] = time.monotonic()
                    self._createChannel(data[1], self.channels[data[1]])
                    actionLockout.pop(data[1],None)

            if data[0] == 'rmChannel':
                self.deleteChannel(data[1])

            if data[0] == 'savePreset':
                self.savePreset(data[1])
                self.sendPresets()

            if data[0] == 'loadPreset':
                self.loadPreset(data[1])

            if data[0] == 'deletePreset':
                self.deletePreset(data[1])
                self.sendPresets()

            if data[0] == 'record':
                with self.lock:
                    if not recorder:
                        try:
                            recorder = Recorder(
                                pattern=data[1], channels=int(data[2]))
                            recorder.start()
                        except Exception as e:
                            self.api.send(['recordingStatus', str(e)])
                            raise

                    self.api.send(['recordingStatus', "recording"])

            if data[0] == 'stopRecord':
                with self.lock:
                    try:
                        recorder.sendEOS()
                    except Exception:
                        pass

                    try:
                        recorder.stop()
                    except Exception:
                        pass
                    recorder = None
                    self.api.send(['recordingStatus', "off"])
        workers.do(f2)

board = MixingBoard()

try:
    board.loadPreset('default')
except Exception:
    messagebus.postMessage("Could not load default preset for JACK mixer")
    log.exception(
        "Could not load default preset for JACK mixer. Maybe it doesn't exist?")


from django.shortcuts import render
from django.views.generic import TemplateView, FormView, UpdateView, ListView
from django.contrib import messages
from django.core.files.storage import FileSystemStorage
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.db.models import Q
from django.shortcuts import render, HttpResponse, redirect
from el_pagination.views import AjaxListView
from el_pagination.decorators import page_template
from argus.models import ArgusADSL, ArgusFTTx, ArgusGPON, ASTU, Client
from argus.forms import ArgusFileUploadForm, ArgusSearchForm, ASTUSearchForm
from argus.argus_lib import parse_adsl_csv, parse_fttx_csv, parse_gpon_csv, parse_astu_csv, ip_pattern
import re

# Create your views here.
# Загрузка из CSV файла
class ClientImport(LoginRequiredMixin, TemplateView):
    template_name = 'argus/adsl.html'

    def get(self, request, *args, **kwargs):
        form = ArgusFileUploadForm()
        args = {'form': form}
        return render(request, self.template_name, args)

    def post(self, request):
        form = ArgusFileUploadForm(request.POST, request.FILES)
        if form.is_valid():
            uploaded_file = request.FILES['file']
            fs = FileSystemStorage()
            filename = fs.save(uploaded_file.name, uploaded_file)
            file_path = fs.url(filename)
            messages.add_message(request, messages.INFO, 'File was uploaded to '+file_path)
            # Check tech | технология подключения
            tech = form.cleaned_data['tech']
            counter = '0'
            ignored = '0'
            if tech == '1':
                counter = parse_adsl_csv(file_path)
            elif tech == '2':
                counter, ignored = parse_gpon_csv(file_path)
            elif tech == '3':
                counter, ignored = parse_fttx_csv(file_path)
            elif tech == '4':
                counter, ignored = parse_astu_csv(file_path)
            else:
                messages.add_message(request, messages.ERROR, 'Выбрана непонятная технология включения')

            messages.add_message(request, messages.INFO,
                                 'Добавлено ' + str(counter) + ' записей. Проигнорировано - ' + str(ignored))
        else:
            messages.add_message(request, messages.ERROR, "Ошибка")
            messages.add_message(request, messages.ERROR, form.errors)
            pass

        # New Form
        form = ArgusFileUploadForm()
        args = {'form': form}
        return render(request, self.template_name, args)

# Lists NE from ASTU
class ASTUView(LoginRequiredMixin, FormView, ListView):
    context_object_name = 'astu_list'
    template_name = 'argus/astu.html'
    form_class = ASTUSearchForm
    success_url = '/argus/astu'
    model = ASTU

    def get_queryset(self):
        if self.request.method == 'POST':
            form = ASTUSearchForm(self.request.POST)
            if form.is_valid():
                input_string = form.cleaned_data['input_string']
                status = form.cleaned_data['status']
                vendor = form.cleaned_data['vendor']
                model = form.cleaned_data['model']
                segment = form.cleaned_data['segment']

                search_objects = ASTU.objects.all()

                if input_string:
                    if re.findall(ip_pattern, input_string):
                        search_objects = ASTU.objects.filter(ne_ip=input_string)
                    else:
                        search_objects = ASTU.objects.filter(
                            Q(hostname__icontains=input_string) |
                            Q(address__icontains=input_string)
                        )
                if status:
                    search_objects = search_objects.filter(status=status)
                if vendor:
                    search_objects = search_objects.filter(vendor=vendor)
                if model:
                    search_objects = search_objects.filter(model=model)
                if segment:
                    search_objects = search_objects.filter(segment=segment)
                return search_objects
            else:
                messages.add_message(self.request, messages.ERROR, form.errors)

        return ASTU.objects.all().filter(status__exact='эксплуатация')[:5]

    def post(self, request, *args, **kwargs):
        return self.get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(ASTUView, self).get_context_data(**kwargs)
        # context['fluid_container'] = True
        context['row_count'] = self.get_queryset().count() or 0
        return context

class SearchView(LoginRequiredMixin, AjaxListView, FormView):
    template_name = 'argus/search_view.html'
    form_class = ArgusSearchForm
    success_url = '/argus/find'
    page_template = 'argus/search_view_list_page.html'

    def get_queryset(self):
        return None

    # OMFG!!!
    def post(self, request, *args, **kwargs):
        return self.get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(SearchView, self).get_context_data(**kwargs)
        context['page_template'] = self.page_template
        if self.request.method == 'POST':
            context['search'] = self.request.POST['input_string']
            context['results'] = self.get_results(self.request.POST['input_string'])
            context['form'] = ArgusSearchForm(self.request.POST)
        else:
            context['search'] = self.request.GET.get('search')
            context['results'] = self.get_results(self.request.GET.get('search'))
            context['form'] = ArgusSearchForm()
        context['fluid_container'] = True
        context['gpon_page'] = 'gpon_page'
        return context

    def get_results(self, input_string=None):
        search_objects = Client.objects.all().order_by('id')
        count = 15
        if input_string:
            if re.findall(ip_pattern, input_string):
                search_objects = search_objects.filter(ne_ip=input_string)
                count = search_objects.count()
            else:
                search_objects = search_objects.filter(
                    Q(inet_login__contains=input_string) |
                    Q(iptv_login__contains=input_string) |
                    Q(tel_num__contains=input_string) |
                    Q(address__icontains=input_string) |
                    Q(fio__icontains=input_string) |
                    Q(hostname__icontains=input_string)
                )
                count = search_objects.count()
            return {'search_objects': search_objects, 'count': count}
        return {'search_objects': search_objects[:15], 'count': count}


from tensorflow import keras
from keras.preprocessing.image import ImageDataGenerator, load_img,img_to_array
from keras.models import Sequential, Model
from keras.layers import Dense,Flatten,Dropout,Input
from keras.layers.convolutional import Conv2D,MaxPooling2D
import keras.layers as layers
from keras.applications import MobileNetV2
from keras.applications.mobilenet_v2 import decode_predictions, preprocess_input
from keras.models import load_model
from keras import backend as K
import cv2
import numpy as np

train_datagen = ImageDataGenerator(
    rescale=1.0/255.0,
    shear_range = 0.3,
    zoom_range = 0.3,
    horizontal_flip=True,
    preprocessing_function=preprocess_input
)

train_flow = train_datagen.flow_from_directory(
    './data/train/', #replace this path to your own dataset
    target_size=(224,224),
    batch_size=32,
    class_mode='categorical'
)

test_datagen = ImageDataGenerator(
    rescale=1.0/255.0,
    horizontal_flip=False
)

test_flow = test_datagen.flow_from_directory(
    './data/test/',
    target_size=(224,224),
    batch_size=32,
    class_mode='categorical'
)

conv_base = MobileNetV2(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
conv_base.trainable = False
model = keras.Sequential()
model.add(conv_base)
model.add(layers.GlobalAveragePooling2D())
model.add(layers.Flatten())
model.add(Dropout(0.25))

model.add(layers.Dense(100, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dense(52, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dense(26, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit_generator(train_flow, epochs=10)

model.save('./saved_model/MobileNetV2')
scores = model.evaluate(test_flow)
print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100))


from langchain.embeddings import HuggingFaceEmbeddings
from langchain.retrievers import TimeWeightedVectorStoreRetriever
from langchain.vectorstores import FAISS
from langchain.docstore import InMemoryDocstore
from langchain.schema import Document
from typing import Any, Dict, List, Optional, Tuple
import faiss
import datetime
import os
import _pickle as cPickle
import bz2

#model_name = "intfloat/e5-base-v2"
model_name = "models/e5-base-v2"
model_kwargs = {'device': 'cpu'}
EMBEDDING_MODEL = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs)
# Initialize the vectorstore as empty
embedding_size = 768

def _get_hours_passed(time: datetime, ref_time: datetime) -> float:
    """Get the hours passed between two datetime objects."""
    return (time - ref_time).total_seconds() / 3600

class TimeWeightedVectorStoreRetriever_custom(TimeWeightedVectorStoreRetriever):
    def _get_combined_score(
            self,
            document: Document,
            vector_relevance: Optional[float],
            current_time: datetime,
        ) -> float:
        """Return the combined score for a document."""
        hours_passed = _get_hours_passed(
            current_time,
            document.metadata["last_accessed_at"],
        )
        #Note: We can change the above 'last_accessed_at' above to 'created_at' to rank memory based on when it was created (rather than when it was last accessed in the Langchain default implementation)
        #https://github.com/hwchase17/langchain/blob/85dae78548ed0c11db06e9154c7eb4236a1ee246/langchain/retrievers/time_weighted_retriever.py#L119

        score = (1.0 - self.decay_rate) ** hours_passed
        # print(f'score contributed by time: {score}')
        for key in self.other_score_keys:
            if key in document.metadata:
                if key != 'importance':
                  score += document.metadata[key]
                else:
                  score += int(document.metadata[key])/10.
                #   print(f'score contributed by importance: {int(document.metadata[key])/10.}')

        if vector_relevance is not None:
            score += vector_relevance
            # print(f'score contributed by vector relevance: {vector_relevance}')

        # print(f'total score: {score}')
        # print('------------')
        return score

def prepare_memory_object(timestamp, lastAccess, vector, importance):
  memoryObject = {
        "timestamp": timestamp,
        "lastAccess": lastAccess,
        "vector": vector,
        "importance": importance
    }
  return memoryObject

def initialize_vector_memory():
  index = faiss.IndexFlatL2(embedding_size)
  vectorstore = FAISS(EMBEDDING_MODEL.embed_query, index, InMemoryDocstore({}), {})
  retriever = TimeWeightedVectorStoreRetriever_custom(vectorstore=vectorstore, other_score_keys = ['importance'] , decay_rate=.01, k=1)
  return retriever

def load_vector_memory(root = '/content/vector_mems', mem_name = 'dialogue'):
  path = os.path.join(root, mem_name) + '.pbz2'
  assert os.path.exists(path)
  data = bz2.BZ2File(path, "rb")
  retriever = cPickle.load(data)
  return retriever

def save_vector_memory(retriever, root = '/content/vector_mems', mem_name = 'dialogue'):
  path = os.path.join(root, mem_name) + '.pbz2'
  with bz2.BZ2File(path, "w") as f:
    cPickle.dump(retriever, f)
  return

def retrieve(query, retriever, top_k = 3):
  retriever.k = top_k
  retrieved_docs = retriever.get_relevant_documents(query)
  retrieved_docs_str = [('- ' + doc.page_content) for doc in retrieved_docs]
  return '\n'.join(retrieved_docs_str)


import globals as gb
from globals import *
import datetime
from backtrader.stores import ibstore as store
from samples.ibtest.ibtest import TestStrategy

class St(bt.Strategy):
    def start(self):
        print('strategy is running')

    def logdata(self):
        txt = []
        txt.append('{}'.format(len(self)))

        txt.append('{}'.format(
            self.data.datetime.datetime(0).isoformat())
        )
        txt.append('{:.2f}'.format(self.data.open[0]))
        txt.append('{:.2f}'.format(self.data.high[0]))
        txt.append('{:.2f}'.format(self.data.low[0]))
        txt.append('{:.2f}'.format(self.data.close[0]))
        txt.append('{:.2f}'.format(self.data.volume[0]))
        print(','.join(txt))

    def next(self):
        self.logdata()

    def notify_data(self, data, status, *args, **kwargs):
        print(f'new data recieved: {data} {status}')

stockkwargs = dict(
    timeframe=bt.TimeFrame.Days,
    rtbar=False,  # use RealTime 5 seconds bars
    historical=True,  # only historical download
    qcheck=0.5,  # timeout in seconds (float) to check for events
    fromdate=datetime.datetime(2019, 4, 20),  # get data from..
    todate=datetime.datetime(2019, 4, 30),  # get data from..
    latethrough=False,  # let late samples through
    tradename=None  # use a different asset as order target
)

def backtest():
    global cerebro
    cerebro = gb.cerebro
    cerebro.addstrategy(TestStrategy)
    from backtest import add_data
    add_data(limit=0, random=False, start_date=datetime(2016,11,30), end_date=datetime(2021, 4, 26), dirpath='data_feeds', stock_names=['ABC.csv'])
    backfill(cerebro.datas)
    global strategies
    strategies = backtest()
    merge_data(cerebro.datas)
    pass

def backfill(datas):
    global cerebro
    store = bt.stores.IBStore(port=7497, notifyall=False, _debug=True)
    for data in datas:
        from_date = find_end_date(data)
        live_data = store.getdata(dataname=data._name+'-STK-SMART-USD', fromdate=from_date, todate=datetime.datetime.today(), historical=True, timeframe=bt.TimeFrame.Days)
        cerebro.adddata(live_data)

def merge_data(datas):
    for data in datas:
        data.roll
        pass

def run(args=None):
    cerebro = bt.Cerebro(stdstats=False)
    global store
    store = bt.stores.IBStore(port=7497, notifyall=False, _debug=True)
    cerebro.addstrategy(TestStrategy)
    # quote('BAX', 'CDE', 'CAD', 'FUT') # got result (contract) but no len
    # quote('TSX', 'CDE', 'CAD', 'FUT') # got result (contract) but no len

    disk_data = bt.feeds.GenericCSVData(dataname='data_feeds/NVDA.csv', fromdate=datetime.datetime(2019, 4, 20), todate=datetime.datetime(2019,4,26), dtformat='%Y-%m-%d', high=1, low=2, open=3, close=4, volume=5)
    data = store.getdata(dataname='NVDA-STK-SMART-USD', **stockkwargs, backfill_from=disk_data,)
    # cerebro.replaydata(data, timeframe=bt.TimeFrame.Days)
    cerebro.adddata(data, name='A')
    # cerebro.adddata(disk_data, name='A')
    # cerebro.resampledata(data, timeframe=bt.TimeFrame.Days, compression=1)
    # store.start()
    cerebro.run()
    print(len(cerebro.datas[0]))
    # print(len(cerebro.datas[1]))
    print(len(cerebro.datas))
    pass

def run_no_backfill():
    cerebro = bt.Cerebro(stdstats=False)
    istore = bt.stores.IBStore(port=7497, notifyall=False, _debug=True)
    exchanges =['SMART','AMEX','NYSE','CBOE','PHLX','ISE','CHX','ARCA','ISLAND','DRCTEDGE',
                'BEX','BATS','EDGEA','CSFBALGO','JEFFALGO','BYX','IEX','EDGX','FOXRIVER','PEARL','NYSENAT','LTSE','MEMX','PSX']
    for exc in exchanges:
        data = istore.getdata(dataname=f'ZION-STK-{exc}-USD', fromdate=datetime.datetime(2021,11,16), todate=datetime.datetime(2021,11,23), historical=True)
        cerebro.adddata(data)
    cerebro.addstrategy(TestStrategy)
    strat = cerebro.run()[0]
    pass

# %%

def quote(symbol, exchange, currency='USD', type='STK'):
    if cerebro.datas:
        cerebro.datas=[]
    query = f'{symbol}-{type}-{exchange}-{currency}'
    print ('queying '+query)
    data = store.getdata(dataname=query, **stockkwargs)
    cerebro.resampledata(data, timeframe=bt.TimeFrame.Days, compression=1)
    cerebro.adddata(data)
    cerebro.run()
    return cerebro.datas[0]

if __name__ == '__main__':
    run_no_backfill()


#import yt
import numpy as np
from soxs.thermal_spectra import CIEGenerator
import matplotlib.pyplot as plt
from pyxsim.spectral_models import SpectralInterpolator1D

'''
Initial parameters of the spectral model /see thermal_sources.py/
'''
model = "apec"
emin = 6.0
emax = 12.0
nbins = 100
binscale = "linear"  # or "log"
var_elem = None
model_root = None
model_vers = None
thermal_broad = True
nolines = False
abund_table = "angr"
nei = False  # don't use the non-equilibrium ionization tables
#***
kT_min = 0.025
kT_max = 64.0

cgen = CIEGenerator(
            model,
            emin,
            emax,
            nbins,
            binscale=binscale,
            var_elem=var_elem,
            model_root=model_root,
            model_vers=model_vers,
            broadening=thermal_broad,
            nolines=nolines,
            abund_table=abund_table,
            nei=nei,
        )

zobs = 0.0

idx_min = max(np.searchsorted(cgen.Tvals, kT_min) - 1, 0)
idx_max = min(np.searchsorted(cgen.Tvals, kT_max) + 1, cgen.nT - 1)

Tvals = cgen.Tvals[idx_min : idx_max]

cosmic_spec, metal_spec, var_spec = cgen._get_table(
            list(range(idx_min, idx_max)), zobs, 0.0
        )

si = SpectralInterpolator1D(
            Tvals, cosmic_spec, metal_spec, var_spec
        )

kTi = 10**(5 + 3*np.random.rand(10)) #kT[ibegin:iend]
cspec, mspec, vspec = si(kTi)

#kTi

'''
Thermal emission spectrum returned by get_spectrum function
'''

tot_spec = cspec


import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

path = "ex2.txt"

data = pd.read_csv(path,names=['size','bedrooms','price'])

#   特征归一化。 这个对于pandas来说很简单
data = (data - data.mean()) / data.std()
data.insert(0,'ones',1)
print(data.head())

cols = data.shape[1]
m = data.shape[0]
x = data.iloc[:,0:cols-1]
y = data.iloc[:,cols-1:cols]

def computeCost(x,y,theta):
    inner = np.power((x * theta.T - y), 2)
    return np.sum(inner) / (2 * m)

x = np.matrix(x)
y = np.matrix(y)
print(x.shape[1])
theta = np.matrix(np.zeros(x.shape[1]))  # theta就是一个（1,cols-1）矩阵
print(theta)

def gradientDescentMulti(x, y, theta, alpha, iters):
    temp = np.zeros(theta.shape[1])
    cost = np.zeros(iters)    #cost.shape = (iters)而不是（1，iters),是向量

    for i in range(iters):
        temp = theta - (alpha / m) * (x * theta.T - y).T * x
        theta = temp

        cost[i] = computeCost(x, y, theta)  #如果cost.shape=(1,iters),此处应为cost[0,i]

    return theta,cost

alpha = 0.01
iters = 1000
final_theta , cost = gradientDescentMulti(x,y,theta,alpha,iters)
final_cost = computeCost(x,y,final_theta)
print(f'final_theta={final_theta}')
print(f'final_cost={final_cost}')
print(len(cost))

fig, ax = plt.subplots(figsize=(8,5))
ax.plot(np.arange(iters), cost,'r')
ax.set_xlabel('Iterations')
ax.set_ylabel('Cost')
ax.set_title('Error vs. Training Epoch')
plt.show()


from . import Entity
from PIL import Image
import pygame

class Boss(Entity):
    def __init__(self, pos):
        self.alive = True
        super().__init__(pos)
        self.img = Image.open('assets/animations/boss.png')
        self.img = self.img.resize((self.img.width*4, self.img.height*4), 0)
        self.img.save('AAA.png')
        self.imgs = pygame.image.load('AAA.png')

    def update(self, player):
        if player.bar >= 800:
            self.alive = False
            player.bar = 0

    def draw(self, win):
        win.blit(self.imgs, (self.x-(self.img.width//2),
                 self.y-(self.img.height//2)))


# global
import ivy
import pytest
import numpy as np

# local
from ivy_vision import implicit as ivy_imp
from ivy_vision_tests.data import TestData

class ImplicitTestData(TestData):
    def __init__(self):
        super().__init__()

        # set framework
        ivy.set_backend("numpy")

        # sampled pixel coords
        self.samples_per_dim = [9, 12]

        # stratified sampling
        self.start_vals = np.array([0, 1, 2], np.float32)
        self.end_vals = np.array([10, 21, 7], np.float32)

        # render rays via quadrature rule
        self.radial_depths = np.array(
            [[0, 1, 2, 3], [1, 1.5, 2, 2.5], [2, 4, 5, 6]], np.float32
        )
        self.inter_sample_distances = (
            self.radial_depths[..., 1:] - self.radial_depths[..., :-1]
        )
        self.features = np.array(
            [
                [[0.0, 0.5, 0.7], [1.0, 0.2, 0.3], [0.1, 0.2, 0.3]],
                [[0.3, 0.2, 0.1], [0.5, 0.7, 0.3], [0.1, 0.9, 0.7]],
                [[0.6, 0.4, 0.2], [0.9, 0.3, 0.6], [0.4, 0.8, 0.5]],
            ],
            np.float32,
        )
        self.densities = np.array(
            [[0.3, 2.4, 0.4], [0.2, 0.7, 0.4], [0.1, 0.3, 1.2]], np.float32
        )

        self.occ_probs = np.array(
            [
                [0.2591818, 0.9092821, 0.32967997],
                [0.09516257, 0.29531187, 0.18126929],
                [0.18126929, 0.2591818, 0.6988058],
            ],
            np.float32,
        )
        self.ray_term_probs = np.array(
            [
                [0.2591818, 0.6736127, 0.0221563],
                [0.09516257, 0.26720923, 0.11558241],
                [0.18126929, 0.21220009, 0.4238471],
            ],
            np.float32,
        )
        self.term_prob_feature_rendering = np.array(
            [
                [0.67582834, 0.2687447, 0.39015797],
                [0.17371163, 0.31010312, 0.1705867],
                [0.46928048, 0.47524542, 0.37549746],
            ],
            np.float32,
        )
        self.term_prob_var_rendering = np.array(
            [
                [0.18848504, 0.03828975, 0.00651178],
                [0.01524993, 0.12262806, 0.12084134],
                [0.02295334, 0.03439996, 0.01307792],
            ],
            np.float32,
        )

        # render implicit features and depth
        self.implicit_fn = lambda pts, feat, timestamps, v=None: (
            ivy.array(self.features),
            ivy.array(self.densities),
        )
        self.rays_o = np.array([0, 0, 0], np.float32)
        self.rays_d = np.array([[1, 2, 3], [-1, -2, -3], [1, -2, 1]], np.float32)
        self.near = np.array([0.5, 0.7, 0.9], np.float32)
        self.far = np.array([6, 7, 8], np.float32)
        self.samples_per_ray = 3
        self.timestamps = np.array([0], np.float32)
        self.inter_feat_fn = lambda x: x**2

        # render implicit features and depth from net inputs
        batch_shape = list(self.rays_o.shape[:-1])
        num_batch_dims = len(batch_shape)
        ray_batch_shape = list(self.rays_d.shape[num_batch_dims:-1])
        num_ray_batch_dims = len(ray_batch_shape)
        self.z_vals = ivy.expand_dims(
            ivy_imp.stratified_sample(self.near, self.far, 3), axis=-1
        )
        rays_d = ivy.expand_dims(self.rays_d, axis=-2)
        rays_o = ivy.broadcast_to(
            ivy.reshape(
                self.rays_o, batch_shape + [1] * (num_ray_batch_dims + 1) + [3]
            ),
            rays_d.shape,
        )
        self.query_points = rays_o + rays_d * self.z_vals

        # unset framework
        ivy.previous_backend()

td = ImplicitTestData()

def test_downsampled_image_dims_from_desired_num_pixels(device, fw):
    ivy.set_backend(fw)
    new_img_dims, num_pixels = ivy_imp.downsampled_image_dims_from_desired_num_pixels(
        [32, 32], 256
    )
    assert new_img_dims == [16, 16]
    assert num_pixels == 256
    new_img_dims, num_pixels = ivy_imp.downsampled_image_dims_from_desired_num_pixels(
        [14, 18], 125
    )
    assert new_img_dims == [10, 13]
    assert num_pixels == 130
    new_img_dims, num_pixels = ivy_imp.downsampled_image_dims_from_desired_num_pixels(
        [14, 18], 125, maximum=True
    )
    assert new_img_dims == [9, 12]
    assert num_pixels == 108
    ivy.previous_backend()

def test_create_sampled_pixel_coords_image(device, fw):
    if fw == "mxnet":
        # MXNet does not support clipping based on min or max specified as arrays
        pytest.skip()
    ivy.set_backend(fw)
    sampled_img = ivy_imp.create_sampled_pixel_coords_image(
        td.image_dims,
        td.samples_per_dim,
        (td.batch_size, td.num_cameras),
        normalized=False,
        randomize=False,
    )
    assert ivy.min(sampled_img).to_scalar() == 26
    assert ivy.max(sampled_img).to_scalar() == 613
    sampled_img = ivy_imp.create_sampled_pixel_coords_image(
        td.image_dims,
        td.samples_per_dim,
        (td.batch_size, td.num_cameras),
        normalized=False,
        randomize=True,
    )
    assert ivy.min(sampled_img).to_scalar() >= 0
    assert ivy.max(sampled_img).to_scalar() < max(td.image_dims)
    sampled_img = ivy_imp.create_sampled_pixel_coords_image(
        td.image_dims,
        td.samples_per_dim,
        (td.batch_size, td.num_cameras),
        normalized=True,
        randomize=False,
    )
    assert np.allclose(ivy.min(sampled_img).to_scalar(), 0.040625)
    assert np.allclose(ivy.max(sampled_img).to_scalar(), 0.9578125)
    sampled_img = ivy_imp.create_sampled_pixel_coords_image(
        td.image_dims,
        td.samples_per_dim,
        (td.batch_size, td.num_cameras),
        normalized=True,
        randomize=True,
    )
    assert ivy.min(sampled_img).to_scalar() >= 0
    assert ivy.max(sampled_img).to_scalar() < 1
    ivy.previous_backend()

def test_sample_images(device, fw):
    if fw == "mxnet":
        # MXNet does not support splitting based on
        # section sizes, only number of sections as integer input
        # is supported.
        pytest.skip()
    ivy.set_backend(fw)
    img0, img1 = ivy_imp.sample_images(
        [ivy.array(td.pixel_coords_to_scatter)] * 2,
        32,
        (td.batch_size, td.num_cameras),
        td.image_dims,
    )
    assert list(img0.shape) == [td.batch_size, td.num_cameras, 35, 3]
    assert list(img1.shape) == [td.batch_size, td.num_cameras, 35, 3]
    ivy.previous_backend()

def test_sampled_volume_density_to_occupancy_probability(device, fw):
    ivy.set_backend(fw)
    occ_prob = ivy_imp.sampled_volume_density_to_occupancy_probability(
        ivy.array(td.densities), ivy.array(td.inter_sample_distances)
    )
    assert occ_prob.shape == td.densities.shape
    assert np.allclose(occ_prob, td.occ_probs)
    ivy.previous_backend()

def test_ray_termination_probabilities(device, fw):
    ivy.set_backend(fw)
    ray_term_probs = ivy_imp.ray_termination_probabilities(
        ivy.array(td.densities), ivy.array(td.inter_sample_distances)
    )
    assert ray_term_probs.shape == td.densities.shape
    assert np.allclose(ray_term_probs, td.ray_term_probs)
    ivy.previous_backend()

def test_stratified_sample(device, fw):
    ivy.set_backend(fw)
    num = 10
    res = ivy_imp.stratified_sample(
        ivy.array(td.start_vals), ivy.array(td.end_vals), num
    )
    assert res.shape == (3, num)
    for i in range(3):
        for j in range(num - 1):
            assert res[i][j] < res[i][j + 1]
    ivy.previous_backend()

def test_render_rays_via_termination_probabilities(device, fw):
    ivy.set_backend(fw)
    rendering, var = ivy_imp.render_rays_via_termination_probabilities(
        ivy.array(td.ray_term_probs), ivy.array(td.features), render_variance=True
    )
    assert rendering.shape == td.radial_depths.shape[:-1] + (3,)
    assert var.shape == td.radial_depths.shape[:-1] + (3,)
    assert np.allclose(rendering, td.term_prob_feature_rendering)
    assert np.allclose(var, td.term_prob_var_rendering)
    ivy.previous_backend()

@pytest.mark.parametrize("with_features", [True, False])
@pytest.mark.parametrize("with_timestamps", [True, False])
def test_render_implicit_features_and_depth(device, fw, with_features, with_timestamps):
    if fw == "mxnet":
        # MXNet does not support splitting with remainder
        pytest.skip()
    ivy.set_backend(fw)
    rgb, depth = ivy_imp.render_implicit_features_and_depth(
        td.implicit_fn,
        ivy.array(td.rays_o),
        ivy.array(td.rays_d),
        ivy.array(td.near),
        ivy.array(td.far),
        td.samples_per_ray,
        ivy.array(td.timestamps) if with_timestamps else None,
        inter_feat_fn=td.inter_feat_fn if with_features else None,
    )
    assert rgb.shape == (3, 3)
    assert depth.shape == (3, 1)
    ivy.previous_backend()


import io
import pickle
from typing import List, Tuple

from fastapi import HTTPException, UploadFile
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaIoBaseUpload

SCOPES = ["https://www.googleapis.com/auth/drive.file"]

async def get_user_credentials() -> Credentials:
    """
    Gets the user credentials for Google Drive API.
    """
    try:
        # Load the saved credentials from file
        with open("../token.pickle", "rb") as token:
            creds = pickle.load(token)
    except FileNotFoundError:
        # Create a flow instance to manage the OAuth2.0 Authorization Grant Flow steps.
        flow = InstalledAppFlow.from_client_secrets_file(
            "../client_secret.json", scopes=SCOPES
        )

        # Run the flow to authorize this app and get the user credentials.
        creds = flow.run_local_server(port=8080)

        # Save the credentials for the next run
        with open("../token.pickle", "wb") as token:
            pickle.dump(creds, token)

    return creds

async def upload_photos_to_google_drive(
    files: List[UploadFile], cat_id: int, cat_name: str
) -> Tuple[List[str], str]:
    """
    Upload photos to Google Drive and return their URLs and the ID of the cat's folder.
    """
    try:
        # Authorize with Google Drive API
        creds = await get_user_credentials()
        drive_service = build("drive", "v3", credentials=creds)

        # Check if the "Photos of cats" folder exists, create it if necessary
        query = (
            "mimeType='application/vnd.google-apps.folder' and name='Photos of cats'"
        )
        folder = (
            drive_service.files()
            .list(q=query, fields="files(id)")
            .execute()
            .get("files")
        )
        if not folder:
            # Create the "Photos of cats" folder if it doesn't exist
            folder_metadata = {
                "name": "Photos of cats",
                "mimeType": "application/vnd.google-apps.folder",
            }
            folder = (
                drive_service.files()
                .create(body=folder_metadata, fields="id")
                .execute()
            )
            google_folder_id = folder.get("id")
        else:
            google_folder_id = folder[0].get("id")

        # Check if the cat's folder exists, create it if necessary
        query = f"mimeType='application/vnd.google-apps.folder'" \
                f" and name='{cat_id} - {cat_name}' and parents='{google_folder_id}'"
        folder = (
            drive_service.files()
            .list(q=query, fields="files(id)")
            .execute()
            .get("files")
        )
        if not folder:
            # Create the cat's folder if it doesn't exist
            folder_metadata = {
                "name": f"{cat_id} - {cat_name}",
                "parents": [google_folder_id],
                "mimeType": "application/vnd.google-apps.folder",
            }
            folder = (
                drive_service.files()
                .create(body=folder_metadata, fields="id")
                .execute()
            )
            google_folder_id = folder.get("id")
        else:
            google_folder_id = folder[0].get("id")

        # Upload photos to the cat's folder
        urls = []
        for file in files:
            media = MediaIoBaseUpload(
                io.BytesIO(await file.read()),
                mimetype=file.content_type,
                resumable=True,
            )
            file_metadata = {"name": file.filename, "parents": [google_folder_id]}
            file = (
                drive_service.files()
                .create(body=file_metadata, media_body=media, fields="id")
                .execute()
            )
            url = f"https://drive.google.com/uc?id={file.get('id')}"
            urls.append(url)

        return urls, google_folder_id

    except HttpError as e:
        # Handle errors from Google Drive API requests
        if e.resp.status == 401:
            raise HTTPException(status_code=401, detail="Invalid credentials")
        elif e.resp.status == 404:
            raise HTTPException(
                status_code=404, detail="Failed to find a folder in Google Drive"
            )
    except Exception:
        raise HTTPException(
            status_code=500, detail="Failed to upload photos to Google Drive"
        )

async def delete_photos_from_drive(cat_id: int, cat_name: str) -> None:
    """
    Delete photos of the cat from Google Drive using Google Drive API.
    """
    try:
        # Authorize with Google Drive API
        creds = await get_user_credentials()
        if creds is None:
            raise HTTPException(status_code=401, detail="Authorization required")
        drive_service = build("drive", "v3", credentials=creds)

        # Find the folder with the cat's photos
        query = f"mimeType='application/vnd.google-apps.folder'" \
                f" and name='{cat_id} - {cat_name}'"
        folder = (
            drive_service.files()
            .list(q=query, fields="files(id)")
            .execute()
            .get("files")
        )
        if not folder:
            raise HTTPException(
                status_code=404, detail=f"Folder for cat with id:{cat_id} not found"
            )
        google_folder_id = folder[0].get("id")

        # Delete all files in the folder
        query = f"'{google_folder_id}' in parents"
        files = (
            drive_service.files()
            .list(q=query, fields="files(id)")
            .execute()
            .get("files", [])
        )
        for file in files:
            drive_service.files().delete(fileId=file.get("id")).execute()

        # Delete the folder
        drive_service.files().delete(fileId=google_folder_id).execute()

    except HttpError as e:
        if e.resp.status == 401:
            raise HTTPException(status_code=401, detail="Invalid credentials")
        elif e.resp.status == 404:
            raise HTTPException(
                status_code=404, detail=f"Folder for cat with id:{cat_id} not found"
            )
    except Exception:
        raise HTTPException(
            status_code=500, detail="Failed to delete photos from Google Drive"
        )

async def update_google_folder_name(folder_id: str, cat_id: int, new_name: str):
    try:
        creds = await get_user_credentials()
        drive_service = build("drive", "v3", credentials=creds)

        # Get the metadata of the folder by its ID
        folder_metadata = {"name": f"{cat_id} - {new_name}"}
        folder = (
            drive_service.files()
            .update(fileId=folder_id, body=folder_metadata)
            .execute()
        )

        return folder

    except HttpError as e:
        if e.resp.status == 401:
            raise HTTPException(status_code=401, detail="Invalid credentials")
        elif e.resp.status == 404:
            raise HTTPException(
                status_code=404, detail=f"Folder for cat with id:{cat_id} not found"
            )
    except Exception:
        raise HTTPException(
            status_code=500, detail="Failed to update folder name in Google Drive"
        )

async def delete_google_drive_file(file_id: str):
    """
    Delete a file from Google Drive by ID.
    """
    try:
        creds = await get_user_credentials()
        service = build("drive", "v3", credentials=creds)
        service.files().delete(fileId=file_id).execute()

    except HttpError as e:
        if e.resp.status == 401:
            raise HTTPException(status_code=401, detail="Invalid credentials")
        elif e.resp.status == 404:
            raise HTTPException(status_code=404, detail="File not found")
    except Exception:
        raise HTTPException(
            status_code=500, detail="Failed to update photos from Google Drive"
        )


T = int(input())

def people(a,b):
    building = [[i for i in range(1,b+1)]]
    # 1,2,3, ..., b
    for i in range(1,a+1):
        section = [1]
        for j in range(1,b):
            section.append(section[j-1]+building[i-1][j])
        building.append(section)
    return building[a][b-1]

for _ in range(T):
    k = int(input())
    n = int(input())
    print(people(k,n))


from pyspark.sql import *
from pyspark.sql.types import *
from datetime import datetime

# Get spark session

spark = SparkSession.builder.appName('convert-files').getOrCreate()
sc = spark.sparkContext
sqlContext = SQLContext(sc)
spark.conf.set('spark.sql.debug.maxToStringFields', 100000)

def write_parquet():
    try:
        path  = '/home/isaraujo@raiadrogasil.com/Documentos/flink-code/input/A_RAIABD-TB_CANAL_VENDA.json'
        df_read = spark.read.json(path)
        df_read.repartition(1).write.mode('append').parquet('/home/isaraujo@raiadrogasil.com/Documentos/flink-code/output/')
    except FileNotFoundError:
        print('Arquivo não encontrado...')
    except PermissionError:
        print('permissao negada para escrever...')

if __name__ == "__main__":
    write_parquet()