import dash_bootstrap_components as dbc
import pandas as pd
import plotly.express as px
from dash import html, dcc
import plotly.io as pio
pio.templates.default = "simple_white"

class FastViewCo2(object):
    def __init__(self, data):
        self.df_co2 = data
        Un_Kt = 1000
        co2_country = data.groupby("name")['CO2'].sum()
        co2_country_acum = sum(co2_country)
        co2_country_df = pd.DataFrame(co2_country).reset_index()
        co2_country_df['% Representation'] = (co2_country_df['CO2'] / co2_country_acum) * 100
        co2_country_df['CO2'] = co2_country_df['CO2'] / Un_Kt
        co2_country_df = co2_country_df.sort_values(by='% Representation', ascending=False)

        co2_country_df['acumulado'] = co2_country_df['% Representation'].cumsum()
        co2_country_df = co2_country_df.reset_index()
        co2_country_df = co2_country_df

        ten_top = co2_country_df.iloc[10, 4]
        ten_top = str(f'{ten_top:.2f}' + '%')

        top_countries = list(co2_country_df['name'].head(8))
        g20_df = data[data['name'].isin(top_countries)]

        self.co2_country_df = co2_country_df
        self.ten_top = ten_top
        self.top_countries = top_countries
        self.g20_df = g20_df

    def get_fig_geo_co2(self):
        data = self.df_co2
        fig_geo_co2 = px.choropleth(data, locations='code',
                                    animation_frame='year',
                                    color='CO2',
                                    hover_name='name',
                                    color_continuous_scale='temps_r'
                                    )
        fig_geo_co2.update_layout(height=350)
        return fig_geo_co2

    def get_fig_countries_co2(self):
        fig_countries_co2 = px.area(self.g20_df.sort_values(by='CO2', ascending=False),
                                    x="year",
                                    y="CO2",
                                    color="name",
                                    line_group="name")
        fig_countries_co2.update_layout(height=350, )
        return fig_countries_co2

    def get_fig_pie_co2(self):
        fig_pie_co2 = px.pie(self.co2_country_df.head(10).sort_values(by='CO2', ascending=False),
                             values='% Representation', names='name',
                             hover_data=['name'], labels={'% Representation': '%'})
        fig_pie_co2.update_traces(textposition='inside', textinfo='label+value')
        return fig_pie_co2

    def get_fig_scatter(self):
        fig_scatter = px.scatter(
            self.df_co2, x='GDP', y='CO2',
            animation_frame='year',
            animation_group='CO2',
            size='pop',
            color='code_region',
            hover_name='name',
            log_x=True,
            range_x=[100, 100000],
            range_y=[100, 12000000]
        )
        return fig_scatter

    def get_fig_area_case_ukraine(self):
        case_ukraine = px.area(self.df_co2[self.df_co2['name'] == 'Ukraine'],
                               x="year",
                               y="CO2",
                               color="name",
                               line_group="name")
        return case_ukraine

    def get_html_components(self):
        return dbc.CardBody([
            dbc.Row([
                dbc.Col([html.Label("Overall view", className="align-middle")])],
                style={
                    "background-color": "#EEFFD6",
                    'height': '35px',
                    'border-radius': '5px',
                    'padding': '5px 0px',
                    'text-align': 'left'
                }
            ),
            dbc.Row([
                dbc.Col([dcc.Graph(id='id-geo-co2', figure=self.get_fig_geo_co2())], width=6),
                dbc.Col([dcc.Graph(id='id-area-country-co2', figure=self.get_fig_countries_co2())], width=6),
            ]),
            dbc.Row(
                [dbc.Col(html.Div('Comparative with other economics indicators'))],
                style={
                    "background-color": "#EEFFD6",
                    'height': '35px',
                    'border-radius': '5px',
                    'padding': '5px 0px',
                    'text-align': 'left',
                }
            ),
            dbc.Row([
                dbc.Col(
                    [
                        html.Label('% Emitions top ten', className='card-tittle'),
                        html.H4(self.ten_top)
                    ],
                    style={
                        'border-radius': '5px',
                        'margin-top': '30px',
                        'padding': '30px 0 0 30px'
                    },
                    width=2),
                dbc.Col(
                    [
                        dcc.Graph(
                            id='id-pie-co2',
                            config={'displayModeBar': False},
                            figure=self.get_fig_pie_co2())
                    ],
                    width=4),
                dbc.Col(
                    [
                        dcc.Graph(
                            id='id-scatter-co2',
                            config={'displayModeBar': False},
                            figure=self.get_fig_scatter())
                    ],
                    width=6),
            ]),
            dbc.Row([
                dbc.Col(html.Div('Case Ukraine'),)],
                style={
                    "background-color": "#EEFFD6",
                    'height': '35px',
                    'border-radius': '5px',
                    'padding': '5px 0px',
                    'text-align': 'left',
                }
            ),
            dbc.Row(
                [
                    dbc.Col(
                        [
                            dcc.Graph(
                                id='id-area-case-ukraine',
                                config={'displayModeBar': False},
                                figure=self.get_fig_area_case_ukraine())
                        ],
                        width=12),
            ]),
        ])


from enum import Enum

users = []
class Account(Enum):
    USD = "USD"
    KZT = "KZT"
    RUB = "RUB"
    EUR = "EUR"

class BankAccount:
    name: str
    surname: str
    amount: int = 0
    account: Account = 'KZT'

    def __init__(self, name:str, surname:str, account:Account) -> None:
        self.name = name
        self.surname = surname
        self.account = account

    def set_user(self, name: str, surname: str, account: Account) -> None:
        self.name = name
        self.surname = surname
        self.account = account
    def set_amount(self, amount:int):
        self.amount = amount

    def name(self) -> str:
        return self.name
    def surname(self) -> str:
        return self.surname
    def account(self) -> Account:
        return self.account
    def amount(self) -> int:
        return self.amount

    def addToBankAccount(self, x:int):
        self.amount += x
        print("Счет успешно пополнен")

    def substractFromBankAccount(self, x:int):
        if self.amount < x:
            print("Недостаточно средств")
        else:
            self.amount -= x
            print("Вы успешно сняли деньги")

    def moneyConversion(self, b):
        a = self.account
        kurs_kzt = {"KZT":1, "RUB":7.53, "USD":470.69, "EUR":496.17}
        kurs_rub = {"RUB":1, "KZT":0.13, "USD":62.52, "EUR":65.90 }
        kurs_usd = {"RUB":0.016, "KZT":0.0021, "USD":1, "EUR":1.05 }
        kurs_eur = {"RUB":0.015, "KZT":0.0020, "USD":0.95, "EUR":1}
        if b == "KZT":
            self.amount *= kurs_kzt[a]
            self.account = "KZT"
        elif b == "RUB":
            self.amount *= kurs_rub[a]
            self.account = "RUB"
        elif b == "USD":
            self.amount *= kurs_usd[a]
            self.account = "USD"
        elif b == "EUR":
            self.amount *= kurs_eur[a]
            self.account = "EUR"

    def __repr__(self):
        return f'{self.name} {self.surname} {self.amount} {self.account}'

def create_account(name: str, surname: str, amount:int, account: Account) -> BankAccount:
    user = BankAccount(name=name, surname=surname, account=account)
    user.set_amount(amount=amount)
    users.append(user)

    return user

def get_user(name: str, surname: str) -> BankAccount | None:
    user = next((u for u in users if name == u.name and surname == u.surname), None)
    if not user:
        print('User not found')
        return
    return f'{user.name} {user.surname}. Ваш счет: {user.amount} {user.account}'

def delete_user(name: str, surname: str) -> BankAccount | None:
    user = next((u for u in users if name == u.name and surname == u.surname), None)
    if not user:
        print('User not found')
        return
    users.remove(user)
    print("Пользователь удален")

fake_account = BankAccount(name="Mark", surname="Doe", account="RUB")

fake_account.amount = 1500
users.append(fake_account)

d = {"1":"KZT", "2":"RUB", "3":"USD", "4":"EUR"}
while(True):
    inp = input("Выберите действие: \n 1. Создание пользователя \n 2. Выбрать пользователя \n 3. Удалить пользователя \n 0. Выход \n")
    if inp == '0':
        break
    elif inp == "1":
        name = input("Введите имя: ")
        surname = input("Введите фмаилия: ")
        v = input("Выберите курс валют: \n 1. KZT \n 2. RUB \n 3. USD \n 4. EUR \n")
        account = d[v]
        user = create_account(name=name, surname=surname, account=account, amount=0)
        print("Ваш аккаунт создан. ")
    elif inp == '2':
        name = input("Введите имя: ")
        surname = input("Введите фмаилия: ")
        print(get_user(name=name, surname=surname))
        while(True):
            inp2 = input("Выберите операцию: \n 1. Добавить на счет \n 2. Снять деньги \n 3. Сконвертировать \n 0. Назад\n")
            if inp2 == "0":
                break
            elif inp2 == "1":
                x = int(input("Введите суммy: \n"))
                user.addToBankAccount(x)
                print(get_user(name=name, surname=surname))
            elif inp2 == "2":
                x = int(input("Введите суммy: \n"))
                user.substractFromBankAccount(x)
                print(get_user(name=name, surname=surname))
            elif inp2 == "3":
                b = input("Выберите курс валют: \n 1. KZT \n 2. RUB \n 3. USD \n 4. EUR \n")
                account = d[b]
                user.moneyConversion(account)
                print(get_user(name=name, surname=surname))
    elif inp == "3":
        name = input("Введите имя: ")
        surname = input("Введите фмаилия: ")
        delete_user(name=name, surname=surname)


import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

def iterative_algorithm(img):
    img_array = np.array(img).astype(np.float32)
    I=img_array
    Ti=50 #Set initial arbitrary value as threshold value
    b=1
    m,n=I.shape
    diff = 255
    count = 1
    while diff!=0:
        foreground=0
        background=0
        sum_fg=0
        sum_bg=0
        for i in range(1,m):
             for j in range(1,n):
                tmp=I[i][j]
                if tmp>=Ti:
                    foreground = foreground + 1
                    sum_fg= sum_fg + int(tmp)
                else:
                    background = background + 1
                    sum_bg = sum_bg + int(tmp)

        mean_fg = int(sum_fg/foreground)
        mean_bg = int(sum_bg/background)
        mean = int((mean_bg+mean_fg)/2)
        diff = abs(mean - Ti)
        Ti=mean
        print("Iteration " + str(count) + " Threshold value : " + str(Ti))
        count = count + 1

    return Ti

img = cv2.imread("C:/Users/imrk0/Desktop/Github/Image_Processing_nd_Computer_Vision_Programs/00_img/img10.jpg")

img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
orignal_img = img
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
img = cv2.resize(gray,(1017,1199))

final_threshold_value = iterative_algorithm(img)
print("Final Threshold value : " + str(final_threshold_value))
ret1, th1 = cv2.threshold(img, final_threshold_value, 255, cv2.THRESH_BINARY)
plot1 = plt.figure("Original")
plt.imshow(orignal_img)
plot2 = plt.figure("Segmented image : ")
plt.imshow(th1,cmap=cm.gray)
plt.show()


from django.forms import ModelForm
from django.http import HttpResponse, HttpResponseNotFound, HttpResponseGone
from django.shortcuts import render, redirect, get_object_or_404
import datetime

from app.models import Book

class BookForm(ModelForm):
    class Meta:
        model = Book
        fields = ['name', 'pages', 'date_written', 'type']

def home(request):
    html = """

Django CRUD Example

    Book list

    """
    return HttpResponse(html)

def book_list(request, template_name='book_list.html'):
    books = Book.objects.all()
    data = {}
    data['object_list'] = books
    return render(request, template_name, data)

def book_list_range(request, y1, m1, d1, y2, m2, d2, template_name='book_list.html'):
    date1 = datetime.date(int(y1), int(m1), int(d1))
    date2 = datetime.date(int(y2), int(m2), int(d2))
    books = Book.objects.exclude(date_written__lt=date1).exclude(date_written__gt=date2)
    print(len(books), date1, date2)
    data = {}
    data['object_list'] = books
    data['date_range_start'] = date1
    data['date_range_end'] = date2
    return render(request, template_name, data)

def book_create(request, template_name='book_form.html'):
    form = BookForm(request.POST or None)
    if form.is_valid():
        form.save()
        return redirect('book_list')
    return render(request, template_name, {'form':form})

def book_update(request, pk):
    book= get_object_or_404(Book, pk=pk)
    form = BookForm(request.POST or None, instance=book)
    print(request.POST);
    if form.is_valid():
        form.save()
        return HttpResponse(status=200)
    return HttpResponse(status=400)

def book_delete(request, pk):
    book= get_object_or_404(Book, pk=pk)
    if request.method=='DELETE':
        book.delete()
        return HttpResponse(status=204)
    return HttpResponse(status=405)


"""Some useful type aliases relevant to this project."""
import pathlib
from typing import AbstractSet, Callable, List, Mapping, Optional, Tuple, Union

import torch

Layer = Union[int, str]
Unit = Tuple[Layer, int]

PathLike = Union[str, pathlib.Path]

TensorPair = Tuple[torch.Tensor, torch.Tensor]
TensorTriplet = Tuple[torch.Tensor, torch.Tensor, torch.Tensor]
OptionalTensors = Tuple[Optional[torch.Tensor], ...]
StateDict = Mapping[str, torch.Tensor]

Device = Union[str, torch.device]

# All strings are also Sequence[str], so we have to distinguish that we
# mean lists or tuples of strings, or sets of strings, not other strings.
StrSequence = Union[List[str], Tuple[str, ...]]
StrSet = AbstractSet[str]
StrIterable = Union[StrSet, StrSequence]
StrMapping = Mapping[str, str]

# Some common transforms.
TransformTensor = Callable[[torch.Tensor], torch.Tensor]
TransformStr = Callable[[str], str]
TransformStrSeq = Callable[[StrSequence], StrSequence]


import json

from django.test import TestCase

from wagtail.models import Site

from ..models import GeneralPage

class TestGeneral(TestCase):
    def setUp(self):
        root = Site.objects.get().root_page

        self.general_page = GeneralPage(
            title="General page",
            teaser_text="test",
            body=json.dumps(
                [
                    {"type": "paragraph", "value": {"text": "This is a paragraph"}},
                ]
            ),
        )
        root.add_child(instance=self.general_page)

    def test_view_uses_correct_template(self):
        url = self.general_page.get_url()
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, "generic_pages/general_page.html")


from helpers import *
import cv2
import tensorflow as tf
import json
import sys

# Checking for incorrect usage
if len(sys.argv) != 2:
    print("Usage: python main.py path_to_image")
    exit(-1)
image_file = sys.argv[1]

# These are set to the default names from exported models, update as needed.
INPUT_TENSOR_NAME = 'image_tensor:0'
OUTPUT_TENSOR_NAMES = ['detected_boxes:0', 'detected_scores:0', 'detected_classes:0']
filename = "Object_Identification_Model/model.pb"
labels_filename = "Object_Identification_Model/labels.txt"

# Create tf graph and return list of labels
labels = create_tf_graph(filename, labels_filename)
image = cv2.imread(image_file)
if image is None:
    print("Invalid Path")
    exit(-1)
image = resize_down_to_1600_max_dim(image)
# Create clone to visualize on
clone = image.copy()
# Necessary preprocessing
network_input_size = 320
augmented_image = cv2.resize(image, (network_input_size, network_input_size))
# Extracting probabilities and box bounds
pred = predict_from_graph(OUTPUT_TENSOR_NAMES, augmented_image, INPUT_TENSOR_NAME)
final = []
# Scaling percentage box bounds
h, w = image.shape[:2]
for i in pred[0]:
    for j in range(4):
        if j % 2 == 0:
            i[j] *= w
        else:
            i[j] *= h
        if i[j] < 0:
            i[j] = 0
coords = pred[0]
# Applying non-maxima suppression
ind = tf.image.non_max_suppression(pred[0], pred[1], len(coords), iou_threshold=0.8,
                                   score_threshold=float('-inf'), name=None)
# Only taking into account images with a high probability
for i in ind:
    j = pred[1][i]
    if j > 0.6:
        j = [j, pred[0][i], i]
        final.append(j)
coords_list = []
# Creating a final list of box bounds which only included those of selected boxes
for j in final:
    coords_list.append(list(coords[j[2]]))
# Order boxes according to their rows and columns
# Rows and columns of each box stored in loc
loc, coords_list = order_boxes(coords_list)
# Extracting all lettuce images from main image
images = []
for i in coords_list:
    images.append(image[int(i[1]):int(i[3]), int(i[0]):int(i[2])])
if len(images) == 0:
    print("No lettuces detected")
    exit(-1)
# Calculating green intensity of each leaf
green_intensity = []
for i in images:
    total = 0
    count = 0
    for j in i:
        for x in j:
            if int(x[1]) > 0.75*(int(x[0])+int(x[2])):
                total += x[1]
                count += 1
    avg = total/count
    if avg > 220:
        avg = 220
    green_intensity.append(1-avg/220)
# Calculating the relative size of each lettuce
sizes = []
total = 0
for i in images:
    total += i.shape[0] * i.shape[1]
avg = total/len(images)
for i in images:
    area = i.shape[0] * i.shape[1]
    if area > 1.25*avg:
        sizes.append("Large")
    elif area < 0.75*avg:
        sizes.append("Small")
    else:
        sizes.append("Medium")
# These are set to the default names from exported models, update as needed.
output_layer = 'loss:0'
input_node = 'Placeholder:0'
filename = "Image_Classification_Model/model.pb"
labels_filename = "Image_Classification_Model/labels.txt"
# Load graph and extract labels
labels = create_tf_graph(filename, labels_filename)
pred = []
for j in images:
    # Necessary preprocessing
    img = resize_down_to_1600_max_dim(j)
    h, w = img.shape[:2]
    min_dim = min(w, h)
    max_square_image = crop_center(img, min_dim, min_dim)
    augmented_image = resize_to_256_square(max_square_image)
    with tf.compat.v1.Session() as sess:
        input_tensor_shape = sess.graph.get_tensor_by_name('Placeholder:0').shape.as_list()
    network_input_size = input_tensor_shape[1]
    # Crop the center for the specified network_input_Size
    augmented_image = crop_center(augmented_image, network_input_size, network_input_size)
    predictions = predict_from_graph([output_layer], augmented_image, input_node)
    # Convert numpy array to list
    predictions = predictions.tolist()
    # Take the most likely result into account
    prob = max(predictions[0])
    for i in range(len(predictions[0])):
        if predictions[0][i] == prob:
            ind = i
    # If diseased probability is low, output as mixed
    if labels[ind] == "Diseased" and prob < 0.5:
        label = "Mixed"
    else:
        label = labels[ind]
    pred.append((label, prob))
# Resize to fit visualization on screen
if clone.shape[0] > 800 or clone.shape[1] > 800:
    scale = 800/max(clone.shape)
    clone = cv2.resize(clone, (int(clone.shape[1]*scale), int(clone.shape[0]*scale)))
scale = clone.shape[0]/image.shape[0]
for i in range(len(coords_list)):
    for j in range(len(coords_list[i])):
        coords_list[i][j] *= scale
output_info = []
# Output to json and visualize as rectangles
for i in range(len(coords_list)):
    output_info.append({
        "Coordinates": coords_list[i],
        "Object_Identification_Probability": float(final[i][0]),
        "Status": pred[i][0], "Classification_Probability": pred[i][1],
        "Row": loc[i][0],
        "Column": loc[i][1],
        "Green_Intensity": green_intensity[i],
        "Size": sizes[i]})
    if pred[i][0] == 'Healthy':
        color = (0, 255, 0)
    elif pred[i][0] == 'Mixed':
        color = (0, 255, 255)
    else:
        color = (0, 0, 255)
    cv2.rectangle(clone, (int(coords_list[i][0]), int(coords_list[i][1])),
                  (int(coords_list[i][2]), int(coords_list[i][3])), color, 2)
    cv2.putText(clone, pred[i][0], (int(coords_list[i][0]), int(coords_list[i][1])+10),
                cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 0), 2)
    cv2.putText(clone, str(loc[i][0]) + ":" + str(loc[i][1]),
                (int(coords_list[i][0]), int(coords_list[i][1]) + 30),
                cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 0), 2)
with open("output_info.json", "w") as write_file:
    json.dump(output_info, write_file, indent=4)
cv2.imshow('Visual Representation', clone)
cv2.waitKey(0)
cv2.imwrite('Visual_Representation.png', clone)


# coding=utf-8

from pickle import FALSE
from sys import flags, version_info
from tkinter import filedialog
from STCore.Component import StarElement
from logging import root
from operator import contains
from os import scandir
from tkinter.constants import W
import matplotlib
from matplotlib import axes
import numpy
from matplotlib import use, figure
from matplotlib.axes import Axes
from numpy.lib.histograms import histogram

from STCore.item import Star
use("TkAgg")

import matplotlib as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.patches import Rectangle
from matplotlib.colors import Normalize, PowerNorm, LogNorm
from matplotlib.artist import setp, getp
import tkinter as tk
from tkinter import ttk
from STCore.item.Star import *
from STCore import SetStar, Tracker
import STCore.DataManager
from time import time
import STCore.Settings
import STCore.RuntimeAnalysis
import gc
from PIL import Image
import STCore.utils.Icons as icons
from STCore import DataManager, RuntimeAnalysis
from Component import Levels, StarElement

#region Messages and Events
params = {"ytick.color" : "w",
			"xtick.color" : "w",
			"axes.labelcolor" : "grey",
			"axes.edgecolor" : "grey"}
plt.rcParams.update(params)
#endregion

#region Global Variables
ViewerFrame = None
Data = None
level_perc = (0,0)
Stars = []
canvas = None
implot = None
ImageFrame = None
axis : Axes = None

SidebarList = None
SliderLabel = None
ColorMaps = {"Escala de grises" : "gray", "Temperatura" : "seismic", "Arcoiris" : "rainbow", "Negativo" : "binary"}
Modes = {"Linear" : Normalize(), "Raiz cuadrada": PowerNorm(gamma = 0.5), "Logaritmico" : LogNorm()}

SelectedStar = -1
MousePress = None
MousePressTime = -1

img_limits : tuple = None
img_offset : tuple = (0,0)
zoom_factor = 1

z_container : Rectangle = None
z_box : Rectangle = None

App : ttk.Frame = None
levelFrame : Levels = None
Viewport : tk.Canvas = None
Sidebar : tk.Canvas = None
sidebar_buttons : tk.Frame = None

sidebar_elements = []

isInitialized = False
#endregion

#region Main Body

def Awake(root):
	global ViewerFrame, Data, Stars, canvas, implot, ImageFrame, axis, Sidebar, SidebarList, SliderLabel, level_perc, levelFrame, isInitialized

	STCore.DataManager.CurrentWindow = 2

	App.pack(fill=tk.BOTH, expand=1)

	#ViewerFrame = tk.Frame(root)
	#ttk.Label(ViewerFrame,text="Visor de Imagen").pack(fill = tk.X)
	Data =  DataManager.FileItemList[0].data
	level_perc = STCore.DataManager.Levels

	# Setting Levels
	if not isinstance(level_perc, tuple):
		level_perc = (numpy.percentile(Data, 99.8), numpy.percentile(Data, 1))
		STCore.DataManager.Levels = level_perc

	#BuildLayout(root)
	if implot is None:
		App.after(10, DrawCanvas)

	levelFrame.set_limits(numpy.nanmin(Data), numpy.nanmax(Data))
	levelFrame.setMax(level_perc[0])
	levelFrame.setMin(level_perc[1])

	# Star version control
	version_changed = False
	index = 0
	for star in Stars:
		version_changed = version_changed or CheckVersion(star, index)
		index += 1

	if version_changed:
		print ("Se actualizaron las estrellas de una version anterior")
		SetStar.closedTime = 0

	OnStarChange()
	isInitialized = True

# Draws the layout in a single pass
def BuildLayout(root : tk.Tk):
	global App, Viewport, Sidebar, levelFrame, isInitialized

	# Checks if Viewport object hasn't been destroyed or unloaded
	fresh = Viewport is None

	# Check whether the layout hadn't been built yet
	if isInitialized == False:
		App = ttk.Frame(root, width=root.winfo_width(), height=root.winfo_height(), name="imageview")
		App.pack(fill=tk.BOTH, expand=1)

		App.columnconfigure(tuple(range(2)), weight=1)
		App.columnconfigure(1, weight=0)
		App.rowconfigure(tuple(range(2)), weight=1)

		CreateCanvas()
		CreateLevels()
		CreateSidebar(root)

		#Sidebar.grid_propagate(0)

		Viewport.grid(row=0, column=0, rowspan=2, sticky="nsew")
		Sidebar.grid(row=0, column=1, rowspan=2, sticky="nsew")
		levelFrame.grid(row=2, column=0, sticky=tk.EW)
		sidebar_buttons.grid(row=2, column=1, sticky="ew")

		if fresh:
			Destroy()
		isInitialized = True
	#else:	# No need to rebuild
		#Viewport.grid()
		#Sidebar.grid()
		#levelFrame.grid()
		#sidebar_buttons.grid()

#region Create Funcions

# Creates the viewport, but doesn't draw it to the UI
def CreateCanvas():
	global canvas, implot, ImageFrame, axis, Viewport

	#ImageFrame = ttk.Frame(app, width = 700, height = 350)
	#ImageFrame.pack(side=tk.LEFT, fill = tk.BOTH, expand = True, anchor = tk.W)

	fig = figure.Figure(figsize = (7,3.6), dpi = 100)
	fig.set_facecolor("black")

	# Create Canvas before any complex calculations
	canvas = FigureCanvasTkAgg(fig, master=App)

	Viewport = canvas.get_tk_widget()
	Viewport.configure(bg="black")
	Viewport.config(cursor = "fleur")

	axis = fig.add_subplot(111)
	fig.subplots_adjust(0.0,0.05,1,1)

	canvas.mpl_connect("button_press_event", OnMousePress)
	canvas.mpl_connect("motion_notify_event", OnMouseDrag)
	canvas.mpl_connect("button_release_event", OnMouseRelease)
	canvas.mpl_connect('scroll_event',OnMouseScroll)

# Fill the Canvas window for the viewport
def DrawCanvas():
	global canvas, implot, ImageFrame, axis

	axis.clear()
	implot = axis.imshow(Data, vmin = level_perc[1], vmax = level_perc[0], cmap=ColorMaps[STCore.Settings._VISUAL_COLOR_.get()], norm = Modes[STCore.Settings._VISUAL_MODE_.get()])
	if STCore.Settings._SHOW_GRID_.get() == 1:
		axis.grid()

	axis.relim()
	canvas.draw()

	# Get axis limits and save it as a tuple
	global img_limits

	img_limits = (axis.get_xlim(), axis.get_ylim())
	UpdateCanvasOverlay()

# Creates the siderbar, but does not draw it to the UI
def CreateSidebar(root):
	global App, Sidebar, SidebarList, sidebar_buttons

	Sidebar = tk.Canvas(App, width = 300, relief = "flat", bg = "gray16")
	Sidebar.config(scrollregion=(0,0, 300, 1))

	SidebarList = ttk.Frame(Sidebar, width=300,height=root.winfo_height())
	Sidebar.create_window(300, 0, anchor=tk.NE, window=SidebarList, width=300, height=600)

	SidebarList.grid_columnconfigure(0, weight=1)

	ScrollBar = ttk.Scrollbar(App, command=Sidebar.yview)
	ScrollBar.grid(row=0, column=2, rowspan=3, sticky=tk.NS)
	Sidebar.config(yscrollcommand=ScrollBar.set)

	cmdTrack = lambda : Apply(root)
	def CommandCreate():
		if Data is None:
			return
		loc = (int(Data.shape[0] * 0.5), int (Data.shape[1] * 0.5))
		SetStar.Awake(Data, None, OnStarChange, AddStar, location = loc, name = "Estrella " + str(len(Stars) + 1))

	def CommandBack():
		import STCore.ImageSelector
		Destroy()
		STCore.ImageSelector.Awake(root, [])

	def CommandExport():
		with filedialog.asksaveasfile(mode="w", filetypes=[("Valores separados por comas", "*.csv"), ("Archivo de texto", "*.txt")]) as f:
			n=0
			for star in Stars:
				# Reemplazar; con cualquier caracter separador                                                               v
				#star.PrintData((NAME, SUM, FBACK, AREA, SBR, VALUE, FLUX, MBACK, DBACK, VBACK, BSIZE), header= n==0, sep= "{};", stdout=f)
				star.PrintData((NAME, VALUE, SUM, AREA, FLUX, SUMVBACK, BACKREFS, ABACK, FLUXBACK, NETFLUX, ABSMAG), header= n==0, sep= "{};", stdout=f)
				n+=1

	sidebar_buttons = ttk.Frame(App)

	AddButton = ttk.Button(sidebar_buttons, text = "Agregar estrella", command = CommandCreate, style="Highlight.TButton", image=icons.GetIcon("add"), compound="left")

	PrevButton = ttk.Button(sidebar_buttons, text = " Volver", image = icons.GetIcon("prev"), command = CommandBack, compound="left")
	ExpButton = ttk.Button(sidebar_buttons, text= "Exportar datos", image=icons.GetIcon("export"), compound="left", command=CommandExport)
	NextButton = ttk.Button(sidebar_buttons, text = "Continuar", command = cmdTrack, image = icons.GetIcon("next"), compound = "right")

	AddButton.grid(row = 0, column = 0, columnspan=3, sticky = "ew")
	PrevButton.grid(row = 1, column = 0, sticky = "ew")
	ExpButton.grid(row=1, column=1, sticky="ew")
	NextButton.grid(row = 1, column = 2, sticky = "ew")

def CreateLevels():
	global levelFrame
	levelFrame = Levels(App, ChangeLevels)

#endregion

#region Update Funcions

sidebar_dirty = False
def AddStar(star : StarItem, onlyUI = False):
	global Stars, sidebar_elements
	global SidebarList

	index = len(sidebar_elements)

	# onlyUI flag tells whether the program is adding new stars to the list, or just refreshing their UI elements
	if not onlyUI:
		Stars.append(star)

	def SetTrackerDirty():
		Tracker.DataChanged = True
	def SetSidebarDirty():
		global sidebar_dirty
		sidebar_dirty = True

	cmd_star = lambda i=index: SetStar.Awake(Data, Stars[index], OnStarChange, None, i)
	cmd_delete = lambda i=index: (Stars.pop(i), sidebar_elements.pop(i), OnStarChange(), SetTrackerDirty(), SetSidebarDirty())

	element = StarElement(SidebarList, star, index, cmd_star, SetGuideStar, cmd_delete)
	element.grid(row=index, column=0, sticky= "nsew")
	sidebar_elements.append(element)
def SetGuideStar(index):
	i = 0
	for star in Stars:
		star.type = 1 if i == index else 0
		i += 1
	UpdateStarList()

def UpdateStarList():
	global SidebarList, sidebar_elements, sidebar_dirty

	index = 0
	# Checks if sidebar is dirty
	if sidebar_dirty:
		for s in sidebar_elements:
			s.destroy()
		sidebar_elements = []
		sidebar_dirty = False

	# Recreate the list of elements if its size doesn't match the Stars (i.e. Load a trak file)
	if len(sidebar_elements) != len(Stars):
		for star in Stars:
			AddStar(star, onlyUI=True)

	# Assing the guide star if all or none  of them are already set
	# brightest star index, guide star count, brightest star value
	if len(Stars) > 0:
		bsi, gs, bs = 0, 0, 0
		for star in Stars:
			if star.type == 1:
				gs += 1
			if star.value > bs:
				bsi = index
				bs = star.value
			index += 1

		if gs > 1 or gs == 0:
			SetGuideStar(bsi)
			return

	index = 0
	# Update elements if necessary
	star : StarItem
	for star in Stars:
		element :StarElement = sidebar_elements[index]
		element.update_star(star)
		index += 1

	SidebarList.config(height=32 * index)
	Sidebar.update_idletasks()
	Sidebar.config(scrollregion=SidebarList.bbox())
	#Sidebar.after(10, lambda:Sidebar.config(scrollregion=(0,0, 250, 32 * index)))
	#Sidebar.update_idletasks()
	App.after(40, UpdateCanvasOverlay)

def CheckVersion(star : StarItem, index):

	# Version is way too old. needs to recompute
	if not hasattr(star, "version"):

		SetStar.Awake(Data, star, OnStarChange, skipUI = True, starIndex=index)
		return True
	changed = False

	# File is from another version, needs to be re-registered
	if star.version != CURRENT_VER:
		SetStar.Awake(Data, star, OnStarChange, skipUI = True, starIndex=index)
		changed = True
	return changed

def UpdateCanvasOverlay():
	# Si se elimina el primer elemento de un lista en un ciclo for, entonces
	# ya no podra seguir iterando, lo que producir errores, se utiliza reversed para eliminar
	# el ultimo elemento de la lista primero y asi.
	for a in reversed(axis.artists):
		if a.label == "zoom_container" or a.label == "zoom_box":
			continue
		a.remove()
	for t in reversed(axis.texts):
		t.remove()
	for s in Stars:
		rect_pos = (s.location[1] - s.radius, s.location[0] - s.radius)
		rect = Rectangle(rect_pos, s.radius *2, s.radius *2, edgecolor = "w", facecolor='none')
		rect.label = "Rect"+str(Stars.index(s))
		bound_pos = (s.location[1] - s.bounds, s.location[0] - s.bounds)
		bound = Rectangle(bound_pos, s.bounds*2, s.bounds *2, edgecolor = "y", linestyle = 'dashed', facecolor='none')
		bound.label = "Bound"+str(Stars.index(s))
		axis.add_artist(rect)
		axis.add_artist(bound)
		text_pos = (s.location[1], s.location[0] - s.bounds - 6)
		text = axis.annotate(s.name, text_pos, color='w', weight='bold',fontsize=6, ha='center', va='center')
		text.label = "Text"+str(Stars.index(s))
	canvas.draw_idle()

def UpdateZoomGizmo(scale, xrange, yrange):
	global axis, zoom_factor, img_offset, z_container, z_box

	aspect = yrange/xrange

	# Change the size of the Gizmo
	size = 320

	if zoom_factor > 1:
		gizmo_w = size  * scale
		gizmo_h = size * scale * aspect
		gizmo_pos = img_offset[0] - xrange * scale, img_offset[1] + yrange * scale - gizmo_h

		if z_container is None:
			z_container = Rectangle(gizmo_pos, gizmo_w, gizmo_h, edgecolor = "w", facecolor='none')
			z_container.label = "zoom_container"

			z_box = Rectangle(gizmo_pos, gizmo_w, gizmo_h, alpha = 0.5)
			z_box.label = "zoom_box"

			axis.add_artist(z_container)
			axis.add_artist(z_box)
		else:
			z_container.set_xy(gizmo_pos)
			z_container.set_width(gizmo_w)
			z_container.set_height(gizmo_h)

			z_box.set_x(gizmo_pos[0] + 0.5*(img_offset[0] * gizmo_w / xrange- gizmo_w * scale) )
			z_box.set_y(gizmo_pos[1] + 0.5*(img_offset[1] * gizmo_h / yrange- gizmo_h * scale) )
			z_box.set_width(gizmo_w * scale)
			z_box.set_height(gizmo_h * scale)
	else:
		if z_container is not None:
			z_container.remove()
			z_container = None

			z_box.remove()
			z_box = None

def ChangeLevels():
		global level_perc

		if implot is None:
			return

		if levelFrame.getMin() > levelFrame.getMax():
			levelFrame.setMin(levelFrame.getMax() - 1)

		if levelFrame.getMax() <= levelFrame.getMin():
			levelFrame.setMax(levelFrame.getMin() + 1)

		_min = levelFrame.getMin()
		_max = levelFrame.getMax()

		implot.norm.vmax = _max
		implot.norm.vmin = _min + 0.01
		implot.set_cmap(ColorMaps[STCore.Settings._VISUAL_COLOR_.get()])
		implot.set_norm(Modes[STCore.Settings._VISUAL_MODE_.get()])

		STCore.DataManager.Levels =  (_max, _min)
		canvas.draw_idle()

#endregion

def Destroy():
	global img_limits, zoom_factor, img_offset, z_container, z_box

	# Reset current viewport
	zoom_factor =  1
	axis.relim()
	axis.autoscale()
	if z_container is not None:
		z_container.remove()
		z_box.remove()
		z_container = None
		z_box = None

	img_offset = (0,0)
	img_limits = (axis.get_xlim(), axis.get_ylim())
	App.pack_forget()
	#gc.collect()

def Apply(root):
	items = DataManager.FileItemList

	from tkinter import messagebox
	if len(Stars) > 0:
		Destroy()
		Tracker.Awake(root, Stars, items)
		if DataManager.RuntimeEnabled == True:
			RuntimeAnalysis.StartRuntime(root)
	else:
		messagebox.showerror("Error", "Debe tener al menos una estrella para comenzar el analisis")
		return
	DataManager.StarItemList = Stars

def ClearStars():
	global Stars, sidebar_elements
	Stars = []
	for s in sidebar_elements:
		s.destroy()
	sidebar_elements = []

#endregion
def OnMouseScroll(event):
	global Data, canvas, axis, zoom_factor, img_limits, img_offset

	# Check if for some reason, no limits were defined
	if img_limits is None:
		axis.relim()
		axis.autoscale(True)
		img_limits = (axis.get_xlim(), axis.get_ylim())  # By some reason mpl axis are inverted
	# Modify this for faster/slower increments
	increment = 0.5

	xdata = event.xdata # get event x location
	ydata = event.ydata # get event y location
	# If we are outside the viewport, then stop the function
	if xdata is None or ydata is None:
		return

	xrange = 0.5 * (img_limits[0][1] - img_limits[0][0])
	yrange = 0.5 * (img_limits[1][0] - img_limits[1][1])

	if event.button == 'up':
		# deal with zoom in
		if zoom_factor < 10:
			zoom_factor += increment
	elif event.button == 'down':
		# deal with zoom out
		if zoom_factor > 1:
			zoom_factor -= increment
	else:
		# deal with something that should never happen
		zoom_factor = 1
		print (event.button)
	scale = 1. / zoom_factor

	# Set the offset to the current mouse position
	img_offset = numpy.clip(xdata * scale + (1-scale)*img_offset[0], xrange * scale, img_limits[0][1] - xrange * scale), numpy.clip(ydata * scale + (1-scale)*img_offset[1], yrange*scale, img_limits[1][0] - yrange * scale)

	axis.set_xlim([img_offset[0] - xrange * scale,
					img_offset[0] + xrange * scale])
	axis.set_ylim([img_offset[1] + yrange * scale,
					img_offset[1] - yrange * scale])

	UpdateZoomGizmo(scale, xrange, yrange)
	canvas.draw_idle() # force re-draw

#drag displacement = lastX, lastY, dispX, dispY
drag_displacement = (0, 0, 0, 0)

def OnMousePress(event):
	global canvas, MousePress, SelectedStar, axis, drag_displacement
	MousePress = 0, 0, event.xdata, event.ydata
	drag_displacement = event.xdata, event.ydata, 0, 0

	for a in axis.artists:
		contains, attrd = a.contains(event)
		if contains:
			x0, y0 = a.xy
			MousePress = x0, y0, event.xdata, event.ydata

			# Check if we selected the zoom controls
			if a.label == "zoom_container" or a.label == "zoom_box":
				setp(z_box, alpha = 1)
				setp(z_box, edgecolor = "w")
				SelectedStar = -100  # We'll use the code -100 to identify whether the zoom controls are selected (to avoid declaring more global variables)
				break
			SelectedStar = int(next(filter(str.isdigit, a.label)))
			setp(a, linewidth = 4)
		else:
			setp(a, linewidth = 1)
	canvas.draw_idle()

def OnMouseDrag(event):
	global MousePress, Stars, drag_displacement
	if MousePress is None or event.inaxes is None:
		return
	x0, y0, xpress, ypress = MousePress
	dx = event.xdata - xpress
	dy = event.ydata - ypress

	# Check whether the zoom controls are selected
	if SelectedStar == -100:
		if z_container is not None:
			global img_limits, axis, img_offset
			w, h = getp(z_container, "width"), getp(z_container, "height")
			xy = getp(z_container, "xy")

			xrange = 0.5 * (img_limits[0][1] - img_limits[0][0])
			yrange = 0.5 * (img_limits[1][0] - img_limits[1][1])

			scale = 1./zoom_factor
			xcenter = 2*(event.xdata - xy[0]) * xrange / w
			ycenter = 2*(event.ydata - xy[1]) * yrange / h

			xcenter = numpy.clip(xcenter, xrange * scale, img_limits[0][1] - xrange * scale)
			ycenter = numpy.clip(ycenter, yrange * scale, img_limits[1][0] - yrange * scale)
			img_offset = xcenter, ycenter
			axis.set_xlim([xcenter - xrange * scale,
						xcenter + xrange * scale])
			axis.set_ylim([ycenter + yrange * scale,
						ycenter - yrange * scale])
			UpdateZoomGizmo(scale, xrange, yrange)
			canvas.draw_idle() # fo

		return # Stop the function here

	# Fail conditions
	if SelectedStar == -1 or len(Stars) == 0: return

	sel = list(filter(lambda obj: obj.label == "Rect"+str(SelectedStar), axis.artists))
	bod = list(filter(lambda obj: obj.label == "Bound"+str(SelectedStar), axis.artists))
	text = list(filter(lambda obj: obj.label == "Text"+str(SelectedStar), axis.texts))
	if len(sel) > 0 and len(text) > 0:
		sel[0].set_x(x0+dx + Stars[SelectedStar].bounds - Stars[SelectedStar].radius)
		sel[0].set_y(y0+dy + Stars[SelectedStar].bounds - Stars[SelectedStar].radius)
		bod[0].set_x(x0+dx)
		bod[0].set_y(y0+dy)
		text[0].set_x(x0 + dx + Stars[SelectedStar].bounds)
		text[0].set_y(y0 -6 +dy )
		Stars[SelectedStar].location = (int(y0 + dy + Stars[SelectedStar].bounds), int(x0 + dx + Stars[SelectedStar].bounds))
	canvas.draw_idle()

	sx = drag_displacement[2] + abs(event.xdata - drag_displacement[0])
	sy = drag_displacement[3] + abs(event.ydata - drag_displacement[1])
	drag_displacement = event.xdata, event.ydata, sx, sy

def OnMouseRelease(event):
	global MousePress, SelectedStar, drag_displacement

	# Change this value for lower/higher drag tolerance
	drag_tolerance = 0.2

	if SelectedStar == -100:
		if z_box is not None:
			setp(z_box, alpha = 0.5)
			setp(z_box, edgecolor = None)
		SelectedStar = -1
		return
	if SelectedStar >= 0:
		OnStarChange()
	SelectedStar = -1

	if  drag_displacement[2] < drag_tolerance and drag_displacement[3] < drag_tolerance:
		OnImageClick(event)
	for a in axis.artists:
		setp(a, linewidth = 1)

	MousePress = None
	canvas.draw_idle()

def OnImageClick(event):
	loc = (int(event.ydata), int(event.xdata))
	SetStar.Awake(Data, None, OnStarChange, AddStar, location = loc, name = "Estrella " + str(len(Stars) + 1))

def OnStarChange(star : StarItem = None, index = -1):
	global Stars

	if star is not None:
		Stars[index] = star

	UpdateStarList()
	#UpdateCanvasOverlay()
	STCore.DataManager.StarItemList = Stars


import pygame
from levels import *
from constants import *
from player import Player
from sounds import Sound

class Game:
    def __init__(self):
        pygame.init()
        self.background_sound = Sound()

        pygame.key.set_repeat(50, 50)
        size = [SCREEN_WIDTH, SCREEN_HEIGHT]

        self.screen = pygame.display.set_mode(size)
        pygame.display.set_caption(TITLE)

        self.clock = pygame.time.Clock()
        self.font_name = pygame.font.match_font(FONT)

        self.running = True
        self.isover = False
        self.gameispaused = False
        self.levels = {
            1: Level_01,
            2: Level_02,
            3: Level_03,
            4: Level_04,
            5: Level_05,
            6: Level_06,
            7: Level_07,
            8: Level_08,
            9: Level_09,
            10: Level_10}

        self.show_start_screen()

    def new(self, level = 0):
        self.player = Player()
        self.active_sprite_list = pygame.sprite.Group()
        self.active_sprite_list.add(self.player)
        self.level = level + 1
        self.current_level = self.levels[self.level](self.player)
        self.player.level = self.current_level
        self.background_sound.next_song()
        self.run()

    def run(self):
        self.playing = True
        while self.playing:
            self.events()
            self.update()
            self.draw()
        self.end_game()

    def update(self):
        if self.player.iskill:
            self.playing = False
            self.isover = True
            self.gameover()
            return

        if not self.gameispaused and self.running:
            self.active_sprite_list.update()
            self.current_level.update()
            # If the player gets near the right side, shift the world left (-x)
            if self.player.rect.right >= 500:
                diff = self.player.rect.right - 500
                self.player.rect.right = 500
                self.current_level.shift_world(-diff)

            # If the player gets near the left side, shift the world right (+x)
            if self.player.rect.left <= 120:
                diff = 120 - self.player.rect.left
                self.player.rect.left = 120
                self.current_level.shift_world(diff)

            # If the player gets to the end of the level, go to the next level
            current_position = (self.player.rect.x
                                + self.current_level.world_shift)
            if current_position < self.current_level.level_limit:
                self.player.rect.x = 120
                if self.level < len(self.levels) - 1:
                    self.level += 1
                    self.current_level = self.levels[self.level](self.player)
                    self.player.level = self.current_level
                else:
                   self.end_screen();
        else:
            return

    def events(self):
        for bullet in self.player.bullets:
            for enemies in self.current_level.enemy_list:
                if (bullet.y - bullet.radius < enemies.rect.y + 147
                    and bullet.y + bullet.radius > enemies.rect.y):
                    if (bullet.x + bullet.radius > enemies.rect.x
                        and bullet.x - bullet.radius < enemies.rect.x + 52):
                        enemies.loseenergy(self.player.power)
                        if bullet in self.player.bullets:
                            self.player.bullets.remove(bullet)
            if bullet.x < SCREEN_WIDTH and bullet.x > 0:
                bullet.x += bullet.vel
            else:
                if bullet in self.player.bullets:
                    self.player.bullets.remove(bullet)

        for event in pygame.event.get():
            if event.type == pygame.USEREVENT:
                self.background_sound.next_song()
            if event.type == pygame.QUIT:
                if self.playing:
                    self.playing = False
                self.running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_p:
                    self.gameispaused = True
                    self.pause_screen()

                if event.key == pygame.K_LEFT:
                    self.player.sprint(self.player.direction)
                    self.player.go_left()

                if event.key == pygame.K_RIGHT:
                    self.player.sprint(self.player.direction)
                    self.player.go_right()

                if event.key == pygame.K_UP:
                    self.last_key_pressed = pygame.K_UP
                    if self.level < 2:
                        self.draw_text(GRAVITY_WARN,
                                       24,
                                       RED,
                                       SCREEN_WIDTH/2,
                                       SCREEN_HEIGHT/2)

                        self.draw_text(OPTIONS,
                                       22,
                                       RED,
                                       SCREEN_WIDTH/2,
                                       SCREEN_HEIGHT * 3/4)

                        pygame.display.flip()
                        self.wait_for_key()
                    else:
                        self.player.jump()

                if event.key == pygame.K_SPACE:
                    self.player.increasepower()
                    self.player.shoot()

                if event.key == pygame.K_DOWN:
                    self.player.invisibility()

            if event.type == pygame.KEYUP:
                self.player.stop()

    def draw(self):
        self.screen.fill(BLACK)
        self.current_level.draw(self.screen)
        font = pygame.font.SysFont(FONT, 20, True)

        if not self.player.invisible:
            self.active_sprite_list.draw(self.screen)
            text = font.render(HEALTH .format(self.player.health),
                               1,
                               RED)

            self.screen.blit(text,
                             (self.player.rect.x -10,
                              self.player.rect.y -20))

            text = font.render(PRESS_ME,
                               1,
                               WHITE)

            self.screen.blit(text,
                             (SCREEN_WIDTH - 200, 10))

        for bullet in self.player.bullets:
            bullet.draw(self.screen)

        self.clock.tick(FPS)

        for enemies in self.current_level.enemy_list:
            enemies.draw(self.screen)
            if enemies.power is not 0:
                text = font.render(LEVEL .format(enemies.power),
                                   1,
                                   WHITE)

                self.screen.blit(text,
                                 (enemies.rect.x- 10,
                                  enemies.rect.y - 20))

        pygame.display.flip()
        pygame.display.update()

    def show_start_screen(self):
        self.screen.fill(WHITE)

        self.draw_text(TITLE,
                       48,
                       BLACK,
                       SCREEN_WIDTH /2,
                       SCREEN_HEIGHT / 4)

        self.draw_text(START,
                       32,
                       BLACK,
                       SCREEN_WIDTH / 2,
                       SCREEN_HEIGHT / 2)

        self.draw_text(TIP_LVL,
                       12,
                       BLACK,
                       SCREEN_WIDTH / 5,
                       SCREEN_HEIGHT - 21)

        pygame.display.flip()
        self.wait_for_key()

    def gameover(self):
        self.background_sound.stop_music()
        self.screen.fill(WHITE)
        self.draw_text(GAME_OVER,
                       48,
                       BLACK,
                       SCREEN_WIDTH /2,
                       SCREEN_HEIGHT / 4)

        self.draw_text(OPTIONS,
                       22,
                       BLACK,
                       SCREEN_WIDTH / 2,
                       SCREEN_HEIGHT * 3 / 4)

        pygame.display.flip()
        self.wait_for_key()

    def pause_screen(self):
        self.current_level.tip(self.screen)
        self.draw_text(GAME_PAUSED,
                       22,
                       WHITE,
                       SCREEN_WIDTH /2,
                       SCREEN_HEIGHT/4)

        self.draw_text(OPTIONS,
                       22,
                       WHITE,
                       SCREEN_WIDTH /2,
                       SCREEN_HEIGHT * 1/2)

        pygame.display.flip()
        self.wait_for_key()

    def wait_for_key(self):
        waiting = True
        while waiting:
            self.clock.tick(FPS)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    waiting = False
                    self.running = False
                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_q:
                        self.running = False
                        self.playing = False
                        waiting = False

                    if event.key == pygame.K_RETURN:
                        waiting = False
                        if self.gameispaused:
                            self.gameispaused = False
                        if self.isover:
                            self.isover = False
                            self.new()

    def end_screen(self):
        self.screen.fill(WHITE)
        self.isover = True
        self.draw_text(GAME_WIN,
                       40,
                       BLACK,
                       SCREEN_WIDTH/2,
                       SCREEN_HEIGHT/2)

        self.draw_text(OPTIONS,
                       22,
                       BLACK,
                       SCREEN_WIDTH / 2,
                       SCREEN_HEIGHT * 3 / 4)

        pygame.display.flip()
        self.wait_for_key()

    def draw_text(self, text, size, color, x, y):
        font = pygame.font.Font(self.font_name, size)
        text_surface = font.render(text, True, color)
        text_rect = text_surface.get_rect()
        text_rect.midtop = (x, y)
        self.screen.blit(text_surface, text_rect)

    def end_game(self):
        if self.isover and not self.running:
            pygame.quit()


import torch

def xform_transpose(xform):
    s = list(range(len(xform.shape)))
    s[-1], s[-2] = s[-2], s[-1]
    return xform.permute(*s)

def xform_fk_vel(lxform, lpos, lvrt, lvel, parents):
    gr, gp, gt, gv = [lxform[..., :1, :, :]], [lpos[..., :1, :]], [lvrt[..., :1, :]], [lvel[..., :1, :]]
    for i in range(1, len(parents)):
        p = parents[i]
        gp.append(gp[p] + torch.matmul(gr[p], lpos[..., i:i + 1, :][..., None])[..., 0])
        gr.append(torch.matmul(gr[p], lxform[..., i:i + 1, :, :]))
        gt.append(gt[p] + torch.matmul(gr[p], lvrt[..., i:i + 1, :][..., None])[..., 0])
        gv.append(gv[p] + torch.matmul(gr[p], lvel[..., i:i + 1, :][..., None])[..., 0] +
                  torch.cross(gt[p], torch.matmul(gr[p], lpos[..., i:i + 1, :][..., None])[..., 0], dim=-1))

    return torch.cat(gr, dim=-3), torch.cat(gp, dim=-2), torch.cat(gt, dim=-2), torch.cat(gv, dim=-2)

def xform_orthogonalize_from_xy(xy, eps=1e-10):
    xaxis = xy[..., 0:1, :]
    zaxis = torch.cross(xaxis, xy[..., 1:2, :])
    yaxis = torch.cross(zaxis, xaxis)

    output = torch.cat([
        xaxis / (torch.norm(xaxis, 2, dim=-1)[..., None] + eps),
        yaxis / (torch.norm(yaxis, 2, dim=-1)[..., None] + eps),
        zaxis / (torch.norm(zaxis, 2, dim=-1)[..., None] + eps)
    ], dim=-2)

    return xform_transpose(output)


import copy
from BU.NTS.dataCheck.dataCheck import getNowAccount,warning_rate,isOpen,t_risk_limit_leverage
from param.dict import SuccessMessage,FailMessage
from common import mysqlClient
from common.other import httpCheck as e
from UnitTest.com import LogName
from common.util import truncate, printc, printl, d, Count,LogOut
import BU.NTS.Calculator as cal

log_level = 0
thisCaseNumber = 0
tradeType = 'linearPerpetual';
symbol = 'BTCUSDT'
currency = 'USDT'
pageNum = 1
pageSize = 100
t = mysqlClient.mysql(7)

class Formula():
    def __init__(self,NTS_,symbol):
        MarginIsolated=0;unRealIsolated=0;MarginCross=0;unReal=0;Isolated={};OpenOrderDic={};PositionDic={}
        global NTS
        self.NTS = NTS_;NTS = NTS_
        self.instrumentList = NTS.instrumentList;Instument=NTS.instrument[symbol[:-4]]
        self.TakerFeeRate=Instument[2]
        self.CtVal = Instument[1]
        self.FundingRate=-0.0018830852
        self.MarkPrice = {"BTCUSDT": 16000, "ETHUSDT": 1600};self.Symbol=symbol
        self.IndexPrice = {"BTCUSDT": 16000, "ETHUSDT": 1600};
        self.CalOpenOrderDic=self.CalOpenOrder(self.instrumentList)
        self.CalPositionDic=self.CalPosition(self.instrumentList)
        self.Balance=self.Balance()
        self.Leverage=self.GetLeverage()
        self.WarnX=self.GetWarnX()
        if self.CalPositionDic:
            self.Equity_Isolated_Long=self.CalPositionDic[self.Symbol]['isolated_long'][1]#逐仓-多仓权益
            self.Equity_Isolated_Short = self.CalPositionDic[self.Symbol]['isolated_short'][1] #逐仓-空仓权益
            self.PositionQty_Isolated_Long=self.CalPositionDic[self.Symbol]['isolated_long'][5] #逐仓- 多仓 持仓数量
            self.PositionQty_Isolated_Short = self.CalPositionDic[self.Symbol]['isolated_short'][5]  # 逐仓- 多仓 持仓数量
            self.PositionValue_Isolated_Long=self.CalPositionDic[self.Symbol]['isolated_long'][0] #逐仓-多仓 持仓价值
            self.PositionValue_Isolated_Short = self.CalPositionDic[self.Symbol]['isolated_short'][0]  # 逐仓-多仓 持仓价值
            self.PositionValue_Cross_Short = self.CalPositionDic[self.Symbol]['cross_short'][0]  # 全仓-空仓 持仓价值
            self.PositionValue_Cross_Long = self.CalPositionDic[self.Symbol]['cross_long'][0]  # 全仓-多仓 持仓价值

    # 掛單接口：
    def CalOpenOrder(self,instrumentList):
        OpenOrderDic={};FrozenMargin=d(0);DefaultList=[0,0];
        for symbol in instrumentList:
            OpenOrderDic[symbol]={'isolated_buy':copy.deepcopy(DefaultList),'isolated_sell':copy.deepcopy(DefaultList),'cross_buy':copy.deepcopy(DefaultList),'cross_sell':copy.deepcopy(DefaultList)};
        OpenOrderRes = self.NTS.OpenOrders(tradeType=tradeType, pageSize=100);
        if e(OpenOrderRes)[0]:
            for openOrder in OpenOrderRes['data']['list']:
                symbol=openOrder['symbol']
                coinValue = d(self.NTS.instrument[symbol[:-4]][1])
                Key = openOrder['marginType'] + '_' + openOrder['side']
                #分别计算：挂单价值
                if isOpen(openOrder['side'], openOrder['positionSide']):
                    OpenOrderDic[symbol][Key][0] = OpenOrderDic[symbol][Key][0] + d(openOrder['leavesQty']) * d(openOrder['price']) * coinValue
                    FrozenMargin =FrozenMargin+ cal.FrozenMargin(openOrder['side'], openOrder['price'], openOrder['leavesQty'],self.TakerFeeRate,openOrder['leverage'],self.CtVal)
                else:
                    OpenOrderDic[symbol][Key][1]=OpenOrderDic[symbol][Key][1]+float(openOrder['leavesQty'])
                    # print(openOrder['symbol'],openOrder['positionSide'],openOrder['leavesQty'])
            if OpenOrderRes['data']['totalPage'] > 1:
                for i in range(OpenOrderRes['data']['totalPage']):
                    if i + 2 <= OpenOrderRes['data']['totalPage']:
                        OpenOrderRes = NTS.openOrders(log_level=log_level, tradeType=tradeType,pageSize=100, pageNum=i + 2);
                        for openOrder in OpenOrderRes['data']['list']:
                            symbol = openOrder['symbol']
                            if isOpen(openOrder['side'], openOrder['positionSide']):
                                FrozenMargin = FrozenMargin + cal.FrozenMargin(openOrder['side'],openOrder['price'],openOrder['leavesQty'],self.TakerFeeRate,openOrder['leverage'], self.CtVal)
        self.FrozenMargin=FrozenMargin
        return OpenOrderDic
    #持仓接口： 获取持仓价值、维持保证金率
    def CalPosition(self,instrumentList):
        PositionDic = {};self.PositionMargin_Cross=d(0);self.UnReal_Cross=d(0);self.PositionMargin_Isolated=d(0);self.UnReal_Isolated=d(0);
        self.CalPositionMap={};PositionMap={};self.PositionMap=PositionMap
        for symbol in instrumentList:
            DefaultList=[0,0,0,0,0,0];DefaultDic={};
            PositionDic[symbol]={'isolated_long':copy.deepcopy(DefaultList),'isolated_short':copy.deepcopy(DefaultList),'cross_long':copy.deepcopy(DefaultList),'cross_short':copy.deepcopy(DefaultList)}
            PositionMap[symbol]={'isolated_long': {},'isolated_short':copy.deepcopy(DefaultDic),'cross_long':copy.deepcopy(DefaultDic),'cross_short':copy.deepcopy(DefaultDic)}
        PositionRes = self.NTS.position(log_level=0, tradeType='linearPerpetual')
        if e(PositionRes)[0]:
            if PositionRes['data'].__len__() > 0:
                for i in PositionRes['data']:
                    Key = i['marginType'] + '_' + i['positionSide'];symbol=i['symbol']
                    positionMargin = 'posMargin' if 'positionMargin' not in i.keys() else 'positionMargin';
                    # MarkPrice = d(19500) if symbol == 'BTCUSDT' else d(1800)

                    positionValue = self.MarkPrice[symbol] * d(i['positionAmt']) * d(self.NTS.instrument[symbol[:-4]][1])
                    PositionDic[symbol][Key][0]=positionValue
                    PositionDic[symbol][Key][1]=d(i[positionMargin]) + d(i['unrealisedPnl'])
                    if self.NTS.source=='API':
                        PositionDic[symbol][Key][2] = i['maintMarginRatio']
                        PositionDic[symbol][Key][3] = i['insuranceLevel']
                    PositionDic[symbol][Key][4] = i['availPos']
                    PositionDic[symbol][Key][5] = i['positionAmt']

                    CalUnRealisePnl=cal.UnRealisePnl(i['positionSide'], self.MarkPrice[symbol], i['avgEntryPrice'], i['positionAmt'],self.CtVal)
                    CalPositionMargin=cal.PositionMargin(self.MarkPrice[symbol], i['positionAmt'],self.CtVal,i['leverage'])
                    PositionMap[symbol][Key]['CalUnRealisePnl']=CalUnRealisePnl
                    PositionMap[symbol][Key]['CalPositionMargin'] = CalPositionMargin
                    PositionMap[symbol][Key]['UnRealisePnl']=i['unrealisedPnl']
                    PositionMap[symbol][Key]['positionSide']=i['positionSide']
                    PositionMap[symbol][Key]['symbol'] = i['symbol']
                    PositionMap[symbol][Key]['markPrice'] =self.MarkPrice[symbol]
                    PositionMap[symbol][Key]['positionMargin'] = i['positionMargin'] if not self.NTS.source=='web' else i['posMargin']
                    if self.NTS.source=='web' : PositionMap[symbol][Key]['earningRate'] = i['earningRate']
                    PositionMap[symbol][Key]['avgEntryPrice_positionAmt_ctVal'] = i['avgEntryPrice']+'_'+i['positionAmt']+'_'+self.CtVal
                    # self.CalPositionMap[symbol]={}
                    # self.CalPositionMap[symbol].update({Key:CalUnRealisePnl})
                    #单独 计算 全仓、逐仓 的 总持仓冻结、总未实现盈亏
                    if i['marginType']=='cross':    self.PositionMargin_Cross+=d(i[positionMargin]);self.UnReal_Cross+=d(i['unrealisedPnl']);
                    else:   self.PositionMargin_Isolated += d(i[positionMargin]);self.UnReal_Isolated+=d(i['unrealisedPnl']);
            self.PositionMap=PositionMap
        else:print(f'{self.NTS.user_id}持仓查询异常:',e(PositionRes));return False
        return PositionDic
    # 资金接口
    def Balance(self):
        BalanceRes=self.NTS.Balance(currency='USDT');
        if e(BalanceRes)[0]:
            BalanceRes=BalanceRes['data'][0]
            self.Balance_Equity=BalanceRes['marginEquity']
            self.Balance_Unreal = BalanceRes['profitUnreal'] if self.NTS.source=='API' else  0
            self.Balance_Frozen = BalanceRes['marginFrozen']
            self.Balance_MarginPosition=BalanceRes['marginPosition']
            self.Balance_Available = BalanceRes['marginAvailable']
            self.Balance_WithDrawAmount= BalanceRes['maxWithdrawAmount']
        else:printc(NTS.source+'资金查询异常',BalanceRes)
    #获取风险系数
    def GetWarnX(self):
        warnX = warning_rate(self.Symbol);warningX = warnX[0][0]
        return warningX
    #最大划转、可用保证金计算
    def MaxTransferOut(self,marginType,log_level=None):
        symbol=self.Symbol; result=True
        if marginType=='isolated':
            # warnX = warning_rate(symbol);warningX = warnX[0][0]  #风险系数
            MaintMarginRatio=self.CalPositionDic[symbol][marginType+'_long'][2]
            Equity=self.CalPositionDic[symbol][marginType+'_long'][1]
            PositionAviQty=self.CalPositionDic[symbol][marginType+'_long'][4]
            MarkPrice=self.MarkPrice[symbol]
            WarnMarginRate=d(self.WarnX)*d(MaintMarginRatio)
            printl(log_level,f'marginType={marginType},权益={Equity},WarnMarginRate={WarnMarginRate},FundingRate={self.FundingRate},PositionQty={PositionAviQty},MarkPrice{MarkPrice}')
            TransferAmout=cal.TransferAmount(MarginType=marginType,EquityIsolated=Equity,WarnMarginRate=WarnMarginRate,Side='buy',FundingRate=self.FundingRate,TakerFeeRate=self.TakerFeeRate,PositionQty=PositionAviQty,MarkPrice=MarkPrice,Ctval=self.CtVal,log_level=log_level)
            printl(log_level,f'{symbol} {marginType} buy 最大可转出',TransferAmout)
        else:
            self.Amount = getNowAccount(NTS.user_id)
            equity=d(self.Amount)+d(self.UnReal_Isolated)+d(self.UnReal_Cross)
            # printl(log_level,f'全仓保证金: {self.PositionMargin_Cross}')
            # printl(log_level,f'逐仓保证金: {self.PositionMargin_Isolated}')
            # printl(log_level, f'逐仓未实现盈亏: {self.UnReal_Isolated}')
            # printl(log_level, f'冻结资金: {self.Balance_Frozen}')
            AvailMargin=cal.AvailMargin(equity, self.Balance_Frozen, self.PositionMargin_Cross, self.PositionMargin_Isolated + self.UnReal_Isolated, 0)
            TransferAmout = cal.TransferAmount(AvailMargin,self.Amount)
            if float(AvailMargin)==float(self.Balance_Available): pass #printl(log_level,'可用保证金'+SuccessMessage);Count('公式-可用保证金',1,1,0,0)
            else: pass
                # printc('公式-可用保证金'+FailMessage,' 预期:',AvailMargin,'实际:',self.Balance_Available);Count('公式-可用保证金',1,1,0,0)
                # LogOut('公式-可用保证金'+FailMessage,LogName)
                # LogOut(f' 账户权益 {equity}  余额{self.Amount}+ 逐仓未实现盈亏{self.UnReal_Isolated}+ 全仓未实现盈亏{self.UnReal_Cross}',LogName)
                # LogOut(f'冻结资金: {self.Balance_Frozen}全仓保证金: {self.PositionMargin_Cross}逐仓保证金: {self.PositionMargin_Isolated} ',LogName)
            self.MaxTransferOut_={"Cal_TransferAmout":TransferAmout,"Balance_WithDrawAmount":self.Balance_WithDrawAmount,"Equity":equity,"PositionMargin_Cross":self.PositionMargin_Cross,"PositionMargin_Isolated":self.PositionMargin_Isolated,"UnReal_Isolated":self.UnReal_Isolated,"Balance_Frozen":self.Balance_Frozen}
            if float(TransferAmout)==float(self.Balance_WithDrawAmount): printl(log_level,self.NTS.source+'公式-最大可划转'+SuccessMessage);Count(self.NTS.source+'公式-最大可划转',1,1,0,0);
            else: printc(NTS.user_id+NTS.source+'公式-最大可划转'+FailMessage,' 预期:',TransferAmout,'实际:',self.Balance_WithDrawAmount);Count('公式-最大可划转',1,0,1,0);LogOut('公式-最大可划转'+FailMessage+str(self.MaxTransferOut_),LogName);result=False
        return [TransferAmout,result]
    #维持保证金、风险率 计算 - brian
    def MaintMaringCal(self,marginType,log_level=None,PositionSide='long'):
        O=self.CalOpenOrderDic[self.Symbol]
        MaintMargin = d(0)
        if marginType=='cross':
            for symbol in self.instrumentList:
                P = self.CalPositionDic[symbol]
                Number=0
                for i in P:
                    if marginType in str(i):
                        Number+=1
                        #维持保证金=维持保证金率*数量*面值*标记价
                        Tem=d(P[i][2])*d(P[i][4])*d(self.CtVal)*self.MarkPrice[symbol]
                        MaintMargin+=Tem
                        # print(symbol,i,Tem)
            printl(log_level,'总维持保证金',MaintMargin)
            Equity=(d(self.Balance_Available) + d(self.PositionMargin_Cross))
            RiskRate=d(MaintMargin)/Equity
            printl(log_level,'风险率:',RiskRate)
            return [MaintMargin, Equity, MaintMargin / Equity]
        else:
            P = self.CalPositionDic[self.Symbol]
            for i in P:
                if marginType+'_'+PositionSide in str(i):
                    MaintMargin=d(P[i][2])*d(P[i][4])*d(self.CtVal)*d(self.MarkPrice[self.Symbol])
                    Equity=self.PositionMargin_Isolated+self.UnReal_Isolated
                    printl(log_level,f'维持保证金={MaintMargin},权益={Equity},风险率={MaintMargin/Equity}')
                    return [MaintMargin,Equity,MaintMargin/Equity]
    #冻结保证金 对比 - Case
    def FrozenMarginAssert(self,log_level=None):
        ModuleName='公式-冻结保证金'
        printl(log_level,f'挂单计算的冻结保证金:{self.FrozenMargin}, 资金接口返回的冻结保证金:{self.Balance_Frozen}')
        if float(self.FrozenMargin)==float(self.Balance_Frozen):
            printl(log_level,ModuleName+SuccessMessage);Count(ModuleName,1,1,0,0);return True
        else:
            printc(str(NTS.user_id)+ModuleName+FailMessage+f'挂单计算的冻结保证金:{self.FrozenMargin}, 资金接口返回的冻结保证金:{self.Balance_Frozen}')
            Count(ModuleName, 1, 0, 1, 0);LogOut(ModuleName+FailMessage+f'挂单计算的冻结保证金:{self.FrozenMargin}, 资金接口返回的冻结保证金:{self.Balance_Frozen}',LogName);return False

    # 冻结仓位 对比 - Case  Author : Brian
    def FrozenPositionAssert(self,log_level=None):
       # print(self.CalOpenOrderDic)
       # print(self.CalPositionDic)
       ModuleName='公式-冻结仓位';CaseResult=True
       for symbol in self.CalPositionDic:
           for _type in self.CalPositionDic[symbol]:
               OpenOrderKey=_type.replace('long','sell').replace('short','buy') #多仓 对应 挂单的平仓卖、空仓对应挂单的平仓买  有点绕
               Temp_Postiton=self.CalPositionDic[symbol][_type]
               Temp_OpenOrder=self.CalOpenOrderDic[symbol][OpenOrderKey]
               if not float(Temp_Postiton[5])-float(Temp_Postiton[4])==float(Temp_OpenOrder[1]):
                   ErrorMessage=f'{NTS.user_id} {symbol} {_type}冻结仓位不一致 仓位数量{Temp_Postiton[5]} 仓位可平{Temp_Postiton[4]}仓位冻结{float(Temp_Postiton[5])-float(Temp_Postiton[4])} 平仓挂单冻结{Temp_OpenOrder[1]}'
                   printc(ErrorMessage);LogOut(ErrorMessage,LogName);CaseResult=False
       if  CaseResult: printl(log_level,NTS.user_id+ModuleName+SuccessMessage);Count(ModuleName,1,1,0,0);
       else: Count(ModuleName, 1, 0, 1, 0);
       return CaseResult
       #获取杠杆
    def GetLeverage(self,MarginType=None):
        l={}
        LeverageRes=self.NTS.leverage_info(tradeType='linearPerpetual', symbol=self.Symbol,marginType=MarginType)
        if e(LeverageRes)[0]:
            for i in LeverageRes['data']:   l[i['marginType']]=i['leverage']
        return l
    #获取风险限额
    def GetRiskLimit(self,MarginType=None):
        Leverage=self.Leverage[MarginType]
        MarginTypeNumber=2 if MarginType=='cross' else 1
        RiskLimit=t_risk_limit_leverage(self.Symbol,Leverage,MarginTypeNumber)
        self.RiskLimit=RiskLimit[0][0]
        return RiskLimit[0][0]
    #获取用户风险额度
    def GetRiskAmout(self,MarginType=None,Side=None):
        OpenValue=F.CalOpenOrderDic[self.Symbol]
        PositionValue = F.CalPositionDic[self.Symbol]
        # PositionSide='long' if Side=='buy' else 'short'
        if MarginType=='cross':
            KeyOpen_Buy = MarginType + '_' + 'buy';KeyPosition_Buy=MarginType+'_'+'long'
            KeyOpen_Sell = MarginType + '_' + 'sell';KeyPosition_Sell = MarginType + '_' + 'short'
            LongValue=OpenValue[KeyOpen_Buy][0]+PositionValue[KeyPosition_Buy][0] #多仓总价值：持仓+挂单
            ShortValue = OpenValue[KeyOpen_Sell][0] + PositionValue[KeyPosition_Sell][0] #空仓总价值：持仓+挂单
            RiskAmout=[LongValue,'long',ShortValue,'short'] if LongValue>=ShortValue else [ShortValue,'short',LongValue,'long']
            return RiskAmout
    def GetMaxOpenQty(self,MarginType=None,Side=None,Price=None):
            #获取MarginType、Leverage对应的风险限额
            RiskLimit=self.GetRiskLimit(MarginType=MarginType);
            if MarginType == 'cross':
                Key=['cross_buy','cross_long'] if Side.lower()=='buy' else ['cross_sell','cross_short']
            else:
                Key = ['isolated_buy', 'isolated_long'] if Side.lower() == 'buy' else ['isolated_sell', 'isolated_short']
            #获取MarginType对应的仓位价值、挂单价值
            PositionValue=self.CalPositionDic[self.Symbol][Key[1]][0] #仓位价值
            OpenValue = self.CalOpenOrderDic[self.Symbol][Key[0]][0] #挂单价值
            # print('杠杆,风险额度,持仓价值,挂单价值',self.Leverage[MarginType],RiskLimit,PositionValue,OpenValue)
            #用可用 计算的最大可开[资金接口返回的可用]
            AvailMaxOpenQty = cal.MaxOpenQty(Side, self.Balance_Available, Price, self.Leverage[MarginType], self.TakerFeeRate, self.CtVal, bid1=0);self.AvailMaxOpenQty = AvailMaxOpenQty

            #用风险额度 计算最大可开
            if Side=='Buy' :self.RiskLimitOpenQty=(d(RiskLimit)-d(PositionValue)-OpenValue)/d(Price)/d(self.CtVal)
            else: self.RiskLimitOpenQty=(d(RiskLimit)-d(PositionValue)-OpenValue)/max(0,d(Price))/d(self.CtVal)

            # Mysql数据库中最大下张数量限制
            MysqlMaxOpenQtyLimitNumber = cal.t_order_volume_limit(self.Symbol)
            #可用计算的最大可开、风险限额最大可开 取小值
            #可开多数量 = min { 可开数量x ，（杠杆对应风险限额 - 仓位价值-当前委托价值） / 委托价格 ，最大单笔下单数量限制}
            Qty=min(AvailMaxOpenQty,self.RiskLimitOpenQty,MysqlMaxOpenQtyLimitNumber)

            #提供计算参数
            self.MaxOpenQty={'leverage':self.Leverage[MarginType],'RiskLimit':float(RiskLimit),'PositonValue':float(PositionValue),'OpenValue':float(OpenValue),'AvailMaxOpenQty':AvailMaxOpenQty,'RiskLimitOrderQty':float(self.RiskLimitOrderQty),"Ctval":float(self.CtVal),'TakerFeeRate':float(self.TakerFeeRate)}
            #返回最后结果
            return [Qty,truncate(Qty,0)]
    #持仓 浮动盈亏、浮动盈亏率、持仓保证金 验证 Case
    def PositionAssert(self,log_level=None):
        AssertResult=True;BlankPositionNumber=0
        Module_UnRealisePnl_Formula=self.NTS.source+'公式-未实现盈亏';Assert_UnRealisePnl_Formula=True
        Module_PositionMargin_Formula = self.NTS.source+'公式-持仓保证金';Assert_PositionMargin_Formula=True
        Module_UnRealisePnlRate_Formula = self.NTS.source+'公式-浮动盈亏率';Assert_UnRealisePnlRate_Formula = True
        for symbol in self.PositionMap:
            for MarginType_PositionSide in self.PositionMap[symbol]:
                if self.PositionMap[symbol][MarginType_PositionSide].__len__()>0:
                    PositionData=self.PositionMap[symbol][MarginType_PositionSide]

                    CalUnRealisePnlRate = d(PositionData['CalUnRealisePnl'] / d(PositionData['positionMargin']))
                    #未实现盈亏 检查
                    if float(PositionData['CalUnRealisePnl'])==float(PositionData['UnRealisePnl']): pass
                    else:
                        printc(f' {symbol}{MarginType_PositionSide}{Module_UnRealisePnl_Formula} {FailMessage} 预期 {PositionData["CalUnRealisePnl"]} 实际 {PositionData["UnRealisePnl"]}');
                        LogOut(f'{Module_UnRealisePnl_Formula} {FailMessage} {PositionData}',LogName);
                        Count(Module_UnRealisePnl_Formula,1,0,1,0);Assert_UnRealisePnl_Formula=False
                    #浮动盈亏率检查，仅支持web端
                    if self.NTS.source == 'web':
                        if not float(CalUnRealisePnlRate) == float(PositionData['earningRate']):
                            printc(f' {symbol}{MarginType_PositionSide}{Module_UnRealisePnlRate_Formula} {FailMessage} 预期 {float(CalUnRealisePnlRate)} 实际 {float(PositionData["earningRate"])}');
                            LogOut(f'{Module_UnRealisePnlRate_Formula} {FailMessage} 预期 {float(CalUnRealisePnlRate)} 实际 {float(PositionData["earningRate"])}', LogName);
                            Count(Module_UnRealisePnlRate_Formula, 1, 0, 1, 0);Assert_UnRealisePnlRate_Formula = False
                    # 持仓保证金检查
                    if float(PositionData['CalPositionMargin'])==float(PositionData['positionMargin']): pass
                    else:
                        printc(f'{symbol}{MarginType_PositionSide}{Module_PositionMargin_Formula} {FailMessage} 预期 {PositionData["CalPositionMargin"]} 实际 {PositionData["positionMargin"]}');
                        LogOut(f'{Module_PositionMargin_Formula} {FailMessage} {PositionData}',LogName);
                        Count(Module_PositionMargin_Formula,1,0,1,0);Assert_PositionMargin_Formula=False
                    if not Assert_UnRealisePnl_Formula or not Assert_PositionMargin_Formula or not Assert_UnRealisePnlRate_Formula:
                        if Assert_UnRealisePnl_Formula: Count(Module_UnRealisePnl_Formula,1,1,0,0);printl(log_level,f'{Module_UnRealisePnl_Formula} {SuccessMessage}');
                        if Assert_PositionMargin_Formula: Count(Module_PositionMargin_Formula,1,1,0,0);printl(log_level,f'{Module_PositionMargin_Formula} {SuccessMessage}');
                        if self.NTS.source=='web' and Assert_UnRealisePnlRate_Formula: Count(Module_UnRealisePnlRate_Formula, 1, 1, 0, 0);printl(log_level,f'{Module_UnRealisePnlRate_Formula} {SuccessMessage}');
                        return False
                else:BlankPositionNumber+=1
        #如果无仓位：则公式验证结果为 阻塞
        if  BlankPositionNumber==self.PositionMap.__len__():
            Count(Module_UnRealisePnl_Formula, 1, 0, 0, 1);
            Count(Module_PositionMargin_Formula, 1, 0, 0, 1);
            if self.NTS.source=='web': Count(Module_UnRealisePnlRate_Formula, 1, 0, 0, 1);
        #最终都成功
        if Assert_UnRealisePnl_Formula: Count(Module_UnRealisePnl_Formula,1,1,0,0);printl(log_level,f'{Module_UnRealisePnl_Formula} {SuccessMessage}');
        if Assert_PositionMargin_Formula: Count(Module_PositionMargin_Formula,1,1,0,0);printl(log_level,f'{Module_PositionMargin_Formula} {SuccessMessage}');
        if self.NTS.source=='web' and Assert_UnRealisePnlRate_Formula: Count(Module_UnRealisePnlRate_Formula, 1, 1, 0, 0);printl(log_level,f'{Module_UnRealisePnlRate_Formula} {SuccessMessage}');
        return True
    #资金 总持仓保证金、权益、验证
    def AccountAssert(self,log_level=None):
        MarginAll=self.PositionMargin_Cross+self.PositionMargin_Isolated
        Module_PositionMargin_Formula = self.NTS.source + '公式-持仓保证金';Assert_PositionMargin_Formula = True
        Module_Equity_Formula = self.NTS.source + '公式-账户权益';Assert_Equity_Formula = True
        Module_AvilMargin_Formula = self.NTS.source + '公式-可用保证金';Assert_AvilMargin_Formula = True

        #验证持仓保证金 ，如果校验失败，输出case失败、日志、统计失败case
        if not float(MarginAll) == float(self.Balance_MarginPosition):
            printc(f' {self.NTS.user_id}{Module_PositionMargin_Formula} {FailMessage} 预期 {MarginAll} 实际 {self.Balance_MarginPosition}');
            LogOut(f'{self.NTS.user_id}{Module_PositionMargin_Formula} {FailMessage} 预期 {MarginAll} 实际 {self.Balance_MarginPosition} ', LogName);
            Count(Module_PositionMargin_Formula, 1, 0, 1, 0); Assert_PositionMargin_Formula = False

        self.GetEquity()
        #验证账户权益 ，如果校验失败，输出case失败、日志、统计失败case
        if not float(self.Equity["Equity"]) == float(self.Balance_Equity):
            ErrorMessage=f' {self.NTS.user_id}{Module_Equity_Formula} {FailMessage} 预期 {self.Equity["Equity"]} 实际 {self.Balance_Equity}'
            printc(ErrorMessage);LogOut(f'{ErrorMessage} {self.Equity} ',LogName);
            Count(Module_Equity_Formula, 1, 0, 1, 0); Assert_Equity_Formula = False

        # 产品公式：可用 = 账户权益 - 委托保证金 - 全仓持仓保证金 - 逐仓权益 - 划转冻结； 逐仓权益=逐仓保证金+逐仓未实现盈亏
        self.AvilMargin = cal.AvailMargin(self.Equity["Equity"], self.Balance_Frozen, self.PositionMargin_Cross, self.PositionMargin_Isolated + self.UnReal_Isolated, 0)
        #可用保证金验证
        if not float(self.AvilMargin)==float(self.Balance_Available):
            ErrorMessage = f' {self.NTS.user_id}{Module_AvilMargin_Formula} {FailMessage} 预期 {self.AvilMargin} 实际 {self.Balance_Available}'
            printc(ErrorMessage);LogOut(f'{ErrorMessage} 账户权益={self.Equity["Equity"]}冻结={self.Balance_Frozen} 全仓保证金={self.PositionMargin_Cross}逐仓权益={self.PositionMargin_Isolated + self.UnReal_Isolated} ', LogName);
            Count(Module_AvilMargin_Formula, 1, 0, 1, 0);Assert_AvilMargin_Formula = False
        #最大可划转 验证
        Assert_MaxTransferOut=self.MaxTransferOut('cross',log_level=log_level)[1]

        if Assert_PositionMargin_Formula: Count(Module_PositionMargin_Formula,1,1,0,0);printl(log_level,f'{Module_PositionMargin_Formula} {SuccessMessage}');
        if Assert_Equity_Formula: Count(Module_Equity_Formula,1,1,0,0);printl(log_level,f'{Module_Equity_Formula} {SuccessMessage}');
        if Assert_AvilMargin_Formula: Count(Module_AvilMargin_Formula, 1, 1, 0, 0);printl(log_level,f'{Module_AvilMargin_Formula} {SuccessMessage}');
        if not Assert_PositionMargin_Formula or not Assert_Equity_Formula or not Assert_AvilMargin_Formula and not Assert_MaxTransferOut:
            return False
    #获取账户权益
    def GetEquity(self):
        UnReal_All=self.UnReal_Cross+self.UnReal_Isolated
        # print(self.Amount,self.UnReal_Cross,self.UnReal_Isolated)
        self.Amount = getNowAccount(NTS.user_id)
        Equity=cal.Equity(self.Amount, UnReal_All)
        self.Equity={"Equity":Equity,"Amount":self.Amount,"UnReal_All":UnReal_All,"UnReal_Cross":self.UnReal_Cross,"UnReal_Isolated":self.UnReal_Isolated}
        return Equity
    #获取预估资金费
    def ForecastFunding(self,marginType,log_level=None):
        if marginType=='cross':
            for symbol in self.instrumentList:
                crossPos = self.CalPositionDic[symbol]
                funding = 0
                totalFunding=0
                for tmp in crossPos:
                    funding = (crossPos[tmp]['cross_long'][5] - crossPos[tmp]['cross_short'][5]) * self.FundingRate
                    totalFunding += funding
                    printl(log_level, f'{tmp}的预估资金费={funding}')
                return
        else:
            isolatedfunding = {}
            for symbol in self.instrumentList:
                isolatedPos = self.CalPositionDic[symbol]
                for tmp in isolatedPos:
                    funding = (isolatedPos[tmp]['isolated_long'][5] - isolatedPos[tmp]['cross_short'][5]) * self.FundingRate
                    isolatedfunding[tmp]['funding']=funding
            return isolatedfunding
    #获取最高价格限制
    def LimitOrderPriceLimit(self,OrderPrice,OrderQty,Ctval):
        MarkPrice=self.MarkPrice[self.Symbol]
        IndexPrice=self.IndexPrice[self.Symbol]
        T=False
        if OrderPrice*OrderQty*Ctval>50000:
            MarkPriceRate = 0.05;IndexPriceRate = 0.08;T=True  # 临时写死,需要从db查询获取
        else:
            MarkPriceRate = 0.08;IndexPriceRate = 0.1;T=False  # 临时写死,需要从db查询获取
        MaxBuyPrice=min( d(MarkPrice)*(d(1+MarkPriceRate)),d(IndexPrice)*d(1+IndexPriceRate) )
        MinSellPrice = min(d(MarkPrice) * (d(1 - MarkPriceRate)), d(IndexPrice) * d(1 - IndexPriceRate))
        return [MaxBuyPrice,MinSellPrice,T]
def GetMarkPrice(MarkPrice=None,OrderRange=None,Side='buy'):
    P=1 if Side=='buy' else -1
    MarketPrice=d(MarkPrice)*(d(1)+d(OrderRange)*d(0.03)*d(P))
    return MarketPrice

if __name__ == '__main__':
    from BU.NTS.WebOrder import n_order
    Symbol='BTCUSDT'
    # NTS = NtsApiOrder(6, user_id='97201979')
    NTS = n_order(5, user_id='97201979')
    MaxTransferOut=Formula(NTS,Symbol).MaxTransferOut(marginType='isolated',log_level=0) #最大转出金额 计算
    # print(MaxTransferOut)
    # F=Formula(NTS, Symbol)
    # time.sleep(10000)
    # print(F.CalOpenOrderDic) #挂单价值
    # print(F.CalPositionDic)  # 持仓相关
                # 🀆🀆🀆🀆🀆★★★★★Formula Case - 3 ★★★★★🀆🀆🀆🀆🀆
    # F.FrozenMarginAssert(log_level=0)  # 1- 挂单冻结金额结果 验证
    # F.PositionAssert(log_level= 0) #2-持仓验证
    # F.AccountAssert(log_level= 0) #3-资金
    # Count(summary=1, log_level=2)
    # print('最高买入价\最低卖出价:',F.LimitOrderPriceLimit(19000,200,0.01))
    # print('市价:',GetMarkPrice(19500,0.9,'sell'))
    # time.sleep(10000)
    # print(F.CalOpenOrderDic) #挂单价值
    # print(F.CalPositionDic)  #仓位价值、保证金+未实现盈亏、维持保证金率、风险等级、可用仓位
    # Formula(NTS, Symbol).MaintMaringCal(marginType='cross',log_level=2) #维持保证金计算
    # Formula(NTS, Symbol).MaintMaringCal(marginType='isolated',log_level=2)
    # print('挂单冻结',f'{F.FrozenMargin}') #打印持仓冻结

    # F.GetRiskLimit(MarginType='isolated'); #获取风险限额(全仓、逐仓)
    # print(F.Leverage)
    # print('风险限额 ',f'{F.RiskLimit}') #打印风险限额
    # F.GetRiskAmout(MarginType='cross',Side='buy')
    # MaxQty=F.GetMaxOpenQty(MarginType='cross',Side='buy',Price=16000)
    # print(MaxQty)
    # print(F.MaxOpenQty)
    # time.sleep(10000)


from django.urls import path
from .views import PostList, PostSearch, CreatePost, EditPost, UserPostDetail, DeletePost
urlpatterns = [
    path('posts/', PostList.as_view()),
    path('search/', PostSearch.as_view()),
    path('user/create/', CreatePost.as_view(), name="create_post"),
    path('user/edit/posts/<int:pk>',
         UserPostDetail.as_view(), name="user_post_detail"),
    path('user/delete/posts/<int:pk>', DeletePost.as_view(), name="delete_post")

]


'''
    created on 09 June 2019

    @author: Gergely
'''
import random

def run_game(env, policy, display=True, should_return=True):
    env.reset()
    episode = []
    done = False

    while not done:
        s = env.env.s

        if display:
            env.render()
        timestep = [s]
        action = policy[s]
        state, reward, done, info = env.step(action)

        timestep.append(action)
        timestep.append(reward)
        episode.append(timestep)

    if should_return:
        return episode

def argmaxQ(Q, s):
    '''
    what the fuck
    :param Q:
    :param s:
    :return:
    '''
    Q_list = list(map(lambda x: x[1], Q[s].items()))
    indices = [i for i, x in enumerate(Q_list) if x == max(Q_list)]
    max_Q = random.choice(indices)
    return max_Q

def greedy_policy(Q):
    policy = {}
    for state in Q:
        policy[state] = argmaxQ(Q, state)
    return policy

def field_list(env):
    l = []
    for row in list(map(lambda x: list([str(y)[-2] for y in x]), list(env.env.desc))):
        for field in row:
            l.append(field)
    return l

def create_state_action_dictionary(env, policy):
    Q = {}
    fields = field_list(env)
    for key in policy.keys():
        if fields[key] in ['F', 'S']:
            Q[key] = {a: 0.0 for a in range(0, env.action_space.n)}
        else:
            Q[key] = {a: 0.0 for a in range(0, env.action_space.n)}
    return Q

def test_policy(policy, env):
    wins = 0
    r = 10000
    for i in range(r):
        w = run_game(env, policy, display=False)
        print(w)
        if w[-1][-1] == 1:
            wins += 1
    return wins / r

def create_random_policy(env):
    policy = {}
    for key in range(env.observation_space.n):

        p = {}
        for action in range(0, env.action_space.n):
            p[action] = 1 / env.action_space.n
        policy[key] = p
    return policy

def sarsa(env, episodes=100, step_size=0.01, epsilon=0.01):
    policy = create_random_policy(env)
    Q = create_state_action_dictionary(env, policy)
    for episode in range(episodes):
        env.reset()
        S = env.env.s
        A = greedy_policy(Q)[S]
        finished = False
        total = 0.0
        while not finished:
            S_prime, reward, finished, _ = env.step(A)
            total += reward
            A_prime = greedy_policy(Q)[S_prime]
            Q[S][A] = Q[S][A] + step_size * (reward + epsilon * Q[S_prime][A_prime] - Q[S][A])
            S = S_prime
            A = A_prime
        print("episode", episode, "terminated with reward", total)

    return greedy_policy(Q), Q

import gym

environment = gym.make('FrozenLake8x8-v0')
pol, Q = sarsa(environment, episodes=1000, step_size=0.2, epsilon=0.2)
print(test_policy(pol, environment))


# -*- coding: utf-8 -*-

from numpy.testing import assert_array_almost_equal

from pmdarima.preprocessing import LogEndogTransformer
from pmdarima.preprocessing import BoxCoxEndogTransformer

def test_same():
    y = [1, 2, 3]
    trans = BoxCoxEndogTransformer(lmbda=0)
    log_trans = LogEndogTransformer()
    y_t, _ = trans.fit_transform(y)
    log_y_t, _ = log_trans.fit_transform(y)
    assert_array_almost_equal(log_y_t, y_t)

def test_invertible():
    y = [1, 2, 3]
    trans = LogEndogTransformer()
    y_t, _ = trans.fit_transform(y)
    y_prime, _ = trans.inverse_transform(y_t)
    assert_array_almost_equal(y, y_prime)


import os
import webbrowser
import shapely
from folium import Map, Marker, CircleMarker
from folium.plugins import MarkerCluster
from folium.features import PolygonMarker
from utility_functions import get_state_contours, get_state_fullname

class SpatialPlotter:
    '''
    A class helps visualize commonly used spatial elements: locations, countours and points.
    Args:
        central_point (list): the central point (lat and lon coordinate) for the canvass.
            Note: users can pass a list of points and we will use the mean value as central point
        reverse (boolean): if true, the position of lat and lon will be reversed.
    '''
    def __init__(self, central_point, reverse=False):
        if reverse:
            central_point = self._reverse_lat_lon(central_point)
        self._build_canvass(central_point)
        print('-'*20)
        print('Latitude is assumed to be the first column')
        print('if your data has longitude first. set reverse=True')

    def _build_canvass(self, locations):
        lat_sum = 0; lon_sum = 0
        for row in locations:
            lat_sum = lat_sum + row[0]
            lon_sum = lon_sum + row[1]
        average_lat = lat_sum/len(locations)
        average_lon = lon_sum/len(locations)
        center = [average_lat, average_lon]
        self.canvass = Map(location=center, zoom_start=6)

    def _reverse_lat_lon(self, list_of_coords):
        '''
        allow users to flip the order of latitude and longitude in the list
        Conventionally, use latitude as the first argument unless specified otherwise
        '''
        flipped_locations = []
        for coord in list_of_coords:
            new_loc = list(reversed(coord[0:2])) #only reverse lat and lon
            new_loc.extend(coord[2:])
            flipped_locations.append(new_loc)
        return flipped_locations

    def _pandas_to_list(self, locations):
        '''
        if input is dataframe, this function will convert the input to list
        '''
        try:
            locations = locations.values.tolist()
        except:
            if isinstance(locations, list):
                pass
            else:
                raise TypeError('Acceptable input types: list and dataframe')
        return locations

    def add_point(self, points):
        '''
        Add location markers on the canvass
        Args:
            points: the points to be added. We assume the first two dimension are locational information.
            The orders has to be latitude, longitude
        '''
        points = self._pandas_to_list(points)
        for point in points:
            Marker(location=point[0:2]).add_to(self.canvass)
        return self

    def add_point_clustered(self, points):
        '''
        Add location markers, but will automatically make cluster if there is too many.
        Args:
            points: the points to be added. We assume the first two dimension are locational information
        '''
        points = self._pandas_to_list(points)
        marker_cluster = MarkerCluster(icons="dd").add_to(self.canvass)
        for point in points:
            Marker(location = point[0:2]).add_to(marker_cluster)
        return self

    def add_contour(self, contour='SC'):
        '''
        Add the contour information on the canvass
        Args:
            contour: allow three types of information:
                1.Statenames: like SC, NC or north carolina
                2.shapely polygon type
                3.list of coords
        '''
        print('-'*20)
        print('allow two input types: 1. eg. DC, SC 2. a list of coordinates')

        if isinstance(contour, str):
            polygon = get_state_contours(contour)[-1]
            make_coords = True

        elif isinstance(contour, shapely.geometry.polygon.Polygon):
            polygon = contour
            make_coords = True

        elif isinstance(contour, list):
            make_coords = False

        else:
            raise TypeError('only support str, list and polygon type')

        if make_coords:
            longitudes, latitudes = polygon.exterior.coords.xy
            list_of_coords = list(zip(latitudes, longitudes))

        PolygonMarker(list_of_coords, color='blue', fill_opacity=0.2, weight=1).add_to(self.canvass)

        return self

    def add_value(self, values, multiplier=4):
        values = self._pandas_to_list(values)
        for record in values:
            value = record[2]; location_info = record[0:2]
            color = "#ff0000" if value >=0 else "#000000" # pos and neg have different colors
            CircleMarker(location=location_info,
                         radius=multiplier*abs(value),
                         alpha=0.5,
                         fill=True,
                         fill_color=color,
                         color=color).add_to(self.canvass)
        return self

    def plot(self, open_in_browser=True, filename=None):
        if filename is None:
            filename = 'map_test.html'

        output_path = os.path.join(os.getcwd(), filename)
        self.canvass.save(output_path)
        print('-'*20)
        print(f'the map has been saved to {filename}')

        if filename == 'map_test.html':
            print('to change to a different name, assign a name to filename')
            print('when calling plot() function')
        if open_in_browser:
            webbrowser.open('file://' + os.path.realpath(output_path))

if __name__ == '__main__':
    #the test based on list input
    s = SpatialPlotter([[34, -80]])
    s.add_point([[33.9, -80], [33.8, -80], [33.2, -80]])\
     .add_contour('North Carolina')\
     .add_value([[33.9, -80, 1], [34.9, -80, 20]])\
     .plot()

    # #the test case based on dataframe input
    # from SampleDataLoader import load_rainfall_data
    # test = load_rainfall_data('monthly')
    # new_map = SpatialPlotter([[34, -80]])\
    #             .add_point(test[['LATITUDE', 'LONGITUDE']])\
    #             .plot(filename='map_test2.html')


import serial
import time
import calendar

connected = False

ser = serial.Serial("COM3", 9600)
ser.close()
ser.open()

while not connected:
  serin = ser.read()
  connected = True

#ser.write("1")

#while ser.read() == '1':
#  ser.read()

while True:
  string = "T" + str(calendar.timegm(time.localtime())) + "\n"
  ser.write(string)
  time.sleep(60*5)

ser.close()