"""
Author: Zhuo Su, Wenzhe Liu
Date: Feb 18, 2021
"""

import math
import numpy as np

import torch
import torch.nn as nn
import torch.nn.functional as F

import torch.nn as nn
import torch
from torch.nn import functional as F
from torchvision import models
import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.nn import init
import functools
from torch.optim import lr_scheduler

###############################################################################
# Helper Functions
###############################################################################
def get_norm_layer(norm_type='instance'):
    """Return a normalization layer
    Parameters:
        norm_type (str) -- the name of the normalization layer: batch | instance | none
    For BatchNorm, we use learnable affine parameters and track running statistics (mean/stddev).
    For InstanceNorm, we do not use learnable affine parameters. We do not track running statistics.
    """
    if norm_type == 'batch':
        norm_layer = functools.partial(nn.BatchNorm2d, affine=True, track_running_stats=True)
    elif norm_type == 'instance':
        norm_layer = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False)
    elif norm_type == 'none':
        norm_layer = None
    else:
        raise NotImplementedError('normalization layer [%s] is not found' % norm_type)
    return norm_layer

def make_layers(cfg, in_channels = 3, batch_norm=False, dilation = False):
    if dilation:
        d_rate = 2
    else:
        d_rate = 1
    layers = []
    for v in cfg:
        if v == 'M':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=d_rate,dilation = d_rate)
            if batch_norm:
                layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
            else:
                layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    return nn.Sequential(*layers)

class block(nn.Module):
    """
    Compact Dilation Convolution based Module
    """
    def __init__(self, in_channels):
        super(block, self).__init__()

        self.conv0_0 = conv_layers(in_channels, in_channels,1)
        self.conv0_1 = conv_layers(in_channels, in_channels,2)

        self.pool0 = pool_layers()
        self.poollast = pool_layers()

        self.conv1_0 = conv_layers(in_channels, in_channels,2)
        self.conv1_1 = conv_layers(in_channels, in_channels,2)

        self.pool1 = pool_layers()

        self.conv2_0 = conv_layers(in_channels, in_channels,2)
        self.conv2_1 = conv_layers(in_channels, in_channels,2)
        self.conv2_2 = conv_layers(in_channels, in_channels,2)

        self.pool2 = pool_layers()

        self.conv3_0 = conv_layers(in_channels, in_channels,2)
        self.conv3_1 = conv_layers(in_channels, in_channels,2)
        self.conv3_2 = conv_layers(in_channels, in_channels,2)
        self.classifier = nn.Conv2d(in_channels*3, 1, kernel_size=1)

    def forward(self, x):
        H, W = x.size()[2:]

        x = self.conv0_0(x)
        x = self.conv0_1(x)

        x = self.pool0(x)

        x = self.conv1_0(x)

        x = self.conv1_1(x)
        e1 = F.interpolate(x, (H, W), mode="bilinear", align_corners=False)

        x = self.pool1(x)

        x = self.conv2_0(x)

        x = self.conv2_1(x)

        x = self.conv2_2(x)
        e2 = F.interpolate(x, (H, W), mode="bilinear", align_corners=False)

        x = self.pool2(x)

        x = self.conv3_0(x)

        x = self.conv3_1(x)

        x = self.conv3_2(x)
        e3 = F.interpolate(x, (H, W), mode="bilinear", align_corners=False)

        return e1+e2+e3

class blockbn(nn.Module):
    """
    Compact Dilation Convolution based Module
    """
    def __init__(self, in_channels):
        super(blockbn, self).__init__()
        #self.norm_layer = get_norm_layer(norm_type='batch')
        norm_layer = get_norm_layer(norm_type='batch')

        self.conv0_0 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))
        self.conv0_1 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))

        self.pool0 = pool_layers()

        self.conv1_0 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))
        self.conv1_1 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))

        self.pool1 = pool_layers()

        self.conv2_0 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))
        self.conv2_1 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))
        self.conv2_2 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))

        self.pool2 = pool_layers()

        self.conv3_0 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))
        self.conv3_1 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))
        self.conv3_2 = nn.Sequential(*self._conv_block(in_channels, in_channels, norm_layer, num_block=1))
        self.classifier = nn.Conv2d(in_channels*3, 1, kernel_size=1)
    def _conv_block(self, in_nc, out_nc, norm_layer, num_block=1, kernel_size=3,
        stride=1, padding=1, bias=False):
        conv = []
        for i in range(num_block):
            cur_in_nc = in_nc if i == 0 else out_nc
            conv += [nn.Conv2d(cur_in_nc, out_nc, kernel_size=kernel_size, stride=stride,
                               padding=padding, bias=bias),
                     norm_layer(out_nc),
                     nn.ReLU(True)]
        return conv

    def forward(self, x):
        H, W = x.size()[2:]

        x = self.conv0_0(x)
        x = self.conv0_1(x)

        x = self.pool0(x)

        x = self.conv1_0(x)

        x = self.conv1_1(x)
        e1 = F.interpolate(x, (H, W), mode="bilinear", align_corners=False)

        x = self.pool1(x)

        x = self.conv2_0(x)

        x = self.conv2_1(x)

        x = self.conv2_2(x)
        e2 = F.interpolate(x, (H, W), mode="bilinear", align_corners=False)

        x = self.pool2(x)

        x = self.conv3_0(x)

        x = self.conv3_1(x)

        x = self.conv3_2(x)
        e3 = F.interpolate(x, (H, W), mode="bilinear", align_corners=False)
        return e1+e2+e3

def conv_layers(inp, oup, dilation):
    #if dilation:
    d_rate = dilation
    #else:
    #    d_rate = 1
    return nn.Sequential(
        nn.Conv2d(inp, oup, kernel_size=3, padding=d_rate, dilation=d_rate),
        nn.ReLU(inplace=True)
    )

def feature_transform(inp, oup):
    conv2d = nn.Conv2d(inp, oup, kernel_size=1)  # no padding
    relu = nn.ReLU(inplace=True)
    layers = []
    layers += [conv2d, relu]
    return nn.Sequential(*layers)

def pool_layers(ceil_mode=True):
    return nn.MaxPool2d(kernel_size=3, stride=2)

class CHRNet(nn.Module): # one conv layer after fully connetced CNN _average3
    def __init__(self):
        super(CHRNet, self).__init__()
        self.seen = 0

        self.conv0_0 = conv_layers(3, 16,4)
        self.pool00 = pool_layers()
        self.conv0_1 = conv_layers(16, 16,4)

        self.pool0 = block(16)
        self.pool0_bn = blockbn(16)

        self.conv1_0 = conv_layers(16, 32,4)
        self.conv1_1 = conv_layers(32, 32,4)

        self.pool1 = block(32)
        self.pool1_bn = blockbn(32)

        self.conv2_0 = conv_layers(32, 64,4)
        self.conv2_1 = conv_layers(64, 64,4)
        self.conv2_2 = conv_layers(64, 64,4)

        self.pool2 = block(64)
        self.pool2_bn = blockbn(64)

        self.conv3_0 = conv_layers(64, 128,4)
        self.conv3_1 = conv_layers(128, 128,4)
        self.conv3_2 = conv_layers(128,128,4)

        self.output_layer0 = nn.Conv2d(16, 1, kernel_size=1)
        self.output_layer1 = nn.Conv2d(32, 1, kernel_size=1)
        self.output_layer2 = nn.Conv2d(64, 1, kernel_size=1)
        self.output_layer3 = nn.Conv2d(256, 1, kernel_size=1)

        self.output_layer4 = nn.Conv2d(128, 1, kernel_size=1)
        self.classifier1 = nn.Conv2d(2, 1, kernel_size=1)
        self.classifier4 = nn.Conv2d(4, 1, kernel_size=1)
        self.classifier= nn.Conv2d(6, 1, kernel_size=1)

        #self._initialize_weights()
        self.features = []
    def get_weights(self):
        conv_weights = []
        bn_weights = []
        relu_weights = []
        for pname, p in self.named_parameters():
            if 'bn' in pname:
                bn_weights.append(p)
            elif 'relu' in pname:
                relu_weights.append(p)
            else:
                conv_weights.append(p)

        return conv_weights, bn_weights, relu_weights
    def forward(self, x):
        self.features = []
        H, W = x.size()[2:]

        x = self.conv0_0(x)
        x = self.pool00(x)
        x = self.conv0_1(x)

        x1 = self.pool0(x)

        x = self.conv1_0(x+x1)
        y1 = self.conv1_1(x)

        e11 = F.interpolate(y1, (H, W), mode="bilinear")

        x2_bn = self.pool1_bn(y1)

        e22 = F.interpolate(x2_bn, (H, W), mode="bilinear")

        x = self.conv2_0(x2_bn+y1)

        x = self.conv2_1(x)
        y2 = self.conv2_2(x)

        x3_bn = self.pool2_bn(y2)

        e33 = F.interpolate(x3_bn, (H, W), mode="bilinear")
        x = self.conv3_0(x3_bn+y2)

        y3 = self.conv3_1(x)
        e3 = F.interpolate(y3, (H, W), mode="bilinear")

        e1 = F.interpolate(y1, (H, W), mode="bilinear")
        e2 = F.interpolate(y2, (H, W), mode="bilinear")

        e1=self.output_layer1(e1)
        e11=self.output_layer1(e11)
        e2=self.output_layer2(e2)
        e22=self.output_layer1(e22)
        e3= self.output_layer4(e3)
        e33=self.output_layer2(e33)

        outputs = [e1,e11,e2,e22,e3,e33]

        x = self.classifier(torch.cat(outputs, dim=1))

        outputs.append(x)

        outputs = [torch.sigmoid(r) for r in outputs]
        return outputs

def chrnet(args):

    return CHRNet()


# References https://github.com/Sayan98/pytorch-segnet/blob/master/src/model.py
# Small segnet version
import torch
import torch.nn as nn
import torch.nn.functional as F
import warnings
warnings.filterwarnings("ignore")

class Conv2dSame(torch.nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size, bias=True, padding_layer=torch.nn.ReflectionPad2d):
        super().__init__()
        ka = kernel_size // 2
        kb = ka - 1 if kernel_size % 2 == 0 else ka
        self.net = torch.nn.Sequential(
            padding_layer((ka,kb,ka,kb)),
            torch.nn.Conv2d(in_channels, out_channels, kernel_size, bias=bias)
        )
    def forward(self, x):
        return self.net(x)

class Segnet(nn.Module):
	def __init__(self, input_channels=3,input_width=480, input_height=360, n_classes=10):
		super(Segnet,self).__init__()
		self.input_channels = input_channels
		self.input_width = input_width
		self.input_height = input_height
		self.n_classes = n_classes

		## Encode
		self.conv1 = nn.Conv2d(in_channels=input_channels, out_channels=64, kernel_size=3, padding=1)
		self.conv1_bn = nn.BatchNorm2d(64)
		self.pool1 = torch.nn.MaxPool2d(kernel_size=2, stride=2, return_indices=True)

		self.conv2 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)
		self.conv2_bn = nn.BatchNorm2d(128)
		self.pool2 = torch.nn.MaxPool2d(kernel_size=2, stride=2, return_indices=True)

		self.conv3 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, padding=1)
		self.conv3_bn = nn.BatchNorm2d(256)
		self.pool3 = torch.nn.MaxPool2d(kernel_size=2, stride=2, return_indices=True)

		self.conv4 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, padding=1)
		self.conv4_bn = nn.BatchNorm2d(512)
		self.pool4 = torch.nn.MaxPool2d(kernel_size=2, stride=2, return_indices=True)

		## Decode
		#self.up1 = nn.Upsample(scale_factor=2, mode='bilinear',align_corners=True)
		self.decoder1 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=3, padding=1)
		self.decoder1_bn = nn.BatchNorm2d(256)

		#self.up2 = nn.Upsample(scale_factor=2, mode='bilinear',align_corners=True)
		self.decoder2 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=3, padding=1)
		self.decoder2_bn = nn.BatchNorm2d(128)

		#self.up3 = nn.Upsample(scale_factor=2, mode='bilinear',align_corners=True)
		self.decoder3 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=3, padding=1)
		self.decoder3_bn = nn.BatchNorm2d(64)

		self.decoder4 = nn.ConvTranspose2d(in_channels=64, out_channels=n_classes, kernel_size=3, padding=1)
		self.decoder4_bn = nn.BatchNorm2d(n_classes)

		self.softmax = nn.Softmax(dim=1)

	def forward(self,x):

		# Encoder phase
		conv1 = self.conv1(x)
		conv1 = F.relu(self.conv1_bn(conv1))
		conv1, ind1 = self.pool1(conv1)

		conv2 = self.conv2(conv1)
		conv2 = F.relu(self.conv2_bn(conv2))
		conv2, ind2 = self.pool2(conv2)

		conv3 = self.conv3(conv2)
		conv3 = F.relu(self.conv3_bn(conv3))
		conv3, ind3 = self.pool3(conv3)

		conv4 = self.conv4(conv3)
		conv4 = F.relu(self.conv4_bn(conv4))
		conv4, ind4 = self.pool4(conv4)

		# Decoder phase
		decod1 = F.max_unpool2d(conv4, ind4, kernel_size=2, stride=2, output_size=conv3.size())
		decod1 = self.decoder1(decod1)
		decod1 = F.relu(self.decoder1_bn(decod1))

		decod2 = F.max_unpool2d(decod1, ind3, kernel_size=2, stride=2, output_size=conv2.size())
		decod2 = self.decoder2(decod2)
		decod2 = F.relu(self.decoder2_bn(decod2))

		decod3 = F.max_unpool2d(decod2, ind2, kernel_size=2, stride=2, output_size=conv1.size())
		decod3 = self.decoder3(decod3)
		decod3 = F.relu(self.decoder3_bn(decod3))

		decod4 = F.max_unpool2d(decod3, ind1, kernel_size=2, stride=2, output_size=x.size())
		decod4 = self.decoder4(decod4)
		decod4 = F.relu(self.decoder4_bn(decod4))

		output = decod4.view(-1,self.n_classes,self.input_width*self.input_height)
		output = self.softmax(output)
		return output

if __name__ == "__main__":
	import numpy as np
	batch_size = 1
	n_channels = 3
	input_width = 480
	input_height = 360
	n_classes = 10
	nz = torch.Tensor(np.zeros((batch_size,n_channels,input_width,input_height)))
	uz = torch.ones(batch_size,input_width*input_height,dtype=torch.long)
	model = Segnet()
	outputs = model.forward(nz)

	criterion = nn.CrossEntropyLoss()
	learning_rate = 1e-4
	optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

	loss = criterion(outputs, uz)
	loss.backward()


import math
from plugin import plugin

@plugin("gui calculator")
class Calculator:
    """
    Opens GUI Calculator.

    {Note: Trigonometric ratios take value in degrees.}
    """

    def __call__(self, jarvis, s):
        try:
            import tkinter as tk
        except ModuleNotFoundError:
            jarvis.say("Tkinter not installed!")

        self.a = 0
        self.operator = ""

        """Calculator window"""
        self.calcwin = tk.Tk()
        self.calcwin.title('Jarvis Calculator')
        self.calcwin.geometry("350x450")
        self.calcwin.resizable(False, False)
        self.calcwin.attributes('-type', 'dialog')
        self.calcwin.attributes('-topmost', True)
        self.calcwin.config(bg="blue")

        """Buttons and other widgets"""
        self.num = tk.Entry(self.calcwin, width=18,
                            font=("Arial black", 20))
        self.num.place(x=17, y=45)
        self.num.focus_set()

        self.buffer = tk.Label(self.calcwin, bg="yellow", width=27,
                               fg="red", anchor='w', font=("", 13))
        self.buffer.place(x=23, y=10)

        self.ans = tk.Label(self.calcwin, bg="light green", width=17,
                            fg="black", font=("Arial black", 20))
        self.ans.place(x=27, y=95)

        self.plusbtn = tk.Button(self.calcwin, text="+",
                                 font=("Elephant", 20), command=self.plus)
        self.plusbtn.config(width=3)
        self.plusbtn.place(x=8, y=165)

        self.minusbtn = tk.Button(
            self.calcwin, text="-", font=("Elephant", 20),
            command=self.minus)
        self.minusbtn.config(width=3)
        self.minusbtn.place(x=93, y=165)

        self.multbtn = tk.Button(
            self.calcwin, text="x", font=("Elephant", 20),
            command=self.mult)
        self.multbtn.config(width=3)
        self.multbtn.place(x=178, y=165)

        self.divbtn = tk.Button(
            self.calcwin, text="/", font=("Elephant", 20),
            command=self.div)
        self.divbtn.config(width=3)
        self.divbtn.place(x=263, y=165)

        self.sqrootbtn = tk.Button(
            self.calcwin, text="sqrt", font=("Elephant", 20),
            command=self.sqroot)
        self.sqrootbtn.config(height=1, width=3)
        self.sqrootbtn.place(x=8, y=245)

        self.factbtn = tk.Button(
            self.calcwin, text="!", font=("Elephant", 20),
            command=self.fact)
        self.factbtn.config(width=3)
        self.factbtn.place(x=93, y=245)

        self.powerbtn = tk.Button(
            self.calcwin, text="x^y", font=("Elephant", 20),
            command=self.power)
        self.powerbtn.config(height=1, width=3)
        self.powerbtn.place(x=178, y=245)

        self.c_btn = tk.Button(self.calcwin, text="C",
                               font=("Elephant", 20),
                               command=self.c)
        self.c_btn.config(height=1, width=3)
        self.c_btn.place(x=263, y=245)

        self.sinbtn = tk.Button(
            self.calcwin, text="sin", font=("Elephant", 20),
            command=self.sin)
        self.sinbtn.config(height=1, width=3)
        self.sinbtn.place(x=8, y=325)

        self.cosbtn = tk.Button(
            self.calcwin, text="cos", font=("Elephant", 20),
            command=self.cos)
        self.cosbtn.config(height=1, width=3)
        self.cosbtn.place(x=93, y=325)

        self.tanbtn = tk.Button(
            self.calcwin, text="tan", font=("Elephant", 20),
            command=self.tan)
        self.tanbtn.config(height=1, width=3)
        self.tanbtn.place(x=178, y=325)

        self.equalbtn = tk.Button(
            self.calcwin, text="=", font=("Elephant", 20),
            command=lambda: self.equal(0))
        self.equalbtn.config(width=3)
        self.equalbtn.place(x=263, y=325)

        self.exitbtn = tk.Button(
            self.calcwin, text="EXIT", bg="red", fg="white",
            command=lambda: self.exit(0))
        self.exitbtn.place(x=150, y=405)

        self.keybinds()
        self.calcwin.mainloop()

    def plus(self):
        self.a = self.num.get()
        self.num.delete(0, 'end')
        self.num.focus_set()
        self.operator = "+"
        self.buffer["text"] = str(self.a) + " " + str(self.operator)

    def minus(self):
        self.a = self.num.get()
        self.num.delete(0, 'end')
        self.num.focus_set()
        self.operator = "-"
        self.buffer["text"] = str(self.a) + " " + str(self.operator)

    def mult(self):
        self.a = self.num.get()
        self.num.delete(0, 'end')
        self.num.focus_set()
        self.operator = "x"
        self.buffer["text"] = str(self.a) + " " + str(self.operator)

    def div(self):
        self.a = self.num.get()
        self.num.delete(0, 'end')
        self.num.focus_set()
        self.operator = "/"
        self.buffer["text"] = str(self.a) + " " + str(self.operator)

    def sqroot(self):
        try:
            self.a = self.num.get()
            self.buffer["text"] = "sqrt(" + str(self.a) + ")"
            if float(self.a) >= 0:
                self.an = math.sqrt(float(self.a))
                if len(str(self.an)) > 17:
                    self.ans["text"] = str(round(self.an, 4))
                else:
                    self.ans["text"] = str(math.sqrt(float(self.a)))
            else:
                self.ans["text"] = "Not a real number"
        except ValueError:
            self.ans["text"] = "Invalid Input "
        except TypeError:
            self.ans["text"] = "Invalid Input "
        except OverflowError:
            self.ans["text"] = "Out of range"

    def fact(self):
        try:
            self.a = self.num.get()
            self.buffer["text"] = str(self.a) + "!"
            if float(self.a) >= 0:
                self.an = str(math.factorial(float(self.a)))
                self.ans["text"] = self.an
                if len(self.an) > 17:
                    self.ans["text"] = "Out of Range"
            else:
                self.ans["text"] = "Error"
        except ValueError:
            self.ans["text"] = "Invalid Input "
        except TypeError:
            self.ans["text"] = "Invalid Input "
        except OverflowError:
            self.ans["text"] = "Out of range"

    def power(self):
        self.a = self.num.get()
        self.num.delete(0, 'end')
        self.num.focus_set()
        self.operator = "^"
        self.buffer["text"] = str(self.a) + str(self.operator)

    def sin(self):
        try:
            self.a = float(self.num.get()) / 57.2958  # convert to deg
            self.buffer["text"] = "sin(" + str(self.num.get()) + ")"
            self.an = str(round(math.sin(float(self.a)), 4))
            self.ans["text"] = self.an
            if len(self.an) > 17:
                self.ans["text"] = "Out of Range"
        except ValueError:
            self.ans["text"] = "Invalid Input "
        except TypeError:
            self.ans["text"] = "Invalid Input "
        except OverflowError:
            self.ans["text"] = "Out of range"

    def cos(self):
        try:
            self.a = float(self.num.get()) / 57.2958  # convert to deg
            self.buffer["text"] = "cos(" + str(self.num.get()) + ")"
            self.an = str(round(math.cos(float(self.a)), 4))
            self.ans["text"] = self.an
            if len(self.an) > 17:
                self.ans["text"] = "Out of Range"
        except ValueError:
            self.ans["text"] = "Invalid Input "
        except TypeError:
            self.ans["text"] = "Invalid Input "
        except OverflowError:
            self.ans["text"] = "Out of range"

    def tan(self):
        try:
            self.a = float(self.num.get()) / 57.2958  # convert to deg
            self.buffer["text"] = "tan(" + str(self.num.get()) + ")"
            self.an = str(round(math.tan(float(self.a)), 4))
            self.ans["text"] = self.an
            if len(self.an) > 17:
                self.ans["text"] = "Out of Range"
        except ValueError:
            self.ans["text"] = "Invalid Input "
        except TypeError:
            self.ans["text"] = "Invalid Input "
        except OverflowError:
            self.ans["text"] = "Out of range"

    def c(self):
        """clear button for calculator"""
        self.a = 0
        self.b = 0
        self.num.delete(0, 'end')
        self.ans["text"] = ""
        self.buffer["text"] = ""

    def equal(self, event):
        try:
            self.b = self.num.get()
            if self.operator == "+":
                self.an = str(float(self.a) + float(self.b))
                if len(self.an) < 17:
                    self.ans["text"] = self.an
                else:
                    self.ans["text"] = "Out of Range"
            elif self.operator == "-":
                self.an = str(float(self.a) - float(self.b))
                if len(self.an) < 17:
                    self.ans["text"] = self.an
                else:
                    self.ans["text"] = "Out of Range"
            elif self.operator == "x":
                self.an = str(float(self.a) * float(self.b))
                if len(self.an) < 17:
                    self.ans["text"] = self.an
                else:
                    self.ans["text"] = "Out of Range"
            elif self.operator == "^":
                self.an = str(math.pow(float(self.a), float(self.b)))
                if len(self.an) < 17:
                    self.ans["text"] = self.an
                else:
                    self.ans["text"] = "Out of Range"
            elif self.operator == "/":
                if float(self.b) != 0:
                    self.an = str(round(float(self.a) / float(self.b), 3))
                    if len(self.an) < 17:
                        self.ans["text"] = self.an
                    else:
                        self.ans["text"] = "Out of Range"
                else:
                    self.ans["text"] = "Not a number"
        except ValueError:
            self.ans["text"] = "Invalid Input "
        except TypeError:
            self.ans["text"] = "Invalid Input "
        except OverflowError:
            self.ans["text"] = "Out of range"

    def exit(self, event):
        self.calcwin.destroy()

    def keybinds(self):
        self.calcwin.bind('<Return>', self.equal)
        self.calcwin.bind('<Escape>', self.exit)


from ext_cloud.BaseCloud.BaseNetworks.BaseGateway import BaseGatewaycls
from boto import vpc
from ext_cloud.AWS.AWSBaseCloud import AWSBaseCloudcls

class AWSGatewaycls(AWSBaseCloudcls, BaseGatewaycls):

    __aws_gateway = None
    __vpc = None

    def __init__(self, *arg, **kwargs):
        self.__aws_gateway = arg[0]
        self._aws_ref = arg[0]
        name = None
        if 'name' in self.__aws_gateway.tags:
            name = self.__aws_gateway.tags['name']

        super(AWSGatewaycls, self).__init__(id=self.__aws_gateway.id, name=name, credentials=kwargs['credentials'])

    @AWSBaseCloudcls.name.setter
    def name(self, value):
        self.addtag('Name', value)
        self._name = value

    @property
    def state(self):
        return self.__aws_gateway.state

    @property
    def __Vpc(self):
        return self.__vpc

    @__Vpc.getter
    def __Vpc(self):
        if self.__vpc is None:
            self.__vpc = vpc.boto.connect_to_region(self._credentials['region_name'], aws_access_key_id=self._credentials['username'], aws_secret_access_key=self._credentials['password'])
        return self.__vpc

    def delete(self):
        self.__Vpc.delete_internet_gateway(self._id)


#!/usr/bin/env python3
from sys import exit
from test.http_test import HTTPTest
from misc.wget_file import WgetFile

"""
    This test ensures that Wget handles Cookie expiry dates correctly.
    Simultaneuously, we also check if multiple cookies to the same domain
    are handled correctly
"""
############# File Definitions ###############################################
File1 = "Hello World!"
File2 = "'Ello! This is Amazing!"
File3 = "So what are we looking at?"
File4 = "This was downloaded"

File1_rules = {
    "SendHeader"        : {
        "Set-Cookie"    : "sess-id=0213; path=/"
    }
}
File2_rules = {
    "ExpectHeader"      : {
        "Cookie"        : "sess-id=0213"
    },
    "SendHeader"        : {
        "Set-Cookie"    : "new-sess=N"
    }
}
File3_rules = {
    "SendHeader"        : {
        # use upper case 'I' to provoke Wget failure with turkish locale
        "Set-Cookie"    : "sess-id=0213; path=/; ExPIRes=Sun, 06 Nov 2001 12:32:43 GMT"
    },
    "ExpectHeader"      : {
        "Cookie"        : "new-sess=N; sess-id=0213"
    }
}
File4_rules = {
    "RejectHeader"      : {
        "Cookie"        : "sess-id=0213"
    },
    "ExpectHeader"      : {
        "Cookie"        : "new-sess=N"
    }
}
A_File = WgetFile ("File1", File1, rules=File1_rules)
B_File = WgetFile ("File2", File2, rules=File2_rules)
C_File = WgetFile ("File3", File3, rules=File3_rules)
D_File = WgetFile ("File4", File4, rules=File4_rules)

WGET_OPTIONS = ""
WGET_URLS = [["File1", "File2", "File3", "File4"]]

Files = [[A_File, B_File, C_File, D_File]]

ExpectedReturnCode = 0
ExpectedDownloadedFiles = [A_File, B_File, C_File, D_File]

################ Pre and Post Test Hooks #####################################
pre_test = {
    "ServerFiles"       : Files
}
test_options = {
    "WgetCommands"      : WGET_OPTIONS,
    "Urls"              : WGET_URLS
}
post_test = {
    "ExpectedFiles"     : ExpectedDownloadedFiles,
    "ExpectedRetcode"   : ExpectedReturnCode
}

err = HTTPTest (
                pre_hook=pre_test,
                test_params=test_options,
                post_hook=post_test
).begin ()

exit (err)


# -*- coding: utf-8 -*-
"""
-------------------------------------------------------------------------------
GUFY - Copyright (c) 2019, Fabian Balzer

Distributed under the terms of the GNU General Public License v3.0.

The full license is in the file LICENSE.txt, distributed with this software.
-------------------------------------------------------------------------------

@author: Fabian Balzer (fabian.balzer@studium.uni-hamburg.de)

A script for writing plot scripts.

The module is structured as follows:
    - The WriteToScriptDialog class for the write-to-script options
    - Functions for saving the file and writing it
    - Functions for constructing the slice and projection plot strings
    - Functions for constructing the line and phase plot strings
    - Function for constructing the profile plot strings
          (All variants including time series)
    - Function for constructing the plot-making string
    - Functions to construct time series strings
    - Functions to construct miscellaneous strings like derived fields, file
          loading and annotations (alphabetical order)
"""
import PyQt5.QtWidgets as QW
import PyQt5.QtCore as QC
import PyQt5.QtGui as QG
from datetime import datetime
import math
import yt
from simgui_modules.utils import getCalcQuanName, getCalcQuanString
from simgui_modules.additionalWidgets import GUILogger
from simgui_modules.checkBoxes import coolCheckBox

# %% The WriteToScriptDialog class for the write-to-script options
class WriteToScriptDialog(QW.QDialog):
    """A dialog that pops up if the user wants to write the settings of the GUI
    or the plot to a reproducible script.
    Params:
        Param_Dict: For receiving information that has to be written to script
        PlotWindow: Whether the instance belongs to a plot window or the
                    general GUI
    """
    def __init__(self, Param_Dict, PlotWindow=True):
        super().__init__()
        if PlotWindow:
            GUILogger.info("Options for writing the plot displayed as a reproducible skript")
            self.setWindowTitle("Options for writing the plot to script")
            self.text = "that were used to create the plot"
        else:
            GUILogger.info("Options for writing settings of the GUI as a reproducible skript")
            self.setWindowTitle("Options for writing the settings to script")
            self.text = "of the GUI as they are specified in the Plot options"
        GUILogger.info("Detailed information for making plots using yt can "
                       "also be found "
                       'here, '
                       'here and '
                       'here.')
        self.noComments = False
        self.plotAll = False
        self.Param_Dict = Param_Dict
        self.setModal(True)
        self.initUi(PlotWindow)
        self.signalsConnection()
        self.resize(400, 300)
        self.setWindowIcon(QG.QIcon('simgui_registry/CoverIcon.png'))
        self.show()

    def initUi(self, PlotWindow):
        """Initialize all of the ingredients for this UI."""
        # Buttons for closing the window:
        self.buttonBox = QW.QDialogButtonBox(self)
        self.buttonBox.addButton("Create script", QW.QDialogButtonBox.AcceptRole)
        self.buttonBox.addButton("Cancel", QW.QDialogButtonBox.RejectRole)
        self.textBrowser = QW.QTextBrowser()
        text = ""
        if not PlotWindow:
            text += ("""
Warning: Due to the way
                     the script writing works, the script might be
                     incomplete concerning some of the extrema or other
                     settings.
""")
        text += f"""
If you want, I can now produce a script with the
        settings {self.text} for you.

Pressing the Create script-button will prompt you
        to enter a filename of where the script is to be created.

If you don't want comments in the script - which is understandable
        if you're experienced, for beginners they might be useful - just
        check the checkbox below.
"""
        self.commentCheckBox = coolCheckBox(text="Disable comments", width=None)
        layout = QW.QVBoxLayout()
        layout.addWidget(self.textBrowser)
        layout.addWidget(self.commentCheckBox)
        series = self.Param_Dict["isValidSeries"]
        noMultiple = not self.Param_Dict["TimeSeriesProf"]
        notTime = not self.Param_Dict["XAxis"] == "time"
        if series and noMultiple and notTime:
            text += """
Since you have loaded a time series, you may transfer
            the Plot all functionality to the script as well. Just tick
            the corresponding CheckBox"""
            self.makeSeriesCheckBox = coolCheckBox(text="Write script to plot all", width=None)
            self.makeSeriesCheckBox.toggled.connect(self.getSeriesInput)
            layout.addWidget(self.makeSeriesCheckBox)
        self.textBrowser.setHtml(text)
        layout.addWidget(self.buttonBox)
        self.setLayout(layout)

    def signalsConnection(self):
        """Connects all the signals and emits some of them for a proper start
        """
        self.buttonBox.accepted.connect(self.applyPressed)
        self.buttonBox.rejected.connect(self.cancelPressed)
        self.commentCheckBox.toggled.connect(self.getCommentInput)
        self.accepted.connect(lambda: saveFile(self))

    def applyPressed(self):
        """Handles the Button Press of 'Create n plots'"""
        self.accept()

    def cancelPressed(self):
        """Handles the Button press of 'Cancel'"""
        self.reject()

    def getCommentInput(self, state):
        """Saves the state of the checkbox"""
        self.noComments = state

    def getSeriesInput(self, state):
        self.plotAll = state

# %% Functions for saving the file and writing it
def saveFile(dialog):
    """Ask the user to give a name for the file to be written out and save
    the plot with its current settings as a script."""
    filename = QW.QFileDialog.getSaveFileName(dialog, 'Write Plot to file',
                                              dialog.Param_Dict["Directory"],
                                              "Python files (*.py);; "
                                              "All files (*)")[0]
    if filename != "":
        writePlotToScript(dialog.Param_Dict, filename, dialog.noComments,
                          dialog.plotAll)
        GUILogger.log(29, "A script to reproduce the plot has been writt"
                      f"en to {filename}.")
    else:
        GUILogger.info("No filename selected.")

def writePlotToScript(Param_Dict, filename, noComments, plotAll):
    """Write the plot passed through Param_Dict to a file filename.
    Params:
        Param_Dict: Parameter dictionary containing all of the settings
        filename: Name of the file to be written (including directory)
        noComments: bool if the user doesn't want comments
        plotAll: For time series if the user wants to have iteration
    """
    if not filename.endswith(".py"):
        filename += ".py"
    text = constructCompleteString(Param_Dict, plotAll, filename)
    if noComments:
        textLines = text.split("\n")
        # remove all lines that begin with a hashtag:
        textLines = [line for line in textLines if not line.strip().startswith("#")]
        textLines.insert(0, "# -*- coding: utf-8 -*-")
        # remove all in-line-comments:
        text = ""
        for line in textLines:
            if "#" in line:
                text += line.split("  # ")[0]
            elif not line.isspace():  # check if all of the characters are spaces
                text += line
            text += "\n"
        # remove all 'Note:'-blocks:
        textBlocks = text.split('"""\nNote:')
        text = ""
        for i, block in enumerate(textBlocks):
            if i != 0:
                block = "".join(block.split('"""')[1:])
            text += block
    with open(filename, "w") as file:
        file.write(text)

# %% Function for writing the introduction
def constructCompleteString(Param_Dict, plotAll, filename):
    """Construct a long string that can be used to replot the plot with its
    current settings.
    Params:
        Param_Dict: Parameter dictionary containing all of the plot data
    Returns:
        text: String that can be used to replot the plot
    """
    introString = f'''# -*- coding: utf-8 -*-
"""
{filename.split("/")[-1]}
Script containing the plot produced using GUFY - GUI for FLASH Code simulations
based on yt.

Created on {datetime.now().strftime("%a, %b %d %X %Y")}.

Contains a suggestion to reproduce the plot(s).
Since it is dynamically created, it may not be the prettiest but I hope it can
help to get started.\n
If you detect any bugs or have questions, please contact me via email through
fabian.balzer@studium.uni-hamburg.de.
"""
import yt
import matplotlib.pyplot as plt
'''
    if Param_Dict["DimMode"] == "2D":
        introString += "from mpl_toolkits.axes_grid1 import make_axes_locatable\n"
    if plotAll:
        introString += "import os"
    introString += "\n\n"
    introString += constructDerFieldString(Param_Dict)
    if checkTimeSeriesPlot(Param_Dict, plotAll):
        plotString = constructSeriesString(Param_Dict, plotAll)
    elif Param_Dict["isValidFile"]:
        plotString = constructFileString(Param_Dict)
    else:
        return "# This shouldn't happen. Something went wrong writing the file"
    text = introString + plotString
    return text

def constructFileString(Param_Dict):
    """Constructs the string when handling a single file"""
    loadingString = "# Load the file through yt and save the dataset as ds:\n"
    loadingString += f'ds = yt.load("{Param_Dict["Directory"]}/{str(Param_Dict["CurrentDataSet"])}")\n\n'
    plotString = eval("construct"+Param_Dict["PlotMode"]+"Plot(Param_Dict)")
    text = loadingString + plotString
    return text

# %% Functions for constructing the slice and projection plot strings
def constructSlicePlot(Param_Dict):
    """Constructs the Slice plot script"""
    ds = Param_Dict["CurrentDataSet"]
    gridUnit = Param_Dict["GridUnit"]
    c0, c1, c2 = Param_Dict["XCenter"], Param_Dict["YCenter"], Param_Dict["ZCenter"]
    width = f'(({Param_Dict["HorWidth"]}, "{gridUnit}"), ({Param_Dict["VerWidth"]}, "{gridUnit}"))'
    field = Param_Dict["ZAxis"]
    if Param_Dict["NormVecMode"] == "Axis-Aligned":
        plotString = (
f'''
# Initialize a yt axis-aligned slice plot with the dataset ds, normal vector {Param_Dict["NAxis"]},
# field {field} and the optional parameters axes_unit, center, width and fontsize:
c0 = yt.YTQuantity({c0}, "{gridUnit}")  # Get the center coordinates.
c1 = yt.YTQuantity({c1}, "{gridUnit}")  # Unfortunately, using a YTArray
c2 = yt.YTQuantity({c2}, "{gridUnit}")  # does not work properly.
slc = yt.SlicePlot(ds, "{Param_Dict["NAxis"]}", "{field}", axes_unit="{gridUnit}",
                   center=[c0, c1, c2], width={width},
                   fontsize=14)
''')
    else:
        normVec = [Param_Dict[axis + "NormDir"] for axis in ["X", "Y", "Z"]]
        northVec = [Param_Dict[axis + "NormNorth"] for axis in ["X", "Y", "Z"]]
        plotString = (
f'''# Initialize a yt off-axis slice plot with the dataset ds, normal vector,
# field {field} and the optional parameters north_vector, axes_unit, center and
# fontsize. The north vector is the vector that defines the 'up'-direction:
c0 = yt.YTQuantity({c0}, "{gridUnit}")  # Get the center coordinates.
c1 = yt.YTQuantity({c1}, "{gridUnit}")  # Unfortunately, using a YTArray
c2 = yt.YTQuantity({c2}, "{gridUnit}")  # does not work properly.
slc = yt.OffAxisSlicePlot(ds, {normVec}, "{field}", north_vector={northVec},
                          axes_unit="{gridUnit}", fontsize=14, center=[c0, c1, c2])
''')
    plotString += f'# Hint: You can access the generated data using slc.frb["{field}"]\n\n\n'
    modString = ""
    fieldMin = Param_Dict["ZMin"]  # Float
    fieldMax = Param_Dict["ZMax"]
    if field not in Param_Dict["FieldMins"].keys():
        modString = (
f'''# It seems that you have not calculated extrema for {field}.
# You can do this by using
# minMaxArray = ds.all_data().quantities.extrema({field})
# which will return a YTArray with two entries plus the units.
''')
    unit = Param_Dict["ZUnit"]
    cmap = Param_Dict["ColorScheme"]
    modString += ("# Set unit, minimum, maximum and color scheme:\n"
                 'slc.set_unit("{0}", "{1}")\nslc.set_zlim("{0}", {2}, {3})  '
                 '# These are given in the same unit, {1}.\n'
                 'slc.set_cmap("{0}", "{4}")\n'.format(field, unit, fieldMin, fieldMax, cmap))
    log = Param_Dict["ZLog"]  # Boolean
    if fieldMin != "":
        modString += "# Set our field scaling logarithmic if wanted:\n"
        if min(fieldMin, fieldMax) <= 0 and log:
            modString += ('slc.set_log("{0}", True, linthresh=(({1}-{2})/1000))  '
            "# linthresh sets a linear scale for a small portion and then a symbolic one "
            "for negative values\n".format(field, fieldMax, fieldMin))
        else:
            modString += f'slc.set_log("{field}", {log})  # This may be redundant in some cases\n'
    zoom = Param_Dict["Zoom"]
    modString += f"slc.zoom({zoom})\n\n\n"

    # Do the annotations:
    annoString = ""
    title = Param_Dict["PlotTitle"]
    if title != "":
        annoString += f'slc.annotate_title("{title}")  # Give the plot the title it deserves.\n'
    if Param_Dict["Timestamp"]:
        annoString += ("slc.annotate_timestamp(corner='upper_left', draw_inset_box=Tr"
        "ue)  # There are many more modifications for the timestamp.\n")
    if Param_Dict["Geometry"] == "cartesian":
        if Param_Dict["Scale"]:
            annoString += "slc.annotate_scale(corner='upper_right')\n"
        if Param_Dict["Grid"]:
            annoString += ('WARNING = "There is a yt-internal bug where grid '
                          'annotation doesn\'t work if a center coordinate is '
                          'set to 0!"\nslc.annotate_grids()\n')
        if Param_Dict["ParticleAnno"]:
            if Param_Dict["PSlabWidth"] == "" or float(Param_Dict["PSlabWidth"]) == 0:
                Param_Dict["PSlabWidth"] = 1
            height = abs(Param_Dict["FieldMins"]["DomainHeight"] - Param_Dict["FieldMaxs"]["DomainHeight"])
            width = float(Param_Dict["PSlabWidth"])*height
            annoString += (f"slc.annotate_particles({width})\n")
        if Param_Dict["VelVectors"]:
            annoString += "slc.annotate_velocity(normalize=True)\n"
        if Param_Dict["VelStreamlines"]:
            annoString += ('WARNING = "There is a yt-internal bug where streamline '
                          'annotation doesn\'t work if a center coordinate is '
                          'set to 0!"\n')
            if Param_Dict["NAxis"] == "x":
                annoString += "slc.annotate_streamlines('velocity_y', 'velocity_z')\n"
            elif Param_Dict["NAxis"] == "y":
                annoString += "slc.annotate_streamlines('velocity_x', 'velocity_z')\n"
            elif Param_Dict["NAxis"] == "z":
                annoString += "slc.annotate_streamlines('velocity_x', 'velocity_y')\n"
        if Param_Dict["MagVectors"]:
            annoString += "slc.annotate_magnetic_field(normalize=True)\n"
        if Param_Dict["MagStreamlines"]:
            annoString += ('WARNING = "There is a yt-internal bug where streamline '
                          'annotation doesn\'t work if a center coordinate is '
                          'set to 0!"\n')
            if Param_Dict["NAxis"] == "x":
                annoString += "slc.annotate_streamlines('magy', 'magz')\n"
            elif Param_Dict["NAxis"] == "y":
                annoString += "slc.annotate_streamlines('magx', 'magz')\n"
            elif Param_Dict["NAxis"] == "z":
                annoString += "slc.annotate_streamlines('magx', 'magy')\n"
        if Param_Dict["Contour"]:
            annoString += "slc.annotate_contour('{}')\n".format(field)
    elif Param_Dict["Geometry"] == "cylindrical":
        if Param_Dict["Grid"]:
            annoString += "slc.annotate_grids()\n"
    if len(annoString) > 0:  # If annotations are made, declare them:
        annoString = "# Annotations for the plot:\n" + annoString + "\n\n"
    figureString = constructUsingMPL(Param_Dict, "slc")

    text = plotString + modString + annoString + figureString
    return text

def constructProjectionPlot(Param_Dict):
    """Constructs the projection plot script"""
    ds = Param_Dict["CurrentDataSet"]
    field = Param_Dict["ZAxis"]
    if Param_Dict["WeightField"] is None:
        weightField = "None"
    else:
        weightField = '"{}"'.format(Param_Dict["WeightField"])
    plotString = ""
    if Param_Dict["DomainDiv"]:
        # in case the user wants to divide everything by the domain_height,
        # we define a new field which is just the old field divided by height
        # and then do a projectionPlot for that.
        height = Param_Dict["FieldMaxs"]["DomainHeight"] - Param_Dict["FieldMins"]["DomainHeight"]
        plotString += (
f"""# We want to norm our projection by the domain height:
domainHeight = {height}  # You can obtain this by calculating
# ds.domain_right_edge - ds.domain_left_edge for all dimensions
# To do this, we can define a new field for the dataset:
def _NormField(field, data):
    return data["{field}"]/yt.units.YTQuantity(ds.arr(domainHeight, "code_length"))  # This way, yt will understand the units
""")
        field = "Normed " + field
        plotString += f'unit = yt.units.unit_object.Unit("{Param_Dict["ZUnit"]}/cm")'
        plotString += ("# Unfortunately add_field doesn't understand lambda functions.\n"
                       'ds.add_field(("gas", "{field}"), function=_NormField,\n'
                       "             units='auto', dimensions=unit.dimensions)\n\n\n")
    NVector, gridUnit = Param_Dict["NAxis"], Param_Dict["GridUnit"]
    c0, c1, c2 = Param_Dict["XCenter"], Param_Dict["YCenter"], Param_Dict["ZCenter"]
    width = f'(({Param_Dict["HorWidth"]}, "{gridUnit}"), ({Param_Dict["VerWidth"]}, "{gridUnit}"))'
    field = Param_Dict["ZAxis"]
    if Param_Dict["ParticlePlot"]:
        plotString += (
f'''
# Initialize a yt Particle Projection plot with the dataSet ds, Normal
# Vector {NVector}, field {field} and the optional parameters axes_unit,
# weight_field, center and fontsize:
c0 = yt.YTQuantity({c0}, "{gridUnit}")  # Get the center coordinates.
c1 = yt.YTQuantity({c1}, "{gridUnit}")  # Unfortunately, using a YTArray
c2 = yt.YTQuantity({c2}, "{gridUnit}")  # does not work properly.
proj = yt.ParticleProjectionPlot(ds, "{Param_Dict["NAxis"]}", "{field}",
                                 axes_unit="{gridUnit}", center=[c0, c1, c2],
                                 weight_field={weightField}, width={width},
                                 fontsize=14)
# Warning: Particle plots are still an experimental feature of the GUI and may
# produce errors
''')
    elif Param_Dict["NormVecMode"] == "Axis-Aligned":
        plotString += (
f'''
# Initialize a yt Projection plot with the dataSet ds, Normal Vector {NVector},
# field {field} and the optional parameters axes_unit, weight_field,
# center and fontsize:
c0 = yt.YTQuantity({c0}, "{gridUnit}")  # Get the center coordinates.
c1 = yt.YTQuantity({c1}, "{gridUnit}")  # Unfortunately, using a YTArray
c2 = yt.YTQuantity({c2}, "{gridUnit}")  # does not work properly.
proj = yt.ProjectionPlot(ds, "{Param_Dict["NAxis"]}", "{field}", axes_unit="{gridUnit}",
                         center=[c0, c1, c2], weight_field={weightField},
                         width={width}, fontsize=14)
''')
    else:
        normVec = [Param_Dict[axis + "NormDir"] for axis in ["X", "Y", "Z"]]
        northVec = [Param_Dict[axis + "NormNorth"] for axis in ["X", "Y", "Z"]]
        plotString += (
f'''
# Initialize a yt Projection plot with the dataSet ds, Normal Vector {NVector},
# field {field} and the optional parameters axes_unit, weight_field,
# center and fontsize:
c0 = yt.YTQuantity({c0}, "{gridUnit}")  # Get the center coordinates.
c1 = yt.YTQuantity({c1}, "{gridUnit}")  # Unfortunately, using a YTArray
c2 = yt.YTQuantity({c2}, "{gridUnit}")  # does not work properly.
proj = yt.OffAxisProjectionPlot(ds, {normVec}, "{field}", north_vector={northVec},
                                axes_unit="{gridUnit}", center=[c0, c1, c2],
                                weight_field={weightField}, width={width},
                                fontsize=14)
''')
    plotString += f'# Hint: You can access the generated data using proj.frb["{field}"]\n\n\n'
    modString = ""
    fieldMin = Param_Dict["ZMin"]  # Float
    fieldMax = Param_Dict["ZMax"]
    if field not in Param_Dict["FieldMins"].keys():
        modString = (
f'''# It seems that you have not calculated extrema for {field}.
# You can do this by using
# minMaxArray = ds.all_data().quantities.extrema({field})
# which will return a YTArray with two entries plus the units.
''')
    unit = Param_Dict["ZUnit"]
    cmap = Param_Dict["ColorScheme"]
    modString += ("# Set unit, minimum, maximum and color scheme:\n"
                 'proj.set_unit("{0}", "{1}")\nproj.set_zlim("{0}", {2}, {3})  '
                 '# These are given in the same unit, {1}.\n'
                 'proj.set_cmap("{0}", "{4}")\n'.format(field, unit, fieldMin, fieldMax, cmap))
    if fieldMin == "":
        log = Param_Dict["ZLog"]  # Boolean
        modString += "# Set our field scaling logarithmic if wanted:\n"
        if min(fieldMin, fieldMax) <= 0 and log:
            modString += ("proj.set_log('{0}', True, linthresh=(({1}-{2})/1000)  "
            "# linthresh sets a linear scale for a small portion and then a symbolic one "
            "for negative values\n".format(field, fieldMax, fieldMin))
        else:
            modString += f'proj.set_log("{field}", {log})  # This may be redundant in some cases.\n'
    zoom = Param_Dict["Zoom"]
    modString += f"proj.zoom({zoom})\n\n"
    # Do the annotations:
    annoString = ""
    title = Param_Dict["PlotTitle"]
    if title != "":
        annoString += f'proj.annotate_title("{title}")  # Give the plot the title it deserves.\n'
    if Param_Dict["Timestamp"]:
        annoString += ("proj.annotate_timestamp(corner='upper_left', draw_inset_box=Tr"
        "ue)  # There are many more modifications for the timestamp.\n")
    if Param_Dict["Geometry"] == "cartesian":
        if Param_Dict["Scale"]:
            annoString += "proj.annotate_scale(corner='upper_right')\n"
        if Param_Dict["Grid"]:
            annoString += ('WARNING = "There is a yt-internal bug where grid '
                          'annotation doesn\'t work if a center coordinate is '
                          'set to 0!"\n')
            annoString += "proj.annotate_grids()\n"
        if Param_Dict["ParticleAnno"]:
            if Param_Dict["PSlabWidth"] == "" or float(Param_Dict["PSlabWidth"]) == 0:
                Param_Dict["PSlabWidth"] = 1
            height = abs(Param_Dict["FieldMins"]["DomainHeight"] - Param_Dict["FieldMaxs"]["DomainHeight"])
            width = float(Param_Dict["PSlabWidth"])*height
            annoString += (f"slc.annotate_particles({width})\n")
        if Param_Dict["VelVectors"]:
            annoString += "proj.annotate_velocity(normalize=True)\n"
        if Param_Dict["VelStreamlines"]:
            annoString += ('WARNING = "There is a yt-internal bug where streamline '
                          'annotation doesn\'t work if a center coordinate is '
                          'set to 0!"\n')
            if Param_Dict["NAxis"] == "x":
                annoString += "proj.annotate_streamlines('velocity_y', 'velocity_z')\n"
            elif Param_Dict["NAxis"] == "y":
                annoString += "proj.annotate_streamlines('velocity_x', 'velocity_z')\n"
            elif Param_Dict["NAxis"] == "z":
                annoString += "proj.annotate_streamlines('velocity_x', 'velocity_y')\n"
        if Param_Dict["MagVectors"]:
            annoString += "proj.annotate_magnetic_field(normalize=True)\n"
        if Param_Dict["MagStreamlines"]:
            annoString += ('WARNING = "There is a yt-internal bug where streamline '
                          'annotation doesn\'t work if a center coordinate is '
                          'set to 0!"\n')
            if Param_Dict["NAxis"] == "x":
                annoString += "proj.annotate_streamlines('magy', 'magz')\n"
            elif Param_Dict["NAxis"] == "y":
                annoString += "proj.annotate_streamlines('magx', 'magz')\n"
            elif Param_Dict["NAxis"] == "z":
                annoString += "proj.annotate_streamlines('magx', 'magy')\n"
        if Param_Dict["Contour"]:
            annoString += "proj.annotate_contour('{}')\n".format(field)
    elif Param_Dict["Geometry"] == "cylindrical":
        if Param_Dict["Grid"]:
            annoString += "proj.annotate_grids()\n"
    annoString += "\n"
    figureString = constructUsingMPL(Param_Dict, "proj")

    text = plotString + modString + annoString + figureString
    return text

# %% Functions for constructing the line and phase plot strings
def constructLinePlot(Param_Dict):
    """Constructs the line plot script"""
    ds = Param_Dict["CurrentDataSet"]
    startends = ["XLStart", "YLStart", "ZLStart", "XLEnd", "YLEnd", "ZLEnd"]
    valueList = []
    for key in startends:
        value = float(yt.units.YTQuantity(Param_Dict[key], Param_Dict["oldGridUnit"]).to(ds.quan(1, 'code_length').units).value)
        valueList.append(value)
    field = Param_Dict["YAxis"]
    plotString = (
f"""# Initialize a yt Line plot with the dataSet ds, field {field}, the
# given start- and end points in code_length and the number of sampling points:
lplot = yt.LinePlot(ds, "{field}", {valueList[:3]}, {valueList[3:]},
                    npoints=512, fontsize=14)
""")
    plotString += ("# Note that you can also add more than one field for the "
                   "line plot and that you\n# can label them independently "
                   'using field_labels={"field":label}.\n\n')
    modString = (f'lplot.annotate_legend("{field}")  # Optional, but looks nice\n')
    fieldMin = Param_Dict["YMin"]  # Float
    fieldMax = Param_Dict["YMax"]
    unit = Param_Dict["ZUnit"]
    modString += f'lplot.set_x_unit("{Param_Dict["LineUnit"]}")\n'
    modString += f'lplot.set_unit("{field}", "{unit}")\n'
    modString += ("# Unfortunately, yt line-plots don't have built in min and "
                  "max settings, so we use pyplot later.\n")
    log = Param_Dict["ZLog"]  # Boolean
    modString += "# Set our field scaling logarithmic if wanted:\n"
    if min(fieldMin, fieldMax) <= 0 and log:
        modString += (f'lplot.set_log("{field}", True, linthresh=(({fieldMax}-{fieldMin})/1000)  '
        "# linthresh sets a linear scale for a small portion and then a symbolic one "
        "for negative values\n")
    else:
        modString += (f'lplot.set_log("{field}", {log})  # This may be redundant '
                      "in some cases.\n")
    annoString = ""
    title = Param_Dict["PlotTitle"]
    if title != "":
        annoString += (f'lplot.annotate_title("{Param_Dict["YAxis"]}", "{title}")'
                       "#  Give the plot the title it deserves.\n")
    figureString = constructUsingMPL(Param_Dict, "lplot")

    text = plotString + modString + annoString + figureString
    return text

def constructPhasePlot(Param_Dict):
    """Constructs the phase plot script"""
    XField, YField, ZField = Param_Dict["XAxis"], Param_Dict["YAxis"], Param_Dict["ZAxis"]
    plotString = ("ad = ds.all_data()  # through e.g. ad = ds.sphere('c', (50, 'kpc"
                  "')) you could also only select a region of the dataset.\n\n")
    if Param_Dict["WeightField"] is None:
        weightField = "None"
    else:
        weightField = f'"{Param_Dict["WeightField"]}"'
    plotString += (
f"""# Initialize a yt phase plot with the data ad, XField {XField},
# YField {YField}, ZField {ZField} and the optional parameters
# weight_field, fractional, the number of bins and fontsize:
""")
    if Param_Dict["ParticlePlot"]:
        plotString += (
f'''phas = yt.ParticlePhasePlot(ad, "{XField}", "{YField}", "{ZField}",
                            weight_field={weightField}, x_bins=128, y_bins=128,
                            fontsize=14)
# Warning: Particle plots are still an experimental feature of the GUI and may
# produce errors

''')
    else:
        plotString += (
f'''phas = yt.PhasePlot(ad, "{XField}", "{YField}", "{ZField}",
                    weight_field={weightField}, fontsize=14)

''')
    cmap = Param_Dict["ColorScheme"]
    modString = ("# Set our field scaling logarithmic if wanted. "
                 "Phase plots don't support symlog scales.\n")
    for axis in ["X", "Y", "Z"]:
        log = Param_Dict[axis + "Log"]  # Boolean
        field = Param_Dict[axis + "Axis"]
        modString += f'phas.set_log("{field}", {log})  # This may be redundant in some cases.\n'
    modString += "# Set unit, minimum, maximum and color scheme:\n"
    for axis in ["X", "Y", "Z"]:
        modString += (f'phas.set_unit("{Param_Dict[axis +"Axis"]}", "{Param_Dict[axis + "Unit"]}")\n')
    if XField not in Param_Dict["FieldMins"].keys():
        modString += (
f'''# It seems that you have not calculated extrema for {XField}.
# You can do this by using
# minMaxArray = ds.all_data().quantities.extrema({XField})
# which will return a YTArray with two entries plus the units.
''')
    if YField not in Param_Dict["FieldMins"].keys():
        modString += (
f'''# It seems that you have not calculated extrema for {YField}.
# You can do this by using
# minMaxArray = ds.all_data().quantities.extrema({YField})
# which will return a YTArray with two entries plus the units.
''')
    if ZField not in Param_Dict["FieldMins"].keys():
        modString += (
f'''# It seems that you have not calculated extrema for {ZField}.
# You can do this by using
# minMaxArray = ds.all_data().quantities.extrema({ZField})
# which will return a YTArray with two entries plus the units.
''')
    XMin = Param_Dict["XMin"]
    XMax = Param_Dict["XMax"]
    YMin = Param_Dict["YMin"]
    YMax = Param_Dict["YMax"]
    ZMin = Param_Dict["ZMin"]
    ZMax = Param_Dict["ZMax"]
    modString += (f'phas.set_xlim({XMin}, {XMax})\n'
                  f'phas.set_ylim({YMin}, {YMax})\n'
                  f'phas.set_zlim("{ZField}", {ZMin}, {ZMax})  # These are given in the same unit, {Param_Dict["ZUnit"]}.\n')
    modString += f'phas.set_cmap("{ZField}", "{cmap}")\n'
    annoString = ""
    title = Param_Dict["PlotTitle"]
    if title != "":
        annoString += (f'phas.annotate_title("{Param_Dict["YAxis"]}", "{title}")'
                       "#  Give the plot the title it deserves.\n")
    figureString = constructUsingMPL(Param_Dict, "phas")

    text = plotString + modString + annoString + figureString
    return text

# %% Function for constructing the profile plot strings
# (All variants including time series)
def constructProfilePlot(Param_Dict, time=False, multiPlot=False):
    """Constructs the Profile plot script.
    Params:
        time: bool to indicate whether 'time' has been used for the x-axis
        multiPlot: bool to indicate whether the user wants to do plots at
                   multiple times
    """
    if Param_Dict["WeightField"] is None:
        weightField = "None"
    else:
        weightField = "'{}'".format(Param_Dict["WeightField"])
    ds = Param_Dict["CurrentDataSet"]
    if time:
        if weightField == "None":
            weightField = "'ones'"
        calcQuanName = getCalcQuanName(Param_Dict)
        calcQuanString = getCalcQuanString(Param_Dict).replace("'", '"')
        dataString = (
f'''# Loop over the datasets of the series to get the datapoints at each time:
# This does NOT reflect the extrema you chose in the GUI, please
# add them by hand by only using ts[start:(end+1)]
i = 0
time_data = []
y_data = []
length = len(ts)
for ds in ts:
    time_data.append(ds.current_time.to_value("{Param_Dict["XUnit"]}"))
    ad = ds.all_data()  # You could select an arbitrary yt region here
    # for available quantities to calculate see
    # https://yt-project.org/doc/analyzing/objects.html as well.
    yResult = {calcQuanString}
    y_data.append(yResult)
    i += 1
    print(f"Progress: {{i}}/{{length}} data points calculated.")
arr_x = yt.YTArray(time_data, "{Param_Dict["XUnit"]}")  # In a YTArray, the units
arr_y = yt.YTArray(y_data, "{Param_Dict["YUnit"]}")  # are stored as well.
"""
Note: Another way to do this is loading the series through yt using
ts = yt.load("{Param_Dict["Directory"]}/{Param_Dict["Seriesname"]}")
and then using the built-in parallel iteration tool, piter, like this:
yt.enable_parallelism(suppress_logging=True)
storage = {{}}  # A storage dictionary to store the data during parallel iteration
for store, ds in ts.piter(storage=storage):
    ad = ds.all_data()
    yResult = {calcQuanString}
    store.result = yResult
    i += 1
    print(f"Progress: {{i}}/{{length}} data points calculated."
y_arr = yt.YTArray(list(storage.values()), "{Param_Dict["YUnit"]}")
"""
''')
        # If possible, pass the values that have already been calculated to the user
        times, values = [], []
        field = Param_Dict["YAxis"]
        for ds in Param_Dict["DataSeries"]:
            try:
                time = Param_Dict["DataSetDict"][str(ds) + "Time"].to_value(Param_Dict["XUnit"])
                value = Param_Dict["DataSetDict"][str(ds) + field + calcQuanName]
                value = yt.YTQuantity(value, Param_Dict["FieldUnits"][field]).to_value(Param_Dict["YUnit"])
                times.append(time)
                values.append(value)
            except KeyError:
                pass
        if len(times) > 0:
            dataString += (
f'''# It seems that you have already calculated some plot points using the GUI.
# I have stored them for you, and you can reuse them if wanted:
calcTimes = yt.YTArray({times}, "{Param_Dict["XUnit"]}")
calcValues = yt.YTArray({values}, "{Param_Dict["YUnit"]}")

''')
    elif multiPlot:
        onlyEvery = Param_Dict["ProfOfEvery"]
        suf = lambda n: "%d%s "%(n,{1:"st",2:"nd",3:"rd"}.get(n if n<20 else n%10,"th"))
        numString = suf(onlyEvery)
        dataString = (
f'''# Loop over the datasets of the series to make a profile at each time:
i = 0
onlyEvery = {onlyEvery}  # If you only want to plot every {numString} plot
length = {math.ceil(len(Param_Dict["DataSeries"])/onlyEvery)}  # You can use math.ceil(len(ts)/onlyEvery) to calculate this.
labels = []
profiles = []  # The data for the y-axis will be stored in YTArrays
for ds in ts:
    if i % onlyEvery == 0:
        # Create a data container to hold the whole dataset.
        ad = ds.all_data()  # you can use an arbitrary yt region here.
        # Create a 1d profile of xfield vs. yfield. Through n_bins the number
        # of bins may be modified as well:
        prof = yt.create_profile(ad, "{Param_Dict["XAxis"]}",
                                 fields=["{Param_Dict["YAxis"]}"],
                                 weight_field={weightField}, n_bins=64)
        # Add labels
        time = ds.current_time.to_value("kyr")
        label = f"{Param_Dict["YAxis"]} at {{time:.2e}} kyr"
        labels.append(label)
        profiles.append(prof["{Param_Dict["YAxis"]}"])
        print(f"Progress: {{int(i/onlyEvery)+1}}/{{length}} profiles done.")
    i += 1
arr_x = prof.x  # get the data for the x-axis
"""
Note: Another way to do this is loading the series through yt using
ts = yt.load("{Param_Dict["Directory"]}/{Param_Dict["Seriesname"]}")
and then using the built-in parallel iteration tool, piter, like this:
yt.enable_parallelism(suppress_logging=True)
storage = {{}}  # A storage dictionary to store the data during parallel iteration
for store, ds in ts.piter(storage=storage):
    if i % onlyEvery == 0:
        ad = ds.all_data()
        prof = yt.create_profile(ad, "{Param_Dict["XAxis"]}",
                                 fields=["{Param_Dict["YAxis"]}"],
                                 weight_field={weightField}, n_bins=64)
        # Add labels
        time = ds.current_time.to_value("kyr")
        label = f"{Param_Dict["YAxis"]} at {{time:.2e}} kyr"
        labels.append(label)
        store.result = prof["{Param_Dict["YAxis"]}"]
        print("Progress: {{int(i/onlyEvery+1}}/{{length}} profiles done."
    i += 1
arr_y = list(storage.values())
"""
''')
    else:
        dataString = (
f'''# First reate a data container to hold the whole dataset.
ad = ds.all_data()  # you can use an arbitrary yt region here.
# Create a 1d profile of the x-field vs. the y-field. You could also pass
# multiple fields for the vertical axis.
prof = yt.create_profile(ad, "{Param_Dict["XAxis"]}",
                         fields=["{Param_Dict["YAxis"]}"],
                         weight_field={weightField})
label = "{Param_Dict["YAxis"]}"
arr_x = prof.x
arr_y = prof["{Param_Dict["YAxis"]}"]

''')
    figureString = (
f'''# Now that we have created the data, we can set up a figure and plot it.
# More information: https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.pyplot.figure.html
fig, axes = plt.subplots(**{{'figsize': (10, 7), 'dpi': 100}})

# Add the plot:
x_values = arr_x.to_value("{Param_Dict["XUnit"]}")
''')
    if multiPlot:
        plotString = (
f'''for i, arr_y in enumerate(profiles):
    label = labels[i]
    y_values = arr_y.to_value("{Param_Dict["YUnit"]}")
    axes.plot(x_values, y_values, "-", linewidth=3, label=label)
''')
    else:
        plotString = (f'y_values = arr_y.to_value("{Param_Dict["YUnit"]}")\n'
                      f'axes.plot(x_values, y_values, "-", linewidth=3, label="{Param_Dict["YAxis"]}")\n')
    field = Param_Dict['XAxis']
    xUnit = yt.YTQuantity(1, Param_Dict["XUnit"]).units.latex_repr  # get latex repr for unit
    if xUnit != "":  # do not add empty brackets
        xUnit = r"$\:\left[" + xUnit + r"\right]$"
    if field == "dens" or field == "temp":
        xName = eval(f"ds.fields.flash.{field}.get_latex_display_name()")
    elif field == "time":
        xName = r"$\rm{Time}$"
    else:
        xName = eval(f"ds.fields.gas.{field}.get_latex_display_name()")
    field = Param_Dict["YAxis"]
    yUnit = yt.YTQuantity(1, Param_Dict["YUnit"]).units.latex_repr  # get latex repr for unit
    if yUnit != "":  # do not add empty brackets
        yUnit = r"$\:\left[" + yUnit + r"\right]$"
    if field == "dens" or field == "temp":
        yName = eval(f"ds.fields.flash.{field}.get_latex_display_name()")
    elif field == "time":
        yName = r"$\rm{Time}$"
    else:
        yName = eval(f"ds.fields.gas.{field}.get_latex_display_name()")
    if time:
        plotString += (
f'''
"""
Note:
Since you might not want to recalculate all the data, you can save the values
in a csv-file the following way:
import csv
with open("time_profile_{calcQuanName}_{field}_data.csv", "w") as writefile:
    # configure writer to write standard csv file
    writer = csv.writer(writefile, delimiter=',', lineterminator='\\n')
    writer.writerow(["time", "{field}"])
    writer.writerow(["{Param_Dict["XUnit"]}", "{Param_Dict["YUnit"]}"])
    for time, y in zip(time_data, y_data):
        writer.writerow([time, y])
    print("The data points have been saved to\\n"
          "time_profile_{calcQuanName}_{field}_data.csv.")
"""

''')
    elif multiPlot:
        pass
    else:
        plotString +=(
f'''
"""
Note:
Since you might not want to recalculate all the data, you can save the values
in a csv-file the following way:
import csv
with open("profile_{Param_Dict["XAxis"]}_{Param_Dict["YAxis"]}_data.csv", "w") as writefile:
    # configure writer to write standard csv file
    writer = csv.writer(writefile, delimiter=',', lineterminator='\\n')
    writer.writerow(["{Param_Dict["XAxis"]}", "{Param_Dict["YAxis"]}"])
    writer.writerow(["{Param_Dict["XUnit"]}", "{Param_Dict["YUnit"]}"])
    for time, y in zip(time_data, y_data):
        writer.writerow([time, y])
    print("The data points have been saved to\\n"
          "profile_{Param_Dict["XAxis"]}_{Param_Dict["YAxis"]}_data.csv.")
"""

''')
    plotString += (
'''
"""
Note:
We could also use yt's built-in yt.ProfilePlot.from_profiles(profiles, labels),
but we're using matplotlib.pyplot since it offers more control.
"""
# Modify the settings for x- and y-axis:
# with axes.set_xscale you can set it log(arithmic) or lin(ear).
''')
    if Param_Dict["XLog"]:
        plotString += 'axes.set_xscale("log")\n'
    if Param_Dict["YLog"]:
        plotString += 'axes.set_yscale("log")\n'
    plotString += (
f'''# We can freely customize the axes' names. You can get a units' LaTeX rep
# like this: yt.YTQuantity(1, "yourUnit").units.latex_repr
# and the display name with ds.fields.gas.YOURFIELD.get_latex_display_name()
axes.set_xlabel(r"{xName + xUnit}")
axes.set_ylabel(r"{yName + yUnit}")
axes.set_xlim({Param_Dict["XMin"]}, {Param_Dict["XMax"]})  # be careful about the units here
axes.set_ylim({Param_Dict["YMin"]}, {Param_Dict["YMax"]})
axes.legend()  # Display the labels we have given
axes.grid()  # You can customize the grid even further if wanted
axes.set_title(r"{Param_Dict["PlotTitle"]}")
''')
    if Param_Dict["AddProfile"]:
        plotString += (
'''
"WARNING: The second profile functionality is not a feature yet."
"""
Note: I have not implemented adding a second plot to this script writer yet.
It's actually pretty simple. Just use
twinaxes = axes.twinx()
twinaxes.tick_params(axis='y')
axes.tick_params(axis='y')
and plot the desired data on the twinaxes, which is going to use the same
x-axis.
"""
''')
    if Param_Dict["Timestamp"] and Param_Dict["XAxis"] != "time" and not multiPlot:
        plotString += ("# Use a custom function to annotate the timestamp (see above):\n"
                       "drawTimestampBox(axes, ds)\n")
    plotString += "# fig.show()  # Works best in iPython console or jupyter\n\n\n"
    saveString = '# example of how you could name the file:\n'
    saveString += f'plotfilename = "{str(Param_Dict["CurrentDataSet"])}_'
    if multiPlot:
        saveString += 'Multi'
    saveString += f'{Param_Dict["PlotMode"]}plot_{Param_Dict["YAxis"]}.png"\n'
    saveString += "fig.savefig(plotfilename)  # Takes the name of the file as an argument.\n"
    saveString += 'print(f"The file has been saved as {plotfilename}")\n'
    text = dataString + figureString + plotString + saveString
    if Param_Dict["Timestamp"] and Param_Dict["XAxis"] != "time" and not multiPlot:
        text = createCustomTimestampString() + text
    return text

# %% Function for constructing the plot-making string
def constructUsingMPL(Param_Dict, plotName):
    """Construct the part of the script where the yt plot is plotted as a mpl
    figure.
    params:
        Param_Dict: for retrieving information about what to plot
        plotName: the name the plot has been given, e.g. 'slc' for slice
    returns:
        text: constructed script text
    """
    mode = Param_Dict["PlotMode"]
    plotString = ("# Everything that's necessary for setting up the figure.\n"
                  "# More information: https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.pyplot.figure.html\n"
                  'fig, axes = plt.subplots(**{"figsize": (10, 7), "dpi": 100})\n')
    if Param_Dict["DimMode"] == "2D":
        plotString += ("# Since we're using the color axis, we need the following:\n"
                       "divider = make_axes_locatable(axes)\n"
                       'cax = divider.append_axes("right", size="5%", pad=0.05)\n')
    plotString += ("\n\n# Now we need to pass our axes to yt so it can setup the"
                   " plots for us.\n")
    if mode == "Line":
        field = Param_Dict["YAxis"]
        plotString += f'{plotName}.plots["{field}"].axes = axes\n'
    else:
        field = Param_Dict["ZAxis"]
        if Param_Dict["DomainDiv"]:
            field = "Normed " + field
        plotString += f'{plotName}.plots["{field}"].axes = axes\n'
    if Param_Dict["DimMode"] == "2D":
        plotString += f'{plotName}.plots["{field}"].cax = cax\n'
    plotString += ("{0}._setup_plots()  # This runs the yt-internal command for"
                   " plotting. It's different for each plot type.\n".format(plotName))
    if mode == "Line":
        plotString += "axes.set_ylim({0}, {1})\n".format(Param_Dict["YMin"], Param_Dict["YMax"])
        if Param_Dict["LineAnno"]:
            plotString += "annotateStartEnd(axes, ds)  # annotate custom start and end points\n"
    if Param_Dict["Timestamp"] and mode in ["Line", "Phase"]:
        plotString += ("# Use a custom function to annotate the timestamp (see below):\n"
                       "drawTimestampBox(axes, ds)\n")
    if mode in ["Slice", "Projection"]:
        if Param_Dict["SetAspect"]:
            plotString += '# Set the aspect ratio. If "1", equal distances will be equally long.\n'
            plotString += 'axes.set_aspect("auto")  # For "auto", the figure is filled.\n'
    plotString += "# fig.show()  # Works best in iPython console or jupyter\n\n"
    saveString = ('\nplotfilename = "{0}_{1}plot_{2}.png"  # example of how you could name the file\n'
                 .format(str(Param_Dict["CurrentDataSet"]), Param_Dict["PlotMode"], field))
    saveString += "fig.savefig(plotfilename)  # Takes the name of the file as an argument.\n"
    saveString += 'print("The file has been saved as {0}".format(plotfilename))\n'
    text = plotString + saveString
    if Param_Dict["Timestamp"] and mode in ["Line", "Phase"]:
        text = createCustomTimestampString() + text
    if Param_Dict["LineAnno"] and mode == "Line":
        text = createLinePointAnnoString(Param_Dict) + text
    return text

# %% Functions to construct time series strings
def checkTimeSeriesPlot(Param_Dict, plotAll):
    """Helper function to check whether the user really used the time series
    attribute."""
    # These are the only occurances where the time series is actually used
    if (Param_Dict["XAxis"] == "time" or Param_Dict["TimeSeriesProf"] or
        plotAll):
        return True
    else:
        return False

def constructSeriesString(Param_Dict, plotAll):
    """Constructs the string when handling a time series"""
    loadingString = ("# Load the series through yt and save it as ts:\n"
                     f'ts = yt.load("{Param_Dict["Directory"]}/{Param_Dict["Seriesname"]}")\n\n\n')
    if Param_Dict["XAxis"] == "time":
        plotString = constructProfilePlot(Param_Dict, time=True)
    elif Param_Dict["TimeSeriesProf"]:
        plotString = constructProfilePlot(Param_Dict, multiPlot=True)
    elif plotAll:
        plotString = constructTimeSeriesPlots(Param_Dict)
    text = loadingString + plotString
    return text

def constructTimeSeriesPlots(Param_Dict):
    """Constructs a string to reproduce the loop used to plot pictures of a
    whole series."""
    plotMode = Param_Dict["PlotMode"]
    onlyEvery = Param_Dict["OnlyEvery"]
    loopString = "\n# We are now performing a loop over each file of the dataset:\n"
    loopString += f"directory = '{Param_Dict['Directory']}/{plotMode}plot_{datetime.now().strftime('%d_%m_%Y_%H_%M_%S')}'\n"
    loopString += "os.mkdir(directory)  # Create a directory to save the frames in\n"
    loopString += f"onlyEvery = {onlyEvery}"
    loopString += "i = 0  # for convenient progress updates\n"
    loopString += "for ds in ts:\n"
    suf = lambda n: "%d%s "%(n,{1:"st",2:"nd",3:"rd"}.get(n if n<20 else n%10,"th"))
    numString = suf(onlyEvery)
    length = len(Param_Dict["DataSeries"])
    loopString += (
f"""    if i % onlyEvery == 0:  # if you only want every {numString} file
        make{plotMode}Plot(ds)
        print(f"Progress: {{(i/{onlyEvery}+1)}}/{math.ceil(length/onlyEvery)} {plotMode}plots done.")
        saveName = f"{{directory}}/{plotMode}plot_{{i+1}}"
        plt.savefig(saveName)
    i += 1
""")
    plotString = "# Here we define the actual plot function that is performed for each frame:\n"
    plotString += f"def make{plotMode}Plot(ds):\n"
    plotString += (
f'''    """A function to produce a {plotMode}plot of the dataset ds using the
    desired parameters.
    Parameters:
        ds: (FLASH)-Dataset loaded using yt
    """
''')
    funcString = eval("construct"+Param_Dict["PlotMode"]+"Plot(Param_Dict)")
    # We need to insert the four spaces because we use this inside of a function
    funcString = funcString.split("\n")
    for line in funcString:
        if line != "":
            plotString += f"    {line}\n"
        else:
            plotString += "\n"
    text = plotString + loopString  # this way the function is defined before it is called
    return text

# %% Functions to construct miscellaneous strings like derived fields, file
# loading and annotations (alphabetical order)
def constructDerFieldString(Param_Dict):
    """Construct a string that reflects the implementation of used custom
    derived fields.
    Returns:
        text: String that is empty in case no custom fields are used
    """
    foundFields = []
    for axis in ["X", "Y", "Z"]:
        fieldName = Param_Dict[axis + "Axis"]
        if (fieldName in Param_Dict["NewDerFieldDict"].keys() and
            fieldName not in foundFields):
            foundFields.append(fieldName)
    text = ""
    for i, fieldName in enumerate(foundFields):
        if i == 0:
            text += "# Adding the custom derived fields that are used:\n"
        displayName = Param_Dict["NewDerFieldDict"][fieldName]["DisplayName"]
        func = Param_Dict["NewDerFieldDict"][fieldName]["FunctionText"]
        unit = Param_Dict["NewDerFieldDict"][fieldName]["Unit"]
        dim = Param_Dict["NewDerFieldDict"][fieldName]["Dimensions"]
        override = Param_Dict["NewDerFieldDict"][fieldName]["Override"]
        if "np." in func:
            text += "import numpy as np\n"
        text += (
f'''# First, define the function that is used for the field:
{func}

# Now we can just add this new field to yt. Another way would be loading the
# dataset(s) first and then add it using ds.add_field(...)
yt.add_field(("gas", "{fieldName}"), function=_{fieldName},
             units="{unit}", dimensions="{dim}", force_override={override},
             display_name=r"{displayName}", take_log=False)

''')
    return text

def createLinePointAnnoString(Param_Dict):
    """Creates a string that can be used to reproduce the lineplot annotation"""
    sNumbers = [Param_Dict[axis + "LStart"] for axis in ["X", "Y", "Z"]]
    eNumbers = [Param_Dict[axis + "LEnd"] for axis in ["X", "Y", "Z"]]
    pointString = (
f'''

# The following functions are used for custom annotation of the start and end
# points used for line plotting
def createLineText(numbers, mode, ds):
    """Creates and returns the text for the line edit.
    Parameters:
        numbers: list of coordinates
        mode: 'Start' or 'End'
        ds: dataset object
    """
    # It is advisable to turn them into a easy-to-read numbers. This is of
    # course optional.
    numbers = yt.YTArray(numbers, "{Param_Dict["LineUnit"]}")
    lengthUnit = ds.get_smallest_appropriate_unit(max(numbers),
                                                  quantity="distance")
    lengths = numbers.to_value(lengthUnit)
    return f"{{mode}}: ({{lengths[0]:.1f}}, {{lengths[1]:.1f}}, {{lengths[2]:.1f}}) {{lengthUnit}}"

def annotateStartEnd(axes, ds):
    """Annotates two boxes for start and end coordinates.
    Parameters:
        axes: mpl axes object for plotting
        ds: yt dataset object for receiving nice units"""
    bboxArgs = {{'boxstyle': 'square,pad=0.3', 'facecolor': 'white',
                'linewidth': 2, 'edgecolor': 'black', 'alpha': 0.5}}
    startText = createLineText({sNumbers}, "Start", ds)
    endText = createLineText({eNumbers}, "End", ds)
    axes.text(x=0.0, y=-0.14, s=startText, size=10, bbox=bboxArgs,
              ha="left", va="bottom", transform=axes.transAxes,
              **{{"color": "black"}})
    axes.text(1.0, -0.14, endText, size=10, bbox=bboxArgs, ha="right",
              va="bottom", transform=axes.transAxes, **{{"color": "black"}})
    print("Annotating line points. Sometimes the positions x and y have "
          "to be adjusted.")
''')
    return pointString

def createCustomTimestampString():
    """Write a string that can be to reproduce the custom timestamp annotation.
    The function call itself has to be added above."""
    stampString = (
'''

# The following two functions are used for a custom timestamp annotation
# because they are not supported for line, phase and profile plots by yt:
def createTimestampText(ds):
    """Creates the text for a timestamp annotation.
    Parameters:
        ds: yt dataset object: To extract the time information from
    """
    timeUnit = ds.get_smallest_appropriate_unit(ds.current_time,
                                                quantity="time")
    time = ds.current_time.to_value(timeUnit)
    return f"t = {time:.1f} {timeUnit}"  # give it a nice look.

def drawTimestampBox(axes, ds):
    """Draw a custom timestamp annotation box like the one
    used by yt on a given axes axes using the dataset ds for information.
    Parameters:
        axes: mpl ax object: The ax to draw the timestamp on
        ds: yt dataset object: To extract the time information from
    """
    bboxArgs = {'boxstyle': 'square,pad=0.3', 'facecolor': 'black',
                'linewidth': 3, 'edgecolor': 'white', 'alpha': 0.5}
    text = createTimestampText(ds)
    axes.text(x=0.03, y=0.96, s=text, size=15, bbox=bboxArgs,
              ha="left", va="top", transform=axes.transAxes, **{"color": "w"})
    # if you leave out the "transform"-keyword, xpos and ypos will be
    # interpreted as data positions.

''')
    return stampString


#WAP to find the Max and Min number using simple if condition.

num1 = int(input("Enter 1 No. :"))
num2 = int(input("Enter 2 No. :"))
num3 = int(input("Enter 3 No. :"))
num4 = int(input("Enter 4 No. :"))
num5 = int(input("Enter 5 No. :"))

if(num1>num2) and (num1>num3) and (num1>num4) and (num1>num5):
    largest = num1
elif (num2>num1) and (num2>num3) and (num2>num4) and (num2>num5):
    largest = num2
elif (num3>num1) and (num3>num2) and (num3>num3) and (num3>num5):
    largest = num3
elif (num4>num1) and (num4>num2) and (num4>num3) and (num4>num5):
    largest = num4
else:
    largest = num5

print("\nMax no. is : ", largest)

if(num1<num2) and (num1<num3) and (num1<num4) and (num1<num5):
    smallest = num1
elif (num2<num1) and (num2<num3) and (num2<num4) and (num2<num5):
    smallest = num2
elif (num3<num1) and (num3<num2) and (num3<num3) and (num3<num5):
    smallest = num3
elif (num4<num1) and (num4<num2) and (num4<num3) and (num4<num5):
    smallest = num4
else:
    smallest = num5

print("\nMin no. is : ", smallest)


import warnings
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")

import argparse
from argparse import ArgumentParser
from Bio import SeqIO
import numpy as np
import os
import pandas as pd
import pickle
import multiprocessing

NR_THREADS = 24
SIGNAL_EVALUATION_RADIUS = 500
SEQUENCE_EVALUATION_RADIUS = 500
SEQTK_PATH = '/Users/igtr4848/seqtk/seqtk'
SEQ_DUMP_DIR = '/scratch/Shares/dowell/itripodi/ATAC/peak_sequences'
OUTDIR = '/scratch/Shares/dowell/itripodi/ATAC/features'

parser = argparse.ArgumentParser(description='This script gathers all features that can be used to classify ATAC-seq peaks')
parser.add_argument('-x', '--prefix', dest='prefix', help='Custom prefix for all output files')
parser.add_argument('-p', '--atac-peaks', dest='atac_peaks', help='Path to the ATAC-seq peaks file')
parser.add_argument('-b', '--atac-bedgraph', dest='atac_bedgraph', help='Path to the ATAC-seq bedGraph file for the same sample of the given peaks file.')
parser.add_argument('-f', '--genome-fasta', dest='genome_fasta', help='Path to the FASTA file for the reference genome in use.')
parser.add_argument('-r', '--refseq-annotations', dest='refseq_annotations', help='Path to the RefSeq annotation file for the same reference genome these files were processed.')
parser.add_argument('-s', '--skip-seq-caching', dest='skip_sequence_caching', help='Skip the caching of peak sequences if this has already been processed.', required=False, action='store_true')
args = parser.parse_args()  # /scratch/Shares/dowell/nascent/hg38/hg38_refseq.bed

atac_peak_df = pd.read_csv(args.atac_peaks, header=None, \
                           sep="\t", na_filter=False, \
                           usecols=[0, 1, 2, 10], \
                           names=['chrom', 'start', 'end', 'ovlp_txn'], \
                           dtype={'chrom':'str', 'start':'int', \
                                  'end':'int', 'ovlp_txn':'int'} )

atac_bed_df = pd.read_csv(args.atac_bedgraph, header=None, \
                           sep="\t", na_filter=False, \
                           usecols=[0, 1, 2, 3], \
                           names=['chrom', 'start', 'end', 'reads'], \
                           dtype={'chrom':'str', 'start':'int', \
                                  'end':'int', 'reads':'float'} )

refseq_df = pd.read_csv(args.refseq_annotations, header=None, \
                           sep="\t", na_filter=False, \
                           usecols=[0, 1, 2, 5], \
                           names=['chrom', 'start', 'end', 'strand'], \
                           dtype={'chrom':'str', 'start':'int', \
                                  'end':'int', 'strand':'str'} )

def get_atac_features(current_chrom):
    print("Processing peaks from chromosome %s" % current_chrom)
    chrom_features = []
    last_peak_end = 0
    ovlp_iter = atac_peak_df[(atac_peak_df.chrom == current_chrom)].itertuples()

    for peak in ovlp_iter:
        signal_features = np.zeros(2 * SIGNAL_EVALUATION_RADIUS)
        peak_midpoint = peak.start + (peak.end - peak.start)/2
        region_start = peak_midpoint - SIGNAL_EVALUATION_RADIUS
        region_end = peak_midpoint + SIGNAL_EVALUATION_RADIUS
        peak_window_reads = np.zeros(peak.end - peak.start)

        # Get the number of reads for every nucleotide +/- the SIGNAL_EVALUATION_RADIUS
        #wide_signal_iter = atac_bed_df[(atac_bed_df['chrom'] == current_chrom) & (atac_bed_df['start'] >= region_start) & (atac_bed_df['end'] <= region_end)].itertuples()
        wide_signal_iter = atac_bed_df.query('chrom == "%s" and start >= %d and end <= %d' % (current_chrom, region_start, region_end)).itertuples()
        for coverage in wide_signal_iter:
            feat_start = coverage.start - region_start + 1
            feat_end = coverage.end - region_start
            for position in range(int(feat_start), int(feat_end)):
                signal_features[position] = coverage.reads

        # Get the mean number of reads for every nucleotide within the peak boundaries
        peak_signal_iter = atac_bed_df.query('chrom == "%s" and start >= %d and end <= %d' % (current_chrom, peak.start, peak.end)).itertuples()
        for coverage in peak_signal_iter:
            feat_start = coverage.start - peak.start + 1
            feat_end = coverage.end - peak.start
            for position in range(int(feat_start), int(feat_end)):
                peak_window_reads[position] = coverage.reads

        # Gather other peak attributes
        dist_from_last_peak = 0
        if last_peak_end > 0:
            dist_from_last_peak = peak.start - last_peak_end
            last_peak_end = peak.end
        peak_width = peak.end - peak.start

        # Check whether it overlaps a promoter
        promoter_overlaps = len(refseq_df.query('chrom == "%s" and ((strand == "+" and %d < start and %d > start) or (strand == "-" and %d < end and %d > end))' % (current_chrom, peak.start, peak.end, peak.start, peak.end)))

        # Get sequence-based features
        sequence_row = seq_df.query('chrom == "%s" and start < %d and end > %d' % (current_chrom, peak_midpoint, peak_midpoint))
        if len(sequence_row) > 0:
            sequence = sequence_row.iloc[0].seq
            gc_count = 0
            for nucleotide in sequence:
                if nucleotide == 'C' or nucleotide == 'G' or nucleotide == 'S':
                    gc_count += 1
            gc_ratio = float(gc_count) / (2 * SEQUENCE_EVALUATION_RADIUS)
        else:
            print("Could not find sequence information for peak %s:%s-%s ... skipping peak" % (current_chrom, peak.start, peak.end))
            continue

        overlaps_nascent_txn = 0
        if peak.ovlp_txn > 0:
            overlaps_nascent_txn = 1

        chrom_features.append(  { 'chrom': current_chrom, \
                                  'start': peak.start, \
                                  'end': peak.end, \
                                  'ovlp_txn': overlaps_nascent_txn, \
                                  'prom_ovlp': promoter_overlaps, \
                                  'width': peak_width, \
                                  'mean_nr_reads': np.mean(peak_window_reads), \
                                  'max_reads': np.max(peak_window_reads), \
                                  'min_reads': np.min(peak_window_reads), \
                                  'dist_from_last_peak': dist_from_last_peak, \
                                  'gc_ratio': gc_ratio,
                                  'sequence': sequence,
                                  'signal_features': signal_features } )
        last_peak_end = peak.end
    return np.array(chrom_features)

# easy way to create an empty pandas dataframe from
# https://stackoverflow.com/questions/36462257/create-empty-dataframe-in-pandas-specifying-column-types#36463086
def df_empty(columns, dtypes, index=None):
    assert len(columns)==len(dtypes)
    df = pd.DataFrame(index=index)
    for c,d in zip(columns, dtypes):
        df[c] = pd.Series(dtype=d)
    return df

if __name__=='__main__':

    CHROMOSOMES = [ 'chr1', 'chr2', 'chr3', 'chr4', 'chr5', 'chr6', 'chr7', 'chr8', \
                    'chr9', 'chr10', 'chr11', 'chr12', 'chr13', 'chr14', 'chr15', \
                    'chr16', 'chr17', 'chr18', 'chr19', 'chr20', 'chr21', 'chr22', \
                    'chrX', 'chrY']

    atac_peaks_file = args.atac_peaks.split('/')[-1].split('.')[0]
    if args.skip_sequence_caching:  # don't waste cycles if this has already been cached
        seq_df = pd.read_pickle('%s/%s_seq_pandas_dataframe.pickle' % (SEQ_DUMP_DIR, atac_peaks_file))
    else:
        print('Gathering the actual sequence overlapping each peak:')
        # Generate a temp bed file with fixed windows for each peak
        tmp_peaks_filename = '%s/%s_tmp_fixed_peak_windows.bed' % (SEQ_DUMP_DIR, atac_peaks_file)
        with open(tmp_peaks_filename, 'w') as tmp_peaks:
            for peak in atac_peak_df.itertuples():
                if peak.chrom in CHROMOSOMES:
                    peak_midpoint = peak.start + (peak.end - peak.start)/2
                    tmp_peaks.write("%s\t%d\t%d\n" % (peak.chrom, \
                                                      peak_midpoint - SEQUENCE_EVALUATION_RADIUS, \
                                                      peak_midpoint + SEQUENCE_EVALUATION_RADIUS))
        # Gather the sequence for all those fixed-width regions
        sequence = os.popen('%s subseq %s %s > %s/%s.fa' % (SEQTK_PATH, args.genome_fasta, tmp_peaks_filename, SEQ_DUMP_DIR, atac_peaks_file)).read()
        fasta_sequences = SeqIO.parse(open('%s/%s.fa' % (SEQ_DUMP_DIR, atac_peaks_file)),'fasta')
        # Create a dataframe to access these more efficiently
        seq_df = df_empty(['chrom', 'start', 'end', 'seq'], [np.str, np.int, np.int, np.str])
        for seq in fasta_sequences:
            try:
                chrom, coordinates = seq.id.split(':')
                start = coordinates.split('-')[0]
                end = coordinates.split('-')[1]
                seq_df.loc[len(seq_df)] = [chrom, int(start), int(end), str(seq.seq)]
            except Exception as e:
                print("Something went sideways parsing the FASTA:")
                print(seq)
                print('-------------------------------------------')
                print(e)
                raise

        seq_df.to_pickle('%s/%s_seq_pandas_dataframe.pickle' % (SEQ_DUMP_DIR, atac_peaks_file))

    pool = multiprocessing.Pool(NR_THREADS)
    X_and_y_train = pool.map(get_atac_features, CHROMOSOMES)
    pool.close()
    pool.join()

    data = [j for sub in X_and_y_train for j in sub]
    data = pd.DataFrame(data)
    data['signal_features'] = data['signal_features'].map(lambda x: x.astype('float32'))
    data['ovlp_txn'] = data['ovlp_txn'].astype('int')
    data['mean_nr_reads'] = data['mean_nr_reads'].astype('float32')
    data['gc_ratio'] = data['gc_ratio'].astype('float32')
    data.to_pickle('%s/%s_from-fstitchtfit.pk' % (OUTDIR, args.prefix))


import abjad
import random
from organi.tools.see_pitches import see_pitches

# RECURSES TO MODIFY PITCH SEGMENTS
# A C B D C E
def permut_thirds(pitches):
    pitch_list = []
    i = 0
    pitch_list.append(pitches[i])
    while i < len(pitches) - 2:
        i = i + 2
        note = pitches[i]
        pitch_list.append(note)
        i = i - 1
        note = pitches[i]
        pitch_list.append(note)
    return pitch_list

# FREQ_B - FREQ_A AND FREQ_B + FREQ_A
def ring_modulation(pitches, sum=True, chords=False):
    pitches_in = pitches.hertz
    pitches_out = []
    if sum is True and chords is False:
        for pitch1, pitch2 in zip(pitches_in, pitches_in[1:]):
            new_pitch_A = pitch2 - pitch1
            pitches_out.append(new_pitch_A)
            pitches_out.append(pitch1)
            new_pitch_B = pitch1 + pitch2
            pitches_out.append(new_pitch_B)
    if sum is False and chords is False:
        for pitch1, pitch2 in zip(pitches_in, pitches_in[1:]):
            new_pitch_A = pitch2 - pitch1
            pitches_out.append(new_pitch_A)
            new_pitch_B = pitch1 + pitch2
            pitches_out.append(new_pitch_B)
    if sum is True and chords is True:
        for pitch1, pitch2 in zip(pitches_in, pitches_in[1:]):
            new_pitch_A = pitch2 - pitch1
            new_pitch_B = pitch1 + pitch2
            new_pitch_A = abjad.NamedPitch.from_hertz(new_pitch_A)
            new_pitch_B = abjad.NamedPitch.from_hertz(new_pitch_B)
            pitch1 = abjad.NamedPitch.from_hertz(pitch1)
            chord = abjad.Chord([new_pitch_A, pitch1, new_pitch_B], (1, 1))
            pitches_out.append(chord)
    if sum is False and chords is True:
        for pitch1, pitch2 in zip(pitches_in, pitches_in[1:]):
            new_pitch_A = pitch2 - pitch1
            new_pitch_B = pitch1 + pitch2
            new_pitch_A = abjad.NamedPitch.from_hertz(new_pitch_A)
            new_pitch_B = abjad.NamedPitch.from_hertz(new_pitch_B)
            chord = abjad.Chord([new_pitch_A, new_pitch_B], (1, 1))
            print(chord)
            pitches_out.append(chord)
    return pitches_out

# ORIGINAL CHORD
pitches = []
original_chord = abjad.pitch.PitchSegment(
    "cqs' f' gs' c'' e'' ftqs'' gqs''"
    + " gs'' b'' cs''' ctqs''' f''' fs''' ftqs'''  gs'''"
    )
pitches = ring_modulation(original_chord, sum=True)
# pitches.sort()
print(pitches)
pitches = [abjad.NamedPitch.from_hertz(_) for _ in pitches]
electronics_pitches = list.copy(pitches)
num_pitches = [abjad.NamedPitch(_).number for _ in pitches]
# NO MICROTONAL
for item, i in zip(num_pitches, range(len(num_pitches))):
    if isinstance(item, float):
        item = item - 0.5
        num_pitches[i] = item
named_pitches = [abjad.NumberedPitch(_).get_name() for _ in num_pitches]
num_pitches_rev = num_pitches
num_pitches_rev.sort(reverse=True)
num_pitches_rev = permut_thirds(num_pitches_rev)
pitch_range = abjad.PitchRange("[C2, G7]")
pitch_list = []
for i, pitch in enumerate(num_pitches_rev):
    test = pitch in pitch_range
    if test is True:
        pitch_list.append(pitch)

pitches = abjad.pitch.PitchSegment(pitch_list)
staff_group = see_pitches(pitches)
pitches_voice_one = pitches
# abjad.f(pitches_voice_one)
pitches_voice_four = pitches[7:]
# abjad.f(pitches_voice_four)

# para funcionar com a debaixo
# conforme primeira versão
# não pode fazer o retrogrado (sort reverse)
# nem fazer as terças
# select notes
# modifiquei para num_pitches_rev para poder funcionar tanto o de cima quanto o de baixo
chord_down = []
notes_down = [8, 9, 11, 12, 13, 14]
for i, note in enumerate(num_pitches):
    if i in notes_down:
        chord_down.append(note)

chord_up = []
notes_up = [15, 16, 17, 18, 19, 20]
for i, note in enumerate(num_pitches):
    if i in notes_up:
        chord_up.append(note)

chord_up_longer = []
chord_down_longer = []

for i in range(8):
    random.seed(i+3)
    random.shuffle(chord_up)
    random.seed(i+3)
    random.shuffle(chord_down)
    for note_up in chord_up:
        chord_up_longer.append(note_up)
    for note_down in chord_down:
        chord_down_longer.append(note_down)


# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
"""
The mriqc package provides a series of :abbr:`NR (no-reference)`,
:abbr:`IQMs (image quality metrics)` to used in :abbr:`QAPs (quality
assessment protocols)` for :abbr:`MRI (magnetic resonance imaging)`.

"""
from .__about__ import (
    __copyright__,
    __credits__,
    __version__,
)

__all__ = [
    "__copyright__",
    "__credits__",
    "__version__",
]


__all__ = [
    'AuthorizationView',
    'RevokeTokenView',
    'TokenView',
    'IntrospectTokenView',
]

from calendar import timegm

from oauth2_provider.views import (
    AuthorizationView,
    RevokeTokenView,
    TokenView,
)
from rest_framework import (
    generics,
    response,
    status,
)

from ..authentication import (
    verify as auth_verify,
)
from ..permissions import (
    TokenHasScope,
)
from .serializers import (
    IntrospectTokenSerializer,
)

class IntrospectTokenView(generics.GenericAPIView):
    permission_classes = [TokenHasScope]
    serializer_class = IntrospectTokenSerializer
    required_scopes = ['introspection']

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        user, access_token = auth_verify(
            serializer.validated_data['token'])
        if not access_token:
            return response.Response(
                {'error': 'user not found'},
                status=status.HTTP_404_NOT_FOUND,
            )

        serializer.save(
            exp=int(timegm(access_token.expires.utctimetuple())),
            scope=access_token.scope,
            user={
                'uuid': user.uuid,
                'username': user.realm_username,
                'realm': user.realm,
            },
        )
        return response.Response(serializer.data)