__author__ = "Adrian Dempwolff (phyghtmap@aldw.de)"
__version__ = "2.23"
__copyright__ = "Copyright (c) 2009-2021 Adrian Dempwolff"
__license__ = "GPLv2+"

import numpy
from matplotlib import __version__ as mplversion
import time
import datetime

from phyghtmap.varint import writableString

def makeElevClassifier(majorDivisor, mediumDivisor):
	"""returns a function taking an elevation and returning a
	category specifying whether it's a major, medium or minor contour.
	"""
	def classify(height):
		if height%majorDivisor==0:
			return "elevation_major"
		elif height%mediumDivisor==0:
			return "elevation_medium"
		else:
			return "elevation_minor"
	return classify

def makeUtcTimestamp():
	return datetime.datetime.utcfromtimestamp(
		time.mktime(time.localtime())).isoformat()+"Z"

class Id(object):
	"""a counter, constructed with the first number to return.

	Count using the getId method.
	"""
	def __init__(self, offset):
		self.curId = offset

	def getId(self):
		self.curId += 1
		return self.curId-1

class Output(object):
	"""An OSM output.

	It is constructed with a destination name, the desired OSM API version,
	the version of phyghtmap as string, an already formatted OSM XML bounds tag
	as output by the hgt.makeBoundsString() function,	an integer representing
	the gzip compressionlevel (or 0 if no gzip compression is desired),
	an elevation classifying function as returned by makeElevClassifier()
	and a hint weather to write timestamps to output or not.
	"""
	def __init__(self, fName, osmVersion, phyghtmapVersion, boundsTag, gzip=0,
		elevClassifier=None, timestamp=False):
		if 0 < gzip < 10:
			import gzip as Gzip
			self.outF = Gzip.open(fName, "wb", gzip)
		else:
			self.outF = open(fName, "wb")
		self.osmVersion = "{0:.1f}".format(osmVersion)
		if osmVersion > 0.5:
			self.versionString = ' version="1"'
		else:
			self.versionString = ""
		if timestamp:
			self.timestampString = ' timestamp="{0:s}"'.format(makeUtcTimestamp())
		else:
			self.timestampString = ""
		self.elevClassifier = elevClassifier
		self.phyghtmapVersion= phyghtmapVersion
		self.boundsTag = boundsTag
		self._writePreamble()

	def _writePreamble(self):
		self.write('<?xml version="1.0" encoding="utf-8"?>\n')
		self.write(
			'<osm version="{0:s}" generator="phyghtmap {1:s}">\n'.format(
			self.osmVersion, self.phyghtmapVersion))
		self.write(self.boundsTag+"\n")

	def done(self):
		self.write("</osm>\n")
		self.outF.close()
		return 0

	def write(self, output):
		self.outF.write(writableString(output))

	def flush(self):
		self.outF.flush()

	def writeWays(self, ways, startWayId):
		IDCounter = Id(startWayId)
		for startNodeId, length, isCycle, elevation in ways:
			IDCounter.curId += 1
			nodeIds = list(range(startNodeId, startNodeId+length))
			if isCycle:
				nodeIds.append(nodeIds[0])
			nodeRefs = ('<nd ref="{:d}"/>\n'*len(nodeIds)).format(*nodeIds)
			self.write('<way id="{0:d}"{1:s}{2:s}>{3:s}'
				'<tag k="ele" v="{4:d}"/>'
				'<tag k="contour" v="elevation"/>'
				'<tag k="contour_ext" v="{5:s}"/>'
				'</way>\n'.format(
					IDCounter.curId-1,
					self.versionString,
					self.timestampString,
					nodeRefs,
					elevation,
					self.elevClassifier(elevation)))

def _makePoints(output, path, IDCounter, versionString, timestampString):
	"""writes OSM representations of the points making up a path to
	output.

	It returns a list of the node ids included in this path.
	"""
	ids, content = [], []
	for lon, lat in path:
		IDCounter.curId += 1
		content.append('<node id="{0:d}" lat="{1:.7f}" lon="{2:.7f}"{3:s}{4:s}/>'.format(
			IDCounter.curId-1,
			lat,
			lon,
			versionString,
			timestampString,)
		)
		ids.append(IDCounter.curId-1)
	if numpy.all(path[0]==path[-1]):  # close contour
		del content[-1]  # remove last node
		del ids[-1]
		ids.append(ids[0])
		IDCounter.curId -= 1
	# output is eventually a pipe, so we must pass a string
	output.write("\n".join(content)+"\n")
	return ids

def _writeContourNodes(output, contourList, elevation, IDCounter,
	versionString, timestampString):
	"""calls _makePoints() to write nodes to <output> and collects information
	about the paths in contourList, namely the node ids for each path, which is
	the returned.
	"""
	ways = []
	for path in contourList:
		nodeRefs = _makePoints(output, path, IDCounter, versionString,
			timestampString)
		if nodeRefs[0] == nodeRefs[-1]:
			ways.append((nodeRefs[0], len(nodeRefs)-1, True, elevation))
		else:
			ways.append((nodeRefs[0], len(nodeRefs), False, elevation))
	return ways

def writeXML(output, contourData, elevations, timestampString, opts):
	"""emits node OSM XML to <output> and collects path information.

	<output> may be anything having a write method.  For now, its used with
	Output instance or an open pipe to the parent process, if running in parallel.

	<contourData> is a phyghtmap.hgt.ContourObject instance, <elevations> a list
	of elevations to generate contour lines for.

	<opts> are the options coming from phyghtmap.
	"""
	IDCounter = Id(opts.startId)
	if opts.osmVersion > 0.5:
		versionString = ' version="1"'
	else:
		versionString = ""
	ways = []
	for elevation in elevations:
		contourList = contourData.trace(elevation)[0]
		if not contourList:
			continue
		ways.extend(_writeContourNodes(output, contourList, elevation,
			IDCounter, versionString, timestampString))
		#output.flush()
	newId = IDCounter.getId()
	return newId, ways


# -*- coding: UTF-8 -*-

from output_class import OutputClass
from google.appengine.ext import db
from comments.comments import Comments
from db_entities.forum.db_forum_category import DBForumCategory
from tools.multipage import Multipage

class forum_category(OutputClass):

    url_handler = '/forum/category/?'

    def get(self):
        self.checkSession(self.request.headers.get('Cookie'), False)
        category = 0
        cat = int(self.request.get('cat')) if self.request.get('cat') else 0#int(self.get_url_part(1))

        cat_name = "Not set"

        try:
            category = DBForumCategory.get_by_id(cat)
            cat_name = category.name.encode("utf-8")
        except:
            self.showMessage("Категория не найдена!")
            return

        _topics = []
        comments = Comments()
        #page = self.request.get(')

        try:
            tops = db.GqlQuery("SELECT * FROM DBForumTopic where category = :category", category = category.key())
            for top in tops:
                #last_message = comments.getLastComment([top.key()])
                _topics.append({'id':int(top.key().id()), 'name':top.name.encode("utf-8"),
                                #'answers' : comments.getCommentsCount([top.key()]) - 1,
                                'answers' : top.comments_count if top.comments_count else 0,
                            'last_message' : top.last_comment,
                            'last_message_date' : top.last_comment.date if top.last_comment else top.date })
        except: pass
        _topics.sort(key=lambda k: k['last_message_date'], reverse=True)
        multipage = Multipage(self.request.get('page'), _topics, '/forum/category&cat=' + str(cat))
        self.insertTemplate('forum/category.html', {
                                                    'tops': multipage.getItems(),
                                                    'pages': multipage.getPages(),
                                                    'cat_name':cat_name,
                                                    'cat_id':cat
                                                    })
        self.drawPage("Форум :: Просмотр категории "+cat_name)


import json
import requests

class HttpRest(object):
    def __init__(self, url="https://jsonplaceholder.typicode.com"):
        self.base_url = url

    def call(self, method, uri, payload):
        if method == "get":
            return self.get(uri)
        if method == "post":
            return self.post(uri, payload)

        return None

    # Perform GET, raise an error based on the Status
    def get(self, uri):
        try:
            response = requests.get(self.base_url + uri)
            response.raise_for_status()
        except requests.exceptions.HTTPError as e:
            raise SystemExit(e)

        return response

    # Perform POST, raise an error based on the Status
    def post(self, uri, payload):
        try:
            response = requests.post(self.base_url + uri, payload)
            response.raise_for_status()
        except requests.exceptions.HTTPError as e:
            raise SystemExit(e)

        return response

    @staticmethod
    def parse_response(response):
        return json.dumps(response.json(), indent=2)


# -*- coding: utf-8 -*-
__author__ = 'Nikitin'
from model.data import Data
import matplotlib.pyplot as plt
from matplotlib.finance import candlestick2_ohlc,candlestick_ohlc
from matplotlib.dates import date2num

class Plotter:
    def __init__(self, data=Data()):
        self.data=data

        print('Запускаем плоттер')
        self._vp_config()
        self._plot()
        plt.show()

    def _vp_config(self):
        f = plt.figure()
        plt.subplots_adjust(hspace=0.001,left=0.05,right=0.99, top=0.96, bottom=0.05)
        num_subplot=len(self.data.viewports)
        self.axises=list()

        ax1 = plt.subplot(num_subplot*100+11)
        self.axises.append(ax1)

        for i in range(num_subplot-1):
            ax = plt.subplot(num_subplot*100+i+12, sharex=ax1)
            self.axises.append(ax)

    def _plot(self):

        self._plot_candles()
        self._plot_graphs()
        self._plot_dots()

        # for item in (self.data.viewports):
        #     print(item.keys())
        # print(self.data.candles)
        # print(self.data.graphs)
        # print(self.data.dots)

    def _plot_candles(self):
        for candle_name in self.data.candles:
            # print(candle_name)
            candles=self.data.candles[candle_name]
            tohlc_list=list()
            for time in sorted(candles):
                t=list()
                t.append(date2num(time))
                t.extend(candles[time])
                tohlc_list.append(t)
                # print(time,candles[time],t)

            vp=self._find_vp(candle_name)
            # print(vp)
            # print(tohlc_list)
            candlestick_ohlc(self.axises[vp],tohlc_list,width=60.0*10/(24.0*3600.0)*0.8)#,alpha=a,colorup=cu,colordown=cd)

    def _plot_graphs(self):
        for graph_name in self.data.graphs:
            # print(graph_name)
            graph=self.data.graphs[graph_name]
            times=list()
            dates=list()
            for time in sorted(graph):
                # print(time,graph[time])
                times.append(time)
                dates.append(graph[time])

            vp=self._find_vp(graph_name)
            self.axises[vp].plot_date(times,dates,ls="-",marker=None)

    def _plot_dots(self):
        for dots_name in self.data.dots:
            # print(dots_name)
            dots=self.data.dots[dots_name]
            times=list()
            dates=list()
            for time in sorted(dots):
                # print(time,dots[time])
                times.append(time)
                dates.append(dots[time])

            vp=self._find_vp(dots_name)
            options=self.data.viewports[vp][dots_name]
            # print(options)
            self.axises[vp].plot_date(times,dates,ls="None",marker=options.get('marker','o'),markersize=options.get('markersize',10))

    def _find_vp(self,name):
        for i in range(len(self.data.viewports)):
            if name in self.data.viewports[i]:
                return i

        raise RuntimeError("Что-то случилось в процедуре Plotter._find_vp")

if __name__ == "__main__":
    pass


import logging

#
# Uses standard Python logging service to
# manage logging of the bot
#

def getlogger(lgfileName):
    # logger
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    ch = logging.FileHandler(lgfileName)
    ch.setLevel(logging.INFO)
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(module)s - %(levelname)s - %(message)s')
    # add formatter to ch
    ch.setFormatter(formatter)
    # add ch to logger
    logger.addHandler(ch)
    # create console handler and set level to debug
    stream_ch = logging.StreamHandler()
    stream_ch.setLevel(logging.INFO)
    stream_ch.setFormatter(formatter)
    logger.addHandler(stream_ch)
    return logger


#!/usr/bin/python3
""" API definition """

from models import storage
from api.v1.views import app_views
from flask import Flask, Blueprint, jsonify, make_response
from os import getenv
from flask_cors import CORS

app = Flask(__name__)
app.register_blueprint(app_views)
cors = CORS(app, resources={"/*": {"origins": "0.0.0.0"}})

@app.teardown_appcontext
def teardown_appcontext(code):
    """ Closes storage """
    storage.close()

@app.errorhandler(404)
def code_404(error):
    """
       Ref: http://flask.pocoo.org/docs/0.12/patterns/errorpages/
       https://kite.com/docs/python;flask.helpers.make_response
    """
    return jsonify({"error": "Not found"}), 404

if __name__ == "__main__":
    host = getenv("HBNB_API_HOST", "0.0.0.0")
    port = int(getenv("HBNB_API_PORT", "5000"))
    app.run(host, port)


import numpy as np
import pandas as pd
import pyfaidx
import argparse

def parse_args():
    parser = argparse.ArgumentParser(
        description="""Runs a number of spaced seeds against specified genomes
                    to determine if seed entropy affects specificity.""")
    parser.add_argument("-g", "--genomes", type=str, help="Paths to the genomes, comma delimited", required=True)
    parser.add_argument("-n", "--num-seeds", type=int, help="Number of spaced seeds to sample", required=True)
    parser.add_argument("-s", "--seeds", type=str,
                        help="Seeds tsv file, output of make_seeds.py and markov_process_seeds.py", required=True)
    # parser.add_argument("-o", "--output", type=str, help="Name of output .tsv file", required=True)
    args = parser.parse_args()
    return args

def sample_seeds(seeds, entropies, sample_size):
    # select_indexes = np.logspace(0, math.log10(len(entropies)-1), sample_size).astype(int)
    select_indexes = np.linspace(0, len(entropies)-1, sample_size, dtype=int)
    sorted_indexes = np.argsort(entropies)
    indexes = np.sort(sorted_indexes[select_indexes])
    return np.array(seeds)[indexes]

def reverse_complement(numpy_sequence):
    complement = {b'A': b'T',
                  b'T': b'A',
                  b'C': b'G',
                  b'G': b'C'}
    rev_seq = np.flipud(numpy_sequence)
    return np.vectorize(lambda nt: complement[nt])(rev_seq)

def determine_word_frequencies(genome_sequence, spaced_seed):
    spaced_seed = str(spaced_seed)
    k = len(spaced_seed)
    weighted_indexes = []
    kmers = {}
    for i in range(k):
        if spaced_seed[i] == '1':
            weighted_indexes.append(i)
    w = len(weighted_indexes)
    for i in range(len(genome_sequence) - k + 1):
        word = np.empty(w, 'S1')
        for j, weighted_index in zip(range(w), weighted_indexes):
            word[j] = genome_sequence[i:i+k][weighted_index]
        word_str = word.tostring()
        reverse_word_str = reverse_complement(word).tostring()
        canonical_word = min(word_str, reverse_word_str)
        kmers[canonical_word] = False if canonical_word in kmers else True
        # kmers[canonical_word] = kmers[canonical_word] + [i] if canonical_word in kmers else [i]
    return kmers

def determine_unique_frames(kmers, num_frames):
    frames_count = np.zeros(num_frames, dtype=np.bool)
    for frames in kmers.values():
        frames_count[frames] = True if len(frames) == 1 else False

    return frames_count

def determine_word_uniqueness(words_dict):
    np_values = np.array(list(words_dict.values()))
    # unique_items_index = np_values == True
    p_uniqueness = len(np_values[np_values]) / len(np_values)
    return p_uniqueness

def main():
    args = parse_args()
    genome = str(pyfaidx.Fasta(args.genomes)[0][0:])
    num_seeds = args.num_seeds
    data = pd.read_csv(args.seeds, sep='\t', index_col=0)
    seeds = data.index.values
    seed_sample = sample_seeds(seeds, data['3bit'].values, num_seeds)
    determine_words_vect = np.vectorize(determine_word_frequencies, excluded=['genome_sequence'])
    kmers_list = determine_words_vect(genome, seed_sample)
    deter_uniqueness_vect = np.vectorize(determine_word_uniqueness)
    uniquenesses = deter_uniqueness_vect(kmers_list)
    print("Seed", "Uniqueness", "2bit", sep='\t')
    for seed, uniqueness in zip(seed_sample, uniquenesses):
        print(seed, uniqueness, sep='\t')

if __name__ == "__main__":
    main()


"""Sync command."""
from __future__ import annotations

import sys

import click
from result import Ok

from pyrgo.cli.cmds._utils import ensure_env_exist_in_lock_file, inform_and_run_program
from pyrgo.command_exec import PythonCommandExec
from pyrgo.conf import PyrgoConf

@click.command("sync")
@click.option(
    "-e",
    "--env",
    "env",
    type=click.STRING,
    required=True,
)
@click.option(
    "--editable/--no-editable",
    "editable",
    type=click.BOOL,
    default=True,
    show_default=True,
)
def sync(env: str, *, editable: bool) -> None:
    """Sync current python environment to locked deps."""
    config = PyrgoConf.new()
    ensure_env_exist_in_lock_file(env=env, config=config)

    piptools_command = PythonCommandExec.new(program="piptools").add_args(
        args=[
            "sync",
            config.requirements.joinpath(f"{env}.txt")
            .relative_to(config.cwd)
            .as_posix(),
        ],
    )
    pip_command = PythonCommandExec.new(program="pip").add_args(
        args=["install", "--no-deps"],
    )
    if editable:
        pip_command.add_args(args=["-e"])
    pip_command.add_args(args=["."])

    program_execution = inform_and_run_program(commands=[piptools_command, pip_command])

    if not isinstance(program_execution, Ok):
        sys.exit(1)

    sys.exit(0)


import time
import numpy as np
import pandas as pd
import elephant as el
import pyqtgraph as pg

from scipy import stats
from multiprocessing import Pool
from scipy.spatial.distance import cdist
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from PyQt5 import QtWidgets, QtCore

from swan.gui.parameter_input_dialog_ui import ParameterInputDialogUI

def p2p_amplitude(waves):
    return np.amax(waves[:, 10:], axis=1) - np.amin(waves[:, 10:], axis=1)

def asymmetry(waves):
    return np.amax(waves[:, 10:], axis=1) / np.amin(waves[:, 10:], axis=1)

def square_sum(waves):
    return np.sum(np.einsum('ij, ij -> ij', waves, waves), axis=1)

def diff(waves):
    return np.gradient(waves, axis=1).mean(axis=0)

def diffdiff(waves):
    return np.gradient(np.gradient(waves, axis=1), axis=1).mean(axis=0)

def isi(train):
    return el.statistics.isi(train)

def lv(train):
    intervals = isi(train)
    return el.statistics.lv(intervals)

def cv2(train):
    intervals = isi(train)
    return el.statistics.cv2(intervals)

def fr(train):
    return el.statistics.mean_firing_rate(train)

def isih(train, bin_max, bin_step):
    intervals = isi(train)
    hist, bins = np.histogram(intervals, bins=range(0, bin_max, bin_step), density=True)
    return hist

def run_kmeans_solver(data, n_clusters, n_init=10):
    solver = KMeans(n_clusters=n_clusters, n_init=n_init)
    solver.fit(data)
    clusters = solver.cluster_centers_
    labels = solver.predict(data)
    return solver, clusters, labels

def run_kmeans_solver_no_prediction(data, n_clusters, n_init=10):
    solver = KMeans(n_clusters=n_clusters, n_init=n_init)
    solver.fit(data)
    return solver, n_clusters

def generate_feature_vectors(parent_class, feature_dictionary, additional_dictionary):
    feature_vectors = []

    for u, (waves, train, reduced_mean, mean) in \
            enumerate(zip(parent_class.all_waveforms,
                          parent_class.all_spiketrains,
                          parent_class.reduced_means,
                          parent_class.mean_waveforms)):
        feature_vectors.append([])
        if feature_dictionary['p2p amplitude']:
            feature_vectors[u].extend(list(stats.describe(p2p_amplitude(waves)))[2:])
        if feature_dictionary['asymmetry']:
            feature_vectors[u].extend(list(stats.describe(asymmetry(waves)))[2:])
        if feature_dictionary['square sum']:
            feature_vectors[u].extend(list(stats.describe(square_sum(waves)))[2:])
        if feature_dictionary['spike width']:
            feature_vectors[u].extend(list(stats.describe(spike_width(waves)))[2:])
        if feature_dictionary['cv2']:
            feature_vectors[u].append((cv2(train)))
        if feature_dictionary['lv']:
            feature_vectors[u].append((lv(train)))
        if feature_dictionary['firing rate']:
            feature_vectors[u].append((fr(train)))
        if feature_dictionary['mean']:
            feature_vectors[u].extend(mean)
        if feature_dictionary['reduced mean']:
            feature_vectors[u].extend(reduced_mean)
        if feature_dictionary['first derivative']:
            feature_vectors[u].extend(diff(waves))
        if feature_dictionary['second derivative']:
            feature_vectors[u].extend(diffdiff(waves))
        if feature_dictionary['described isi']:
            feature_vectors[u].extend(list(stats.describe(isih(train,
                                                               additional_dictionary['bin max'],
                                                               additional_dictionary['bin step']
                                                               )
                                                          )
                                           )[2:]
                                      )
        if feature_dictionary['isi']:
            feature_vectors[u].extend(isih(train,
                                           additional_dictionary['bin max'],
                                           additional_dictionary['bin step']
                                           )
                                      )

    feature_vectors = np.array(feature_vectors)
    feature_vectors = (feature_vectors - feature_vectors.mean(axis=0, keepdims=True)) / feature_vectors.std(axis=0)

    return np.nan_to_num(feature_vectors)

def get_session_ids(blocks):
    session_ids = []
    for b, block in enumerate(blocks):
        units = block.channel_indexes[0].units
        for u, unit in enumerate(units):
            if 'noise' not in unit.description.split() and 'unclassified' not in unit.description.split():
                session_ids.append(b)

    return session_ids

def get_real_unit_ids(blocks):
    unit_ids = []
    for block in blocks:
        units = block.channel_indexes[0].units
        for u, unit in enumerate(units):
            if 'noise' not in unit.description.split() and 'unclassified' not in unit.description.split():
                unit_ids.append(u)

    return unit_ids

def get_mean_waveforms(blocks):
    mean_waveforms = []
    for block in blocks:
        units = block.channel_indexes[0].units
        for unit in units:
            if 'noise' not in unit.description.split() and 'unclassified' not in unit.description.split():
                waves = unit.spiketrains[0].waveforms.magnitude[:, 0, :]
                waves = waves - waves.mean(axis=1, keepdims=True)
                mean_waveforms.append(waves.mean(axis=0))

    return mean_waveforms

def get_all_waveforms(blocks):
    waveforms = []
    for block in blocks:
        units = block.channel_indexes[0].units
        for unit in units:
            if 'noise' not in unit.description.split() and 'unclassified' not in unit.description.split():
                waves = unit.spiketrains[0].waveforms.magnitude[:, 0, :]
                waves = waves - waves.mean(axis=1, keepdims=True)
                waveforms.append(waves)
    return waveforms

def get_all_spiketrains(blocks):
    spiketrains = []
    for block in blocks:
        units = block.channel_indexes[0].units
        for unit in units:
            if 'noise' not in unit.description.split() and 'unclassified' not in unit.description.split():
                train = unit.spiketrains[0].times.magnitude
                spiketrains.append(train)
    return spiketrains

def get_time_stamps(blocks):
    all_time_stamps = []
    for block in blocks:
        units = block.channel_indexes[0].units
        for unit in units:
            if 'noise' not in unit.description.split() and 'unclassified' not in unit.description.split():
                all_time_stamps.append(block.rec_datetime)

    corrected_time_stamps = []
    for ts in all_time_stamps:
        cts = time.mktime(ts.timetuple())
        while cts in corrected_time_stamps:
            cts += 1.
        corrected_time_stamps.append(cts)

    time_stamps = [ts - corrected_time_stamps[0] for ts in corrected_time_stamps]

    return time_stamps

def spike_width(waves):
    return np.argmax(waves[:, 10:], axis=1) - np.argmin(waves[:, 10:], axis=1)

class SwanImplementation:

    # noinspection PyArgumentList
    def __init__(self, neodata, parent=None):

        self.blocks = neodata.blocks
        self.parent = parent

        self.feature_dict = {'mean': True,

                             'reduced mean': True,

                             'first derivative': True,
                             'second derivative': True,

                             'p2p amplitude': True,
                             'asymmetry': True,
                             'square sum': True,
                             'spike width': True,

                             'cv2': True,
                             'lv': True,
                             'firing rate': True,

                             'isi': True,
                             'described isi': True,

                             'clusters': 2,

                             'time split': 10
                             }

        self.additional_dict = {'reduced mean dims': 5,
                                'bin max': 15000,
                                'bin step': 60,
                                'max_clusters': 20}

        self.mean_waveforms = get_mean_waveforms(self.blocks)
        self.all_waveforms = get_all_waveforms(self.blocks)
        self.all_spiketrains = get_all_spiketrains(self.blocks)
        self.timestamps = get_time_stamps(self.blocks)
        self.unit_ids = get_real_unit_ids(self.blocks)
        self.session_ids = get_session_ids(self.blocks)

        self.pca = PCA(n_components=self.additional_dict['reduced mean dims'])
        self.pca.fit(np.vstack(self.all_waveforms))
        self.reduced_means = self.pca.transform(self.mean_waveforms)

        QtWidgets.QApplication.restoreOverrideCursor()
        self.feature_dict, solver, okay = ParameterInputDialog(parent=self.parent, algorithm=self).exec_()
        QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)

        if okay:
            self.feature_vectors = generate_feature_vectors(self, self.feature_dict, self.additional_dict)

            if solver is not None:
                self.solver = solver

                clusters = self.solver.cluster_centers_
                labels = self.solver.predict(self.feature_vectors)
            else:
                self.solver, clusters, labels = run_kmeans_solver(self.feature_vectors,
                                                                  self.feature_dict['clusters'], n_init=100)

            labels = self.remove_duplicates(self.feature_vectors, self.session_ids, self.unit_ids, labels, clusters)

            if self.feature_dict['time split'] is not None:
                labels = self.time_splits(days=self.feature_dict['time split'],
                                          labels=labels, timestamps=self.timestamps)

            self.result = self.generate_result_dataframe(self.session_ids, self.unit_ids, labels)
        else:
            self.result = pd.DataFrame({})

    def time_splits(self, days, labels, timestamps):
        threshold = days * 86400.0

        labels_dict = self.generate_labels_dictionary(labels, timestamps)

        while self.has_time_conflicts(labels_dict, threshold):
            for label in sorted(labels_dict.keys()):
                timestamps = labels_dict[label]
                if timestamps.size > 1:
                    time_diffs = timestamps[1:] - timestamps[:-1]
                    if np.any(time_diffs > threshold):
                        cuts = np.where(time_diffs > threshold)[0]
                        for cut in cuts:
                            max_label = max(labels_dict.keys())
                            labels_dict[max_label + 1] = timestamps[cut + 1:]
                            labels_dict[label] = timestamps[:cut + 1]
                            break
                else:
                    continue
        return self.recreate_labels_list(labels_dict)

    @staticmethod
    def has_time_conflicts(dictionary, threshold):
        for label in dictionary.keys():
            timestamps = dictionary[label]
            if timestamps.size > 1 and np.any((timestamps[1:] - timestamps[:-1]) > threshold):
                return True
        else:
            return False

    @staticmethod
    def recreate_labels_list(dictionary):
        outstamps, outlabels = [], []
        for key in dictionary.keys():
            vals = dictionary[key]
            outstamps.extend(vals.tolist())
            outlabels.extend([key] * vals.size)

        outstamps, outlabels = np.array(outstamps), np.array(outlabels)
        return outlabels[np.argsort(outstamps)].tolist()

    def remove_duplicates(self, feature_vectors, session_ids, unit_ids, labels, clusters):
        def get_duplicates(sessions, units, unit_labels):
            df = pd.DataFrame({
                'session': sessions,
                'unit': units,
                'label': unit_labels
            })

            return df.sort_values(by=['label', 'session']).loc[
                df[['session', 'label']].duplicated(keep=False)
            ]

        duplicates = get_duplicates(session_ids, unit_ids, labels)

        visited = []

        count = 1
        while not duplicates.empty:
            clusters = clusters
            labels = labels

            maximum_distances = self.compute_intracluster_maximum_distance(clusters=clusters,
                                                                           feature_vectors=feature_vectors,
                                                                           labels=labels)
            cross_distances = cdist(feature_vectors, clusters)

            for label in np.unique(duplicates.label):
                cluster_df = duplicates.loc[duplicates.label == label]
                for session in np.unique(cluster_df.session):
                    session_duplicates = cluster_df.loc[cluster_df.session == session]
                    conflicting_unit_distances = cross_distances[session_duplicates.index, label]

                    discard_unit_position = np.argmax(conflicting_unit_distances)
                    discard_unit_id = session_duplicates.unit[session_duplicates.index[discard_unit_position]]

                    visited.append((session, discard_unit_id, label))

                    other_distances = cross_distances[discard_unit_position]
                    differences = other_distances - maximum_distances
                    candidates = []

                    for d, difference in enumerate(differences):
                        if d == label:
                            candidates.append(0.0)
                        elif difference < 0.0:
                            candidates.append(difference)
                        else:
                            candidates.append(0.0)

                    while np.any(np.array(candidates) < 0.0):
                        new_cluster_position = np.argmin(candidates)
                        # noinspection PyTypeChecker
                        candidates[new_cluster_position] = 0.0
                        if (session, discard_unit_id, new_cluster_position) not in visited:
                            labels = self.assign_new_cluster(sessions=session_ids,
                                                             units=unit_ids,
                                                             labels=labels,
                                                             session_id=session,
                                                             unit_id=discard_unit_id,
                                                             new_cluster=new_cluster_position)
                            break
                    else:
                        labels = self.create_new_cluster(sessions=session_ids,
                                                         units=unit_ids,
                                                         labels=labels,
                                                         session_id=session,
                                                         unit_id=discard_unit_id)

                    clusters = self.recompute(feature_vectors, labels)
                    break
                break

            duplicates = get_duplicates(session_ids, unit_ids, labels)
            count += 1

        return labels

    @staticmethod
    def compute_intracluster_maximum_distance(clusters, feature_vectors, labels):
        maximum_distances = []
        for c, center in enumerate(clusters):
            members = feature_vectors[np.where(labels == c)[0]]
            max_dist = 0.0
            for member in members:
                distance = np.linalg.norm(member - center)
                if distance > max_dist:
                    max_dist = distance
            maximum_distances.append(max_dist)

        return np.array(maximum_distances)

    @staticmethod
    def assign_new_cluster(sessions, units, labels, session_id, unit_id, new_cluster):
        pair_list = [(s, u) for s, u in zip(sessions, units)]
        position = [pos for pos, val in enumerate(pair_list) if val == (session_id, unit_id)]

        labels[position] = new_cluster

        return labels

    @staticmethod
    def create_new_cluster(sessions, units, labels, session_id, unit_id):
        pair_list = [(s, u) for s, u in zip(sessions, units)]
        position = [pos for pos, val in enumerate(pair_list) if val == (session_id, unit_id)]

        max_label = np.amax(labels)
        labels[position] = max_label + 1

        return labels

    @staticmethod
    def recompute(feature_vectors, labels):
        cluster_labels = np.unique(labels)
        total_clusters = cluster_labels.size
        label_list = labels

        cluster_centers = np.zeros((total_clusters, feature_vectors.shape[1]))

        for idx, label in enumerate(cluster_labels):
            vectors = feature_vectors[label_list == label]
            cluster_centers[idx] = vectors.mean(axis=0)

        return cluster_centers

    @staticmethod
    def generate_result_dataframe(session_ids, unit_ids, labels):
        dataframe = pd.DataFrame({'session': session_ids,
                                  'unit': unit_ids,
                                  'label': labels
                                  })

        return dataframe.sort_values(by=['session', 'unit'])

    @staticmethod
    def generate_labels_dictionary(labels, timestamps):
        labels = np.array(labels)
        timestamps = np.array(timestamps)

        labels_dict = {}
        for label in np.unique(labels):
            labels_dict[label] = timestamps[np.where(labels == label)[0]]

        return labels_dict

# noinspection PyArgumentList
class ParameterInputDialog(QtWidgets.QDialog):

    def __init__(self, parent=None, algorithm=None):
        # noinspection PyArgumentList
        QtWidgets.QDialog.__init__(self, parent=parent)

        self.setWindowTitle("Automatic algorithm: SWAN Implementation")
        self.algorithm = algorithm

        self.interface = ParameterInputDialogUI(self)
        self.plot_widget = self.interface.elbow_curve_plot.pg_canvas

        self.interface.clusters.textChanged.connect(self.update_plot)

        self._solvers = []
        self._clusters = []
        self._inertias = []
        self.chosen_solver = None

        self.values_dict = {}
        self.final_state = False

        self.spot_size = 10
        self.default_pen = pg.mkPen(color='w',
                                    width=2,
                                    style=QtCore.Qt.DotLine)
        self.default_symbol_pen = pg.mkPen(color='b')
        self.default_brush = pg.mkBrush((0, 255, 0, 128))
        self.default_symbol = 'o'

        self.current_clusters_value = 2

    def on_confirm(self):
        self.final_state = True
        self.accept()

    def on_cancel(self):
        self.final_state = False
        self.reject()

    # @QtCore.pyqtSlot(object, object)
    # def on_plot_clicked(self, data_item, ev):
    #     if ev.button() == 1:
    #         x_pos = ev.pos().x()
    #         closest_val = self._get_closest_val(x_pos)
    #         if closest_val is not None:
    #             self.interface.clusters.setValue(closest_val)
    #
    # @staticmethod
    # def _get_closest_val(input_value):
    #     lower = np.floor(input_value)
    #     lower_diff = input_value - lower
    #     upper = np.ceil(input_value)
    #     upper_diff = upper - input_value
    #
    #     pprint({"lower": lower,
    #             "lower_diff": lower_diff,
    #             "upper": upper,
    #             "upper_diff": upper_diff})
    #
    #     if lower_diff < upper_diff and lower_diff < 0.1:
    #         return lower
    #     elif upper_diff < lower_diff and upper_diff < 0.1:
    #         return upper
    #     else:
    #         return None

    @QtCore.pyqtSlot()
    def update_plot(self):
        clusters = self._clusters
        inertias = self._inertias
        sizes = []
        pens = []
        brushes = []
        symbols = []

        self.current_clusters_value = int(self.interface.clusters.value())

        for cluster in clusters:
            sizes.append(self.spot_size)
            symbols.append(self.default_symbol)
            if cluster == self.current_clusters_value:
                pens.append(pg.mkPen('r'))
                brushes.append(pg.mkBrush((255, 0, 0, 128)))
            else:
                pens.append(self.default_symbol_pen)
                brushes.append(self.default_brush)

        self._plot(clusters, inertias,
                   symbolPen=pens,
                   symbolSize=sizes,
                   symbol=symbols,
                   symbolBrush=brushes,
                   pxMode=True,
                   clear=True)

    def plot(self):
        solvers = []
        clusters = []
        inertias = []

        QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
        self.update_values_dict()
        feature_vectors = generate_feature_vectors(self.algorithm, self.values_dict, self.algorithm.additional_dict)

        with Pool(processes=None) as pool:
            results = pool.starmap_async(run_kmeans_solver_no_prediction,
                                         [(feature_vectors, n_clusters, 100)
                                          for n_clusters in range(2, feature_vectors.shape[0])])

            for result in results.get():
                solver, n_clusters = result[0], result[1]
                solvers.append(solver)
                clusters.append(n_clusters)
                inertias.append(solver.inertia_)

        self._solvers = solvers
        self._clusters = clusters
        self._inertias = inertias

        self.update_plot()

        QtWidgets.QApplication.restoreOverrideCursor()

    def _plot(self, x, y, *args, **kwargs):
        self.interface.elbow_curve_plot.pg_canvas.clear()
        plot_item = self.interface.elbow_curve_plot.pg_canvas.plot(x, y, *args, **kwargs)
        # plot_item.sigClicked.connect(self.on_plot_clicked)

    def update_values_dict(self):
        output_dict = {}
        for key in self.interface.key_names:
            output_dict[key] = self.interface.checkboxes[key].isChecked()
        output_dict["clusters"] = int(self.interface.clusters.value())
        if self.interface.time_split_box.isChecked():
            output_dict["time split"] = int(self.interface.time_split_threshold.value())
        else:
            output_dict["time split"] = None
        self.values_dict = output_dict

    def exec_(self):
        QtWidgets.QDialog.exec_(self)
        self.update_values_dict()
        if self._solvers is not None:
            self.chosen_solver = self._solvers[self._clusters.index(self.current_clusters_value)]
        return self.values_dict, self.chosen_solver, self.final_state

def calculate_mapping_bu(virtual_unit_map, data, storage):
    """
    Calculates a mapping for the units based on features like distance.

    The units will be compared pare-wise and sequential.

    **Arguments**

        *data* (:class:`src.neodata.NeoData`):
            This object is needed to get the data
            which will be used to compare the units.

    """
    wave_length = data.get_wave_length()

    for i in range(len(data.blocks) - 1):
        sessions = np.zeros((sum(data.total_units_per_block), 2, wave_length))

        for j, val in enumerate(storage.get_map().mapping[i]):
            if val != 0:
                runit = virtual_unit_map.get_realunit(i, j, data)
                avg = data.get_data("average", runit)
                sessions[j][0] = avg / np.max(avg)
            else:
                sessions[j][0] = np.zeros(wave_length)

        for j, val in enumerate(storage.get_map().mapping[i + 1]):
            if val != 0:
                runit = virtual_unit_map.get_realunit(i + 1, j, data)
                avg = data.get_data("average", runit)
                sessions[j][1] = avg / np.max(avg)
            else:
                sessions[j][1] = np.zeros(wave_length)

        distances = cdist(sessions[:, 0], sessions[:, 1], metric='euclidean')
        threshold = np.mean(distances[distances > 0])
        print(threshold)
        extend = 0
        exclude = []
        for j, val in enumerate(storage.get_map().mapping[i + 1]):
            # print("Executing this in session {}".format(i))
            # print("J: {}, Val: {}".format(j, val))

            if val != 0:
                print(distances[j])

                min_arg = np.argmin(distances[j])
                # print("Min Arg: {}".format(min_arg))

                if min_arg == j:
                    pass
                elif distances[j, min_arg] < threshold and (j, min_arg) not in exclude:
                    # print("Swapping {} and {}".format(j, min_arg))
                    virtual_unit_map.swap(i + 1, j, min_arg)
                    exclude.append((j, min_arg))
                    exclude.append((min_arg, j))
                elif distances[j, min_arg] >= threshold:
                    # print("Swapping {} and {}".format(j, data.total_units_per_block[i + 1] + extend))
                    virtual_unit_map.swap(i + 1, j, data.total_units_per_block[i + 1] + extend)
                    extend += 1
                    print(extend)
                else:
                    print("Logic flawed, check again!")
        print(exclude)
    storage.change_map()

def swan_implementation(virtual_unit_map, data, storage):
    swaps = 0
    # Retrieve mapping from base
    backup_mapping = np.array(storage.get_map().mapping.copy()).T
    pre_mapping = backup_mapping.copy()
    # loop over columns/sessions
    for s in range(pre_mapping.shape[1]):
        # initialize array of mean waveform max amplitudes
        averages = np.zeros((pre_mapping.shape[0]))

        # loop over rows/units
        for j in range(len(averages)):
            # ignore zeros in the map (correspond to non-existent units)
            if pre_mapping[j][s] != 0:
                # Retrieve unit
                runit = virtual_unit_map.get_realunit(s, j, data)
                # Retrieve mean waveform for the unit
                wave = data.get_data("average", runit)
                # Store max amplitude of mean waveform
                averages[j] = np.amax(np.abs(wave))

        #
        # Sort the units in each session by max amplitude of mean waveform
        # and swap the corresponding units in the base.
        #

        # loop over rows/units
        for i in range(len(averages)):
            # retrieve index of max value in session
            max_index = np.argmax(averages[i:]) + i
            if max_index != i:
                virtual_unit_map.swap(s, i, max_index)
                tmp = averages[max_index]
                averages[max_index] = averages[i]
                averages[i] = tmp
                swaps += 1

    storage.change_map()
    # Refresh the mapping using the values in base
    mapping = np.array(storage.get_map().mapping.copy()).T

    # Initialize an array shaped like "mapping" and set all values to np.nan
    history = np.zeros_like(mapping, dtype=np.float)
    history[history == 0.0] = None
    # print("Initial Mapping: {}".format(mapping))

    # Loop over all columns/sessions except the last one
    for i in range(mapping.shape[1] - 1):
        # Loop over all rows/units
        # print("\nFirst loop: {}".format(i))
        for j in range(mapping.shape[0]):
            # ignore zeros in the map (correspond to non-existent units)
            # print("\tSecond Loop: {}".format(j))
            if mapping[j][i] > 0:  # to avoid errors in float comparison
                # Choose (j, i)th unit as the actor and obtain it's mean waveform
                runit_actor = virtual_unit_map.get_realunit(i, j, data)
                actor_wave = data.get_data("average", runit_actor)
                actor_rp = data.get_data("rate profile", runit_actor)

                # Initialize matrix to store distances to the actor unit
                distances_waves = np.zeros((mapping.shape[0], mapping.shape[1]))
                distances_rp = np.zeros((mapping.shape[0], mapping.shape[1]))

                # Loop over all columns/sessions again
                for k in range(mapping.shape[1]):
                    # Loop over all rows/units again
                    # print("\t\tThird Loop: {}".format(k))
                    for l in range(mapping.shape[0]):
                        if mapping[l][k] > 0:
                            # Choose (l, k)th units as the support and obtain it's mean waveform
                            # print("\t\t\tFourth Loop {}\n".format(l))
                            runit_support = virtual_unit_map.get_realunit(k, l, data)
                            support_wave = data.get_data("average", runit_support)
                            support_rp = data.get_data("rate profile", runit_support)
                            #
                            # Calculate and store the Euclidean distance to
                            # support from actor (multiplied by a distance
                            # factor)
                            #
                            distances_waves[l][k] = np.linalg.norm(
                                np.subtract(actor_wave, support_wave))  # * np.exp(abs(k-i))
                            distances_rp[l][k] = np.linalg.norm(np.subtract(actor_rp, support_rp))
                #                            if mapping[l][k] > 0:
                #                                history[l][k] = distances[l][k]

                # print("Distances:\n {}\n".format(distances))

                #
                # Extract the dataset corresponding to all distance measures
                # with non-zero mapping values and calculate the reject threshold
                #

                distances = distances_waves  # + distances_rp

                threshold_dataset = distances[mapping > 0]
                dataset_reject_threshold = np.mean(threshold_dataset)
                # dataset_std = np.std(threshold_dataset)
                # threshold_range_L = threshold_mean + 0.5 * threshold_std
                # dataset_reject_threshold = dataset_mean + 0.5 * dataset_std

                # print("Threshold: {}\n".format(dataset_reject_threshold))

                #
                # The following loop is the main part of the mapping algorithm,
                # which uses the distances between units to rearrange (swap) them.
                #

                # Generate loop range for the loop
                if mapping.shape[1] < 10:
                    loop_range = range(mapping.shape[1])
                else:
                    loop_range = [x for x in range(i + 9) if (i + x) <= mapping.shape[1]]

                # Start looping over sessions/columns
                for k in loop_range:
                    # Find the argument of the minimum in the kth column
                    min_arg = np.argmin(distances[:, k])
                    # print("i: {}, j: {}, k: {}, min_arg: {}\n".format(i, j, k, min_arg))
                    if min_arg == j:
                        # Do nothing if the minimum argument is the same as the row we're looping over
                        pass
                    elif distances[min_arg][k] <= dataset_reject_threshold:
                        # If the distance between the two waveforms falls within
                        # the threshold, we swap the two positions in the kth col
                        if np.isnan(history[min_arg][k]):
                            # If the unit has not been swapped earlier.
                            history[j][k] = distances[min_arg][k]
                            virtual_unit_map.swap(k, min_arg, j)
                            # mapping = backup_mapping.copy()
                            swaps += 1
                        else:
                            # If the unit has been swapped earlier, swap only if
                            # the new distance is less than the old distance
                            prev_dist = history[min_arg][k]
                            curr_dist = distances[min_arg][k]
                            if curr_dist < prev_dist:
                                history[j][k] = distances[min_arg][k]
                                virtual_unit_map.swap(k, min_arg, j)
                                # mapping = backup_mapping.copy()
                                swaps += 1
                    elif distances[min_arg][k] > dataset_reject_threshold:
                        # If the distance between the two waveforms is greater than
                        # the threshold, move the unit to the first available free
                        # plot
                        loc = 0
                        first_zero = np.where(mapping[:, k] == 0)[0][loc]

                        try:
                            while np.sum(mapping[first_zero]) > 0.5:
                                loc += 1
                                first_zero = np.where(mapping[:, k] == 0)[0][loc]
                        except IndexError:
                            first_zero = 0
                        virtual_unit_map.swap(k, first_zero, min_arg)
                        history[min_arg][k] = np.nan
                        mapping = backup_mapping.copy()
                        swaps += 1
                        # print("Swapped {} with empty plot {} on day {}".format(min_arg, first_zero, k))
                        # print("Mapping: {}".format(mapping))
                    else:
                        # If none of the above cases yield true, something is wrong
                        print("Exception reached")
                        print(distances_waves)
                        print(distances_rp)
                        print(distances)
                        mapping = backup_mapping.copy()
    storage.change_map()
    # print("Total swaps: {}\n".format(swaps))

"""
def calculate_mapping(self, data, base):

    Calculates a mapping for the units based on features like distance.

    The units will be compared pare-wise and sequential.

    **Arguments**

        *data* (:class:`src.neodata.NeoData`):
            This object is needed to get the data
            which will be used to compare the units.

    print("Map being calculated")
    #dis = {}
    #for each block except the last one
    for i in range(len(data.blocks) - 1):
        swapped1 = []
        swapped2 = []

        #do it so often that each real unit can find a partner
        for n in range(data.total_units_per_block[i]):
            distances = []

            #for each unit in block i
            for j in range(self.maximum_units):
                if self.mapping[i][j] != 0:
                    unit1 = self.get_realunit(i, j, data)
                    y1 = data.get_data("average", unit1)[0]

                    #for each unit in block i+1
                    for k in range(self.maximum_units):
                        if self.mapping[i+1][k] != 0:
                            unit2 = self.get_realunit(i+1, k, data)
                            y2 = data.get_data("average", unit2)[0]
                            #calculates the distance between the average waveforms
                            distance = np.linalg.norm(np.subtract(y2, y1))

                            #calculate cross correlation {
                            #calculates the inter-spike-interval
                            isi1 = data.get_data("units", unit1)[0]
                            isi2 = data.get_data("units", unit2)[0]
                            len1 = len(isi1)
                            len2 = len(isi2)
                            if len1 > len2:
                                isi1 = isi1[:len2]
                            else:
                                isi2 = isi2[:len1]
                            cor1 = np.corrcoef(y1, y2)
                            cor2 = np.corrcoef(isi1, isi2)
                            c = ((cor1 + cor2) / 2.0)[1][0]
                            #print("unit {} vs. unit {} = {}, crosscorr = {}".format(j, k, distance, c))
                            # }

                            #calculate norm {
                            y = y1 + y2
                            isi = isi1 + isi2
                            av1 = np.mean(y)
                            std1 = np.std(y)
                            av2 = np.mean(isi)
                            std2 = np.std(isi)
                            yn1 = (y1 - av1) / std1
                            yn2 = (y2 - av1) / std1
                            isin1 = (isi1 - av2) / std2
                            isin2 = (isi2 - av2) / std2
                            #fig = plt.figure("dist: {}; corr: {}".format(distance, c))
                            #fig.add_subplot(111)
                            #plt.plot(yn1)
                            #plt.plot(yn2)
                            #plt.plot(isin1)
                            #plt.plot(isin2)
                            #plt.show()
                            #print("unit {} vs. unit {} = {}, y = {} {}, isi = {} {}".format(j, k, distance,
                            #                                                                yn1[:2], yn2[:2],
                            #                                                                isin1[:2], isin2[:2]))
                            #  }

                            if j not in swapped1 and k not in swapped2:
                                distances.append((j, k, distance))
                                #dis[c] = distance
            if distances:
                #finds the min of the distances
                mini = min(distances, key=lambda x: x[2])
                #swap only if distance is under a threshold
                if mini[2] < 200:
                    #swaps the units to have them in the same unit row
                    self.swap(i+1, mini[0], mini[1])
                    swapped1.append(mini[0])
                    swapped2.append(mini[0])
                #otherwise search for an empty row and swap there
                #but only if the units are in the same row
                else:
                    if mini[0] == mini[1]:
                        for u, v in enumerate(self.mapping[i+1]):
                            testing = True
                            for l in range(i+2):
                                if self.mapping[l][u] != 0:
                                    testing = False
                            if testing:
                                self.swap(i+1, mini[1], u)
                                swapped1.append(mini[0])
                                swapped2.append(u)

    #plt.plot(dis.keys(), dis.values(), "o")
    #plt.show()
"""


import discord
from pymongo import MongoClient
from bson.objectid import ObjectId
import os

client = discord.Client()
cli = MongoClient(os.getenv('MONGO'))
db = cli.heroku_zq5cdk49

bal = db.doc.find_one({'_id': ObjectId('5d5779523f5cc0f1bdd8511a')})
status = db.doc.find_one({'_id': ObjectId('5d5779523f5cc0f1bdd8511b')})
rqtype = db.doc.find_one({'_id': ObjectId('5d5779523f5cc0f1bdd8511c')})
rqdesc = db.doc.find_one({'_id': ObjectId('5d5779523f5cc0f1bdd8511d')})
rqserv = db.doc.find_one({'_id': ObjectId('5d5779523f5cc0f1bdd8511e')})
mess = db.doc.find_one({'_id': ObjectId('5d5779523f5cc0f1bdd8511f')})
slots = db.doc.find_one({'_id': ObjectId('5d5b51f233ba3bc7166344e3')})

typ = {}
version = "v1.2.5"

@client.event
async def on_ready():
    guild = client.get_guild(469591475999604746)
    channel = client.get_channel(612112677166252050)
    await channel.send(f"""<:bfyYes:613851579266760733> Bot started on **{version}**""")
    await client.change_presence(activity=discord.Activity(name=f"""{guild.member_count} members""", type=discord.ActivityType.watching))

@client.event
async def on_member_join(member):
    guild = client.get_guild(469591475999604746)  # Blob-ify
    guild1 = client.get_guild(591038750273044501)  # Pixel Blob Central FLAG!
    if member.guild == guild:
        await client.change_presence(activity=discord.Activity(name=f"""{guild.member_count} members""", type=discord.ActivityType.watching))
        channel = client.get_channel(469591475999604750)
        await channel.send(f""":tada: Hey fellow **blob {member.mention}**, welcome to **Blob-ify**! You're the **{member.guild.member_count}th** blob to join our growing blob community! To get started, please check <#470303845495341057>. In order to request a blob, please check <#526232642425978892>. Have fun! :heart:""")
    elif member.guild == guild1:
        channel = client.get_channel(591038750847533058)
        await channel.send(f""":tada: Hey fellow **blob {member.mention}**, welcome to **{member.guild.name}**! You're the **{member.guild.member_count}th** blob to join our growing blob community! Have fun! :heart:""")

@client.event
async def on_member_remove(member):
    guild = client.get_guild(469591475999604746)
    if member.guild == guild:
        await client.change_presence(activity=discord.Activity(name=f"""{guild.member_count} members""", type=discord.ActivityType.watching))

@client.event
async def on_message(message):
    guild = client.get_guild(469591475999604746)  # Blob-ify
    lower = message.content.lower()
    split = lower.split(" ")
    if message.author.id not in typ.keys():
        typ[message.author.id] = 0
    if str(message.author.id) not in status.keys():
        status[str(message.author.id)] = 0
    if str(message.author.id) not in bal.keys():
        bal[str(message.author.id)] = 0
    if message.author.guild != guild:
        typ[message.author.id] = -1
    if typ[message.author.id] == -1:
        if message.author.guild == guild:
            typ[message.author.id] = 0
    if lower == "cancel":
        if typ[message.author.id] > 0:
            await message.channel.send("<:bfyNo:613851611600781342> **The requested action has been canceled.**")
            typ[message.author.id] = 0
            rqtype[str(message.author.id)], rqdesc[str(message.author.id)], rqserv[str(message.author.id)] = "", "", ""
    elif message.author.id == 292953664492929025:
        if "w.exc" in message.content:
            mem = message.content.split(" ")
            user = mem[1]
            if user not in bal.keys():
                bal[user] = 0
            await message.channel.send("<:bfyYes:613851579266760733> 2 <:blobpoint:577932728033476611> have been added to your account.")
            bal[user] += 2
            channel = client.get_channel(470347448066375681)
            await channel.send(f"""**Bank Update:** Bal of {str(client.get_user(user))} now {bal[user]}""")
    else:
        if typ[message.author.id] <= 0:
            if lower == "w.help":
                embed = discord.Embed(title="Blob Bot Help", description="The full list of W1Z4RD's commands", color=0x0080c0)
                embed.add_field(name="w.request", value="Request a blob")
                embed.add_field(name="w.exchange", value="Exchange 2 BLOB POINTS for 2,500 BLOB COINS")
                embed.add_field(name="w.balance", value="Check your BLOB POINT balance")
                embed.add_field(name="w.request delete", value="Delete your current blob request")
                embed.set_thumbnail(url="https://cdn.discordapp.com/attachments/470304317748805652/581650589423763463/wiz.png")
                embed.set_footer(text=f"""Created by Tri#4823 ~ W1Z4RD {version}""")
                await message.channel.send(content=None, embed=embed)
            elif lower == "w.exchange":
                mem = str(message.author.id)
                truemem = str(message.author)
                if mem not in bal.keys():
                    bal[mem] = 0
                if bal[mem] > 1:
                    await message.channel.send("<:bfyYes:613851579266760733> <@266319920009183242> will complete the transaction within 24 hours.")
                    bal[mem] -= 2
                    channel = client.get_channel(470347448066375681)
                    await channel.send(f"""**Bank Update:** Bal of {truemem} now {bal[mem]}""")
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to do so.")
            elif lower == "w.request":
                if slots["used"] < slots["max"] or guild.get_role(600678332320710666) in message.author.roles:
                    if status[str(message.author.id)] == 0:
                        embed = discord.Embed(title="W1Z Request Command", description="A full list of all blob types!", color=0x0080c0)
                        embed.set_image(url="https://cdn.discordapp.com/attachments/470304317748805652/624941100418859018/5309c984-4d97-40c1-bbeb-24f5045d5ddf.png")
                        embed.set_thumbnail(url="https://cdn.discordapp.com/attachments/470304317748805652/581650589423763463/wiz.png")
                        embed.set_footer(text=f"""Created by Tri#4823 ~ W1Z4RD {version}""")
                        await message.channel.send(content="__Please respond with the respective number, or `cancel`__", embed=embed)
                        typ[message.author.id] += 1
                    else:
                        await message.channel.send("<:bfyNo:613851611600781342> You currently have a request in the queue. Either wait until the request is done or delete your request to request again. You can also choose to edit your request.")
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> The maximum amount of request slots has been reached. Either wait for a slot to open or boost the server to bypass this.")
            elif lower == "w.balance":
                await message.channel.send(f"""**Your Balance:** {bal[str(message.author.id)]} <:blobpoint:577932728033476611>""")
            elif lower == "w.bal":
                await message.channel.send(f"""**Your Balance:** {bal[str(message.author.id)]} <:blobpoint:577932728033476611>""")
            elif "w.say" == split[0]:
                admin = guild.get_role(471024426356310028)
                if admin in message.author.roles:
                    say = message.content.split(" ", 1)
                    await message.channel.send(f"""{say[1]}""")
                    await message.delete()
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have the required permissions to use this command.")
            elif "w.add" == split[0]:
                admin = guild.get_role(471024426356310028)
                if admin in message.author.roles:
                    say = message.content.split(" ", 2)
                    user = client.get_user(int(say[1]))
                    if str(user.id) not in bal.keys():
                        bal[str(user.id)] = 0
                    bal[str(user.id)] += int(say[2])
                    await message.channel.send(f"""<:bfyYes:613851579266760733> **{say[2]}bp** has/have been added to {str(user)}'s balance""")
                    channel = client.get_channel(470347448066375681)
                    await channel.send(f"""**Bank Update:** Bal of {str(user)} now {bal[str(user.id)]}""")
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have the required permissions to use this command.")
            elif "w.remove" == split[0]:
                admin = guild.get_role(471024426356310028)
                if admin in message.author.roles:
                    say = message.content.split(" ", 2)
                    user = client.get_user(int(say[1]))
                    if str(user.id) not in bal.keys():
                        bal[str(user.id)] = 0
                    bal[str(user.id)] -= int(say[2])
                    await message.channel.send(f"""<:bfyYes:613851579266760733> **{say[2]}bp** has/have been removed from {str(user)}'s balance""")
                    channel = client.get_channel(470347448066375681)
                    await channel.send(f"""**Bank Update:** Bal of {str(user)} now {bal[str(user.id)]}""")
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have the required permissions to use this command.")
            elif "w.slots" == split[0]:
                admin = guild.get_role(471024426356310028)
                if admin in message.author.roles:
                    if "set" == split[1]:
                        slots["max"] = int(split[2])
                        await message.channel.send(f"""<:bfyYes:613851579266760733> Max slots now **{split[2]}**.""")
                    elif "check" == split[1]:
                        await message.channel.send(f"""There are **{slots["used"]} out of {slots["max"]} slots used.**""")
                    elif "forceremove" == split[1]:
                        if slots["used"] > 0:
                            slots["used"] -= 1
                            await message.channel.send(f"""<:bfyYes:613851579266760733> Used slots reduced by 1, now {slots["used"]}.""")
                        else:
                            await message.channel.send("<:bfyNo:613851611600781342> There are no requests in the queue or there are no slots used.")
                    elif "forceadd" == split[1]:
                        slots["used"] += 1
                        await message.channel.send(f"""<:bfyYes:613851579266760733> Forcefully used a slot. Used slots now {slots["used"]}.""")
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have the required permissions to use this command.")
            elif lower == "w.emojiupdate":
                admin = guild.get_role(471024426356310028)
                if admin in message.author.roles:
                    channel = client.get_channel(578237505770618891)
                    async for message1 in channel.history():
                        await message1.delete()
                    for emoji in guild.emojis:
                        await channel.send(f"""{emoji} ~ `{emoji.name}`""")
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have the required permissions to use this command.")
            elif "w.request" == split[0]:
                if "delete" == split[1]:
                    if status[str(message.author.id)] > 0:
                        await message.channel.send("Are you sure you would like to delete your request? Your <:blobpoint:577932728033476611> will be refunded. Please `confirm` or `cancel`.")
                        typ[message.author.id] = 5
                    else:
                        await message.channel.send("<:bfyNo:613851611600781342> You currently have no active requests to delete.")
                elif "claim" == split[1]:
                    print()
                elif "submit" == split[1]:
                    print()
                elif "check" == split[1]:
                    stat = status[str(message.author.id)]
                    if stat > 0:
                        embed = discord.Embed(title=f"""Your Request""", color=0x0080c0)
                        embed.add_field(name="Blob Type", value=rqtype[str(message.author.id)])
                        embed.add_field(name="Blob Description", value=rqdesc[str(message.author.id)])
                        embed.add_field(name="Server Blob?", value=rqserv[str(message.author.id)])
                        embed.set_thumbnail(
                            url="https://cdn.discordapp.com/attachments/470304317748805652/581650589423763463/wiz.png")
                        embed.set_footer(text=f"""Created by Tri#4823 ~ W1Z4RD {version}""")
                        if stat == 1:
                            await message.channel.send(content="Your request is **unclaimed**. You can edit it with the `edit` subommand.", embed=embed)
                        elif stat == 2:
                            await message.channel.send(content="Your request was claimed by artist [ARTIST]. The request is no longer able to be edited")
                    else:
                        await message.channel.send("<:bfyNo:613851611600781342> You do not have a request in the queue. Use the `w.request` command to create one.")
                elif "forcedelete" == split[1]:
                    admin = guild.get_role(471024426356310028)
                    if admin in message.author.roles:
                        user = client.get_user(int(split[2]))
                        if status[str(user.id)] > 0:
                            status[str(user.id)] = 0
                            slots["used"] -= 1
                            if rqtype[str(user.id)] == "Animated Blob":
                                bal[str(user.id)] += 6
                            if rqtype[str(user.id)] == "Normal Blob":
                                bal[str(user.id)] += 1
                            elif rqtype[str(user.id)] == "Bongo Cat":
                                bal[str(user.id)] += 7
                            elif rqtype[str(user.id)] == "Peep":
                                bal[str(user.id)] += 3
                            elif rqtype[str(user.id)] == "Animated Peep":
                                bal[str(user.id)] += 8
                            elif rqtype[str(user.id)] == "Full Blob Pack":
                                bal[str(user.id)] += 40
                            elif rqtype[str(user.id)] == "Custom Emoji":
                                bal[str(user.id)] += 10
                            elif rqtype[str(user.id)] == "Custom Emoji Pack":
                                bal[str(user.id)] += 80
                            await message.channel.send(f"""<:bfyYes:613851579266760733> You have forcefully deleted {str(user)}'s request. You may proceed to delete the message in the queue.""")
                            channel = client.get_channel(470347448066375681)
                            await channel.send(f"""**Bank Update:** Bal of {str(user)} now {bal[str(user.id)]}""")
                        else:
                            await message.channel.send("<:bfyNo:613851611600781342> That user has no request in the queue.")
                    else:
                        await message.channel.send("<:bfyNo:613851611600781342> You do not have the required permissions to use this subcommand.")
        elif typ[message.author.id] == 1:
            if "1" in message.content:
                if bal[str(message.author.id)] >= 1:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Normal Blob"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            elif "2" in message.content:
                if bal[str(message.author.id)] >= 6:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Animated Blob"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            elif "3" in message.content:
                if bal[str(message.author.id)] >= 7:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Bongo Cat"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            elif "4" in message.content:
                if bal[str(message.author.id)] >= 3:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Peep"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            elif "5" in message.content:
                if bal[str(message.author.id)] >= 8:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Animated Peep"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            elif "6" in message.content:
                if bal[str(message.author.id)] >= 40:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Full Blob Pack"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            elif "7" in message.content:
                if bal[str(message.author.id)] >= 10:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Custom Emoji"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            elif "8" in message.content:
                if bal[str(message.author.id)] >= 80:
                    await message.channel.send("__Great Choice! Now, please state what you would like your blob(s) to look like, or say `cancel`.__")
                    rqtype[str(message.author.id)] = "Custom Emoji Pack"
                    typ[message.author.id] += 1
                else:
                    await message.channel.send("<:bfyNo:613851611600781342> You do not have enough <:blobpoint:577932728033476611> to request this item. Pick a different item or `cancel`.")
            else:
                await message.channel.send("<:bfyNo:613851611600781342> That was not a valid response. Send a number from 1 through 8.")
        elif typ[message.author.id] == 2:
            await message.channel.send("__Great! One last thing. Would you like this to be a server blob?__")
            await message.channel.send("Server blobs represent you in the server, and are made into emojis that you can use with Nitro.")
            await message.channel.send("However, only NORMAL BLOB requests can qualify. So what do you say, or `cancel`.")
            rqdesc[str(message.author.id)] = message.content
            typ[message.author.id] += 1
        elif typ[message.author.id] == 3:
            await message.channel.send("Cool! Now please confirm the following details before making your purchase.")
            await message.channel.send("The appropriate amount of <:blobpoint:577932728033476611> will be deducted from your account as listed in <#526232642425978892>.")
            embed = discord.Embed(title="Confirm Information", description="Either `confirm` or `cancel`", color=0x0080c0)
            embed.add_field(name="Blob Type", value=rqtype[str(message.author.id)])
            embed.add_field(name="Blob Description", value=rqdesc[str(message.author.id)])
            embed.add_field(name="Server Blob?", value=message.content)
            embed.set_thumbnail(url="https://cdn.discordapp.com/attachments/470304317748805652/581650589423763463/wiz.png")
            embed.set_footer(text=f"""Created by Tri#4823 ~ W1Z4RD {version}""")
            await message.channel.send(embed=embed)
            rqserv[str(message.author.id)] = message.content
            typ[message.author.id] += 1
        elif typ[message.author.id] == 4:
            if lower == "confirm":
                await message.channel.send("<:bfyYes:613851579266760733> The blob request has been added to the queue!")
                await message.channel.send("You will receive notifications as your blob is being made.")
                await message.channel.send("Send any image references to <@266319920009183242>.")
                embed = discord.Embed(title=f"""{message.author}""", color=0x0080c0)
                embed.add_field(name="Blob Type", value=rqtype[str(message.author.id)])
                embed.add_field(name="Blob Description", value=rqdesc[str(message.author.id)])
                embed.add_field(name="Server Blob?", value=rqserv[str(message.author.id)])
                embed.set_thumbnail(url="https://cdn.discordapp.com/attachments/470304317748805652/581650589423763463/wiz.png")
                embed.set_footer(text=f"""Created by Tri#4823 ~ W1Z4RD {version}""")
                channel = client.get_channel(611768294315130891)
                await channel.send(content=f"""**Incoming Blob Request!** From {message.author.mention}""", embed=embed)
                typ[message.author.id] = 0
                status[str(message.author.id)] = 1
                slots["used"] += 1
                if rqtype[str(message.author.id)] == "Animated Blob":
                    bal[str(message.author.id)] -= 6
                elif rqtype[str(message.author.id)] == "Normal Blob":
                    bal[str(message.author.id)] -= 1
                elif rqtype[str(message.author.id)] == "Bongo Cat":
                    bal[str(message.author.id)] -= 7
                elif rqtype[str(message.author.id)] == "Peep":
                    bal[str(message.author.id)] -= 3
                elif rqtype[str(message.author.id)] == "Animated Peep":
                    bal[str(message.author.id)] -= 8
                elif rqtype[str(message.author.id)] == "Full Blob Pack":
                    bal[str(message.author.id)] -= 40
                elif rqtype[str(message.author.id)] == "Custom Emoji":
                    bal[str(message.author.id)] -= 10
                elif rqtype[str(message.author.id)] == "Custom Emoji Pack":
                    bal[str(message.author.id)] -= 80
                channel = client.get_channel(470347448066375681)
                await channel.send(f"""**Bank Update:** Bal of {str(message.author)} now {bal[str(message.author.id)]}""")
            else:
                await message.channel.send("<:bfyNo:613851611600781342> That was not a valid response. Please `confirm` or `cancel`.")
        elif typ[message.author.id] == 5:
            if lower == "confirm":
                await message.channel.send("<:bfyYes:613851579266760733> <@266319920009183242> will proceed to delete your request and refund your <:blobpoint:577932728033476611>. At this point you may also start a new request.")
                status[str(message.author.id)] = 0
                typ[message.author.id] = 0
                slots["used"] -= 1
                if rqtype[str(message.author.id)] == "Animated Blob":
                    bal[str(message.author.id)] += 6
                elif rqtype[str(message.author.id)] == "Normal Blob":
                    bal[str(message.author.id)] += 1
                elif rqtype[str(message.author.id)] == "Bongo Cat":
                    bal[str(message.author.id)] += 7
                elif rqtype[str(message.author.id)] == "Peep":
                    bal[str(message.author.id)] += 3
                elif rqtype[str(message.author.id)] == "Animated Peep":
                    bal[str(message.author.id)] += 8
                elif rqtype[str(message.author.id)] == "Full Blob Pack":
                    bal[str(message.author.id)] += 40
                elif rqtype[str(message.author.id)] == "Custom Emoji":
                    bal[str(message.author.id)] += 10
                elif rqtype[str(message.author.id)] == "Custom Emoji Pack":
                    bal[str(message.author.id)] += 80
                channel = client.get_channel(471878672677208084)
                await channel.send(f"""{str(message.author)} has deleted their request.""")
                channel = client.get_channel(470347448066375681)
                await channel.send(f"""**Bank Update:** Bal of {str(message.author)} now {bal[str(message.author.id)]}""")
            else:
                await message.channel.send("<:bfyNo:613851611600781342> That was not a valid response. Please `confirm` or `cancel`.")

    dicts = [bal, status, rqtype, rqdesc, rqserv, mess, slots]
    for d in dicts:
        db.doc.replace_one({'_id': d['_id']}, d, True)

@client.event
async def on_guild_emojis_update(guild0, before, after):
    channel = 0
    if guild0.id == 469591475999604746:
        channel = client.get_channel(570397486141669406)
    emoadd, emoaddname, emoremove, emoremovename, lis, lis2 = "", "", "", "", [], []
    for emoji in before:
        for emoji2 in after:
            lis.append(emoji2.name)
        if emoji.name not in lis:
            emoremove = emoji
            emoremovename = emoji.name
    for emoji in after:
        for emoji2 in before:
            lis2.append(emoji2.name)
        if emoji.name not in lis2:
            emoadd = emoji
            emoaddname = emoji.name
    if emoremove == "":  # If an emoji was added
        await channel.send(f"""<:w1:579473596041265152> **Added** {emoadd} `{emoaddname}`""")
    else:  # If an emoji was removed
        if emoadd == "":  # If an emoji was ONLY removed
            await channel.send(f"""<:w2:579473617134288898> **Removed** `{emoremovename}`""")
        else:  # If an emoji was renamed
            await channel.send(f"""<:w3:579473645706018827> {emoadd} **Renamed** `{emoremovename}` to `{emoaddname}`""")

client.run(os.getenv('TOKEN'))