import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import classify_num_galaxies as c

from torch.utils.data import Dataset, DataLoader
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt

import math
import sys
import os

# define global vars
BATCH_SIZE = 30
EPOCH = 4
INPUT_SIZE = 9
TIME_STEP = 5
PATH = os.path.join('models', 'rnn_model.pt')
torch.manual_seed(1)

class GalDataset(Dataset):

	def __init__(self, train=True):
		"""
		__init__ takes in boolean of train or test
		and creates a data set consisting of either
		80 percent of data (train) or 20 percent of
		data (test)
		"""

		x_file = os.path.join('params', 'gal_sim_params.txt')
		y_file = os.path.join('params', 'sep_num_found.txt')
		lens_file = os.path.join('params', 'lens.txt')

		x = np.loadtxt(x_file)
		y = np.loadtxt(y_file)
		lens = np.loadtxt(lens_file)

		print(x.shape)

		x = np.reshape(x, (int(x.shape[0]/5), x.shape[1]*5))
		x = torch.from_numpy(x).float()
		y = (torch.from_numpy(y) - 1).long()

		x = c.normalize(x)

		#self.y = F.one_hot(y.to(torch.int64), GALS).float()
		n_samples = len(x)
		print(n_samples)
		print(f'reshaped size of x', x.shape)

		if train:
			self.x = x[0:int(0.8*n_samples),:]
			self.y = y[0:int(0.8*n_samples)]
			self.n_samples = int(0.8*n_samples)
			self.lens = torch.from_numpy(lens[0:int(0.8*n_samples)])
		else:
			self.x = x[int(0.8*n_samples):,:]
			self.y = y[int(0.8*n_samples):]
			self.n_samples = int(0.2*n_samples)
			self.lens = torch.from_numpy(lens[int(0.8*n_samples):n_samples])

	def __getitem__(self, index):
		"""
		returns item in dataset at index
		"""
		return self.x[index], self.y[index], self.lens[index]

	def __len__(self):
		"""
		returns length of dataset
		"""
		return self.n_samples

class RNNClassifier(nn.Module):
    def __init__(self):
    	super(RNNClassifier, self).__init__()
    	"""
    	initiates classifier, takes in 9 galaxy parameters as input
    	has one hidden layer, and outputs dim 5. Output is contains probability of
    	1 - 5 galaxies being predicted by source extractor.
    	"""
    	self.hidden_size = 64

    	self.rnn = nn.LSTM(INPUT_SIZE, 64, num_layers=1, batch_first=True)
    	self.out = nn.Linear(64, TIME_STEP)

    def forward(self, x, x_lengths):
    	"""
    	propogates forward on input x,
    	output is returned at final ieration

    	:params x: shape batch_size x time stipes x input dim
    	:params x_lengths: vector of size batch_size that contains number of stars
    	per image

    	"""
    	# when pack = True we have a varaible sized RNN
    	# otherwise everything is just padded
    	pack = False

    	batch_size, seq_len, _ = x.size()

    	#print('original size in forward', x.size())
    	if pack:
    		x = pack_padded_sequence(x, x_lengths, batch_first=True, enforce_sorted=False)

    	output, (h_n, h_c) = self.rnn(x, None)

    	if pack:
    		output, _ = pad_packed_sequence(output, batch_first=False, total_length=seq_len)
    		output = output.view(batch_size*seq_len, self.hidden_size)

    	"""
    	Idea is to look at the output value on the last non-padded input.
    	Previosuly we were always looking at the fith output regardless of
    	how many galaxies there were.

    	"""
    	if pack:
    		# uses the l-1 time step
    		adjusted_lengths = [batch_size*(l-1) + i for i, l in enumerate(x_lengths)]
    		lengthTensor = torch.tensor(adjusted_lengths, dtype=torch.int64)
    		output = output.index_select(0, lengthTensor)
    	elif not pack:
    		# uses that last output on the fith time step
    		output = output[:,-1,:]

    	#output = output.view(batch_size, self.hidden_size)

    	fc_out = self.out(output)

    	return fc_out

"""
Each input should be the parameters of a single galaxy
between 1 and five sets on input parameters

"""
def NN_vs_source(y_source, y_NN, GALS=5, PARAM_PER_GAL=9):
	"""
	graphs the NN output vs the true source extractor output
	"""

	class_array = np.zeros((TIME_STEP+1, TIME_STEP+1))
	for i in range(len(y_source)):
		class_array[y_source[i]+1][y_NN[i]+1] += 1

	class_array = (class_array.T/class_array.sum(axis=1)).T

	plt.matshow(class_array, cmap='YlOrRd')
	plt.ylabel('# identified by SE')
	plt.xlabel('# predicted by RNN')
	plt.suptitle('RNN predictions of SE vs SE')
	for (i, j), z in np.ndenumerate(class_array):
		plt.text(j, i, '{:0.3f}'.format(z), ha='center', va='center')
	file_name = os.path.join('charts', 'nn_vs_se_rnn.png')
	plt.savefig(file_name)
	plt.show()

def train():
	"""
	creates dataloader objects and instance of RNN to train model.
	saves model at specifified path
	"""

	# load training, test data
	train_set = GalDataset()
	test_set = GalDataset(train=False)
	train_loader = DataLoader(dataset=train_set, batch_size=BATCH_SIZE)
	test_x = Variable(test_set.x, volatile=True).type(torch.FloatTensor)
	test_y = test_set.y.numpy().squeeze()
	test_lens = test_set.lens.numpy().squeeze()

	# create RNN model
	rnn = RNNClassifier()
	print(rnn)
	optimizer = torch.optim.Adam(rnn.parameters(), lr=0.001)
	loss_func = nn.CrossEntropyLoss()

	# set hyper parameters
	total_samples = len(train_set)
	n_iterations = math.ceil(total_samples/BATCH_SIZE)
	print(total_samples, n_iterations)

	for epoch in range(EPOCH):
		for i, (x, y, l) in enumerate(train_loader):

			# reshape x input values to (batch, time_step, input_size)
			b_x = Variable(x.view(-1, TIME_STEP, INPUT_SIZE))
			b_l = Variable(l)
			b_y = Variable(y)

			output = rnn(b_x, b_l)

			loss = loss_func(output, b_y)
			optimizer.zero_grad()

			loss.backward()

			optimizer.step()

			if i % 100 == 0:

				test_output = rnn(test_x.view(-1, TIME_STEP, INPUT_SIZE), test_lens)
				pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
				accuracy = sum(pred_y == test_y) / float(test_y.size)

				print(f'epoch {epoch+1}/{EPOCH}, step {i+1}/{n_iterations}, inputs {x.shape}')
				print(f'test accuracy: {accuracy}')
				print(f'loss {loss}')

	torch.save(rnn.state_dict(), PATH)
	NN_vs_source(test_y, pred_y)
	print(test_y[0:100])
	print(pred_y[0:100])

def eval():
	"""
	loads trained model and evaluates it
	"""

	test_set = GalDataset(train=False)

	test_x = Variable(test_set.x, volatile=True).type(torch.FloatTensor)
	test_y = test_set.y.numpy().squeeze()
	test_lens = test_set.lens.numpy().squeeze()

	rnn = RNNClassifier()
	rnn.load_state_dict(torch.load(PATH))

	rnn.eval()

	test_output = rnn(test_x.view(-1, TIME_STEP, INPUT_SIZE), test_lens)
	pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
	accuracy = sum(pred_y == test_y) / float(test_y.size)

	print(f'overall accuracy: {accuracy}')
	NN_vs_source(test_y, pred_y)

train()
#eval()


import os
import tensorflow as tf

from absl import logging
from monolith.native_training.data.training_instance.python.instance_dataset_ops import PBInstanceDataset
from monolith.native_training.data.training_instance.python.parse_instance_ops import parse_instances
from tensorflow.python.framework import sparse_tensor

FIDV1_FEATURES = [i for i in range(1, 10)]
FIDV2_FEATURES = ["fc_360d_ml_convert_cid", "fc_360d_ml_convert_advertiser_id"]
FLOAT_FEATURES = ["fc_muse_finish_rough_10168_uid_d128"]
FLOAT_FEATURES_DIM = [128]
INT64_FEATURES = ["fc_dense_external_action"]
INT64_FEATURE_DIM = [1]

def parse(serialized):
  return parse_instances(serialized, FIDV1_FEATURES, FIDV2_FEATURES,
                         FLOAT_FEATURES, FLOAT_FEATURES_DIM, INT64_FEATURES,
                         INT64_FEATURE_DIM)

def testInstanceDataset():

  # with self.session() as sess:
  with tf.compat.v1.Session() as sess:
    logging.warning("PBInstanceDatasetV2 process is Starting")
    dataset = PBInstanceDataset(
        file_name="",
        has_sort_id=True,
        kafka_dump_prefix=True,
    )
    dataset = dataset.batch(32).map(parse)
    it = tf.compat.v1.data.make_one_shot_iterator(dataset)
    element = it.get_next()
    logging.warning("PBInstanceDatasetV2 next process is Finished")
    elements = sess.run(element)
    logging.warning(element)
    logging.warning(elements["sample_rate"])

if __name__ == '__main__':
  tf.compat.v1.disable_eager_execution()
  testInstanceDataset()


from netlib.Layer import BaseLayerClass
from configs.config import cfg
import numpy as np

def get_indices(x_shape, kernel_height, kernel_width, padding=1, stride=1, c_=None):
    """
    :param x_shape (tuple): shape of input tensor
    :param kernel_height (int): filter size
    :param kernel_width (int): filter size
    :param padding (int)
    :param stride (int)
    :return: k, i, j - indices
    """
    h, w = x_shape[2], x_shape[3]
    c = c_ if c_ is not None else x_shape[1]
    output_h = (h + 2 * padding - kernel_height) // stride + 1
    output_w = (w + 2 * padding - kernel_width) // stride + 1

    k = np.repeat(np.arange(c), kernel_height * kernel_width).reshape(-1, 1)

    i1 = np.tile(np.repeat(np.arange(kernel_height), kernel_width), c).reshape(-1, 1)
    i2 = (np.repeat(np.arange(output_h), output_w) * stride).reshape(1, -1)
    i = np.sum((i1, i2))

    j1 = np.tile(np.arange(kernel_width), kernel_height * c).reshape(-1, 1)
    j2 = (np.tile(np.arange(output_w), output_h) * stride).reshape(1, -1)
    j = np.sum((j1, j2))
    return k, i, j

def tensor2matrix(x, kernel_height, kernel_width, padding=1, stride=1, indices=None):  # conv, max_pool forward
    """
    :param x: input tensor
    :param kernel_height (int): filter size
    :param kernel_width (int): filter size
    :param padding (int)
    :param stride (int)
    :return: converted matrix
    """
    # get_indices(x, k_width, k_height, pad, stride);
    # apply indices to tensor: x_cols = x[:, k, i, j] (for previous example x_cols.shape (1, 8, 4));
    # (batch_size, x_depth*k_width*k_height, output_width*output_height)
    # reshape new tensor: result = x_cols.transpose(1, 2, 0).reshape(k_width*k_height*x_depth, -1)
    c = x.shape[1]
    k, i, j = get_indices(x.shape, kernel_height, kernel_width, padding, stride) if indices is None else indices
    x = np.pad(x, ((0, 0), (0, 0), (padding, padding), (padding, padding)), mode='constant')
    converted_x = x[:, k, i, j]
    converted_x = converted_x.transpose(1, 2, 0).reshape(kernel_height * kernel_width * c, -1)
    return converted_x

def matrix2tensor(grad_input, input_shape, kernel_height, kernel_width, padding=1, stride=1):  # conv, max_pool backward
    """
    :param grad_input: input tensor of gradients
    :param input_shape (tuple): shape of input tensor
    :param kernel_height (int): filter size
    :param kernel_width (int): filter size
    :param padding (int)
    :param stride (int)
    :return: converted matrix
    """
    b_size, c, h, w = input_shape
    # Initialize new tensor with zeros(input.shape);
    # get indices k, i, j (as in forward pass);
    # reshape input tensor to size=(k_depth * k_height * k_width, output_width*output_height, batch_size);
    # transpose reshaped tensor to size=(batch_size, k_depth * k_height * k_width, output_width*output_height);
    # apply indices to sum values on the same positions (use np.add.at);
    # remove padded part.
    converted_tensor = np.zeros((b_size, c, h + 2 * padding, w + 2 * padding))
    k, i, j = get_indices(input_shape, kernel_height, kernel_width, padding, stride)
    reshaped_input = grad_input.reshape(c * kernel_height * kernel_width, -1, b_size)
    transposed_reshaped_input = reshaped_input.transpose(2, 0, 1)
    np.add.at(converted_tensor, (slice(None), k, i, j), transposed_reshaped_input)
    converted_tensor = converted_tensor[:, :, padding:-padding, padding:-padding] if padding != 0 else converted_tensor
    return converted_tensor

class Convolution(BaseLayerClass):
    def __init__(self, kernel_size, nrof_filters, kernel_depth, zero_pad, stride, use_bias, initialization_type):
        self.cfg = cfg
        self.kernel_size = kernel_size
        self.nrof_filters = nrof_filters
        self.kernel_depth = kernel_depth
        self.zero_pad = zero_pad
        self.stride = stride
        self.use_bias = use_bias
        self.initialization_type = initialization_type

        self.init_weights()

    def trainable(self):
        return True

    def init_weights(self):
        k_width, k_height = self.kernel_size if isinstance(self.kernel_size, tuple) \
            else (self.kernel_size, self.kernel_size)
        k_depth = self.kernel_depth
        n_filters = self.nrof_filters
        n = k_width * k_height * k_depth
        m = n_filters

        if self.initialization_type.name.endswith('Normal'):
            if self.initialization_type.name == 'Normal':
                scale = self.cfg['convolution_layer']['scale']
            elif self.initialization_type.name == 'HeNormal':
                scale = np.sqrt(2 / n)
            elif self.initialization_type.name == 'XavierNormal':
                scale = np.sqrt(1 / n)
            elif self.initialization_type.name == 'GlorotNormal':
                scale = np.sqrt(1 / (n + m))
            else:
                raise Exception
            self.W = np.random.normal(size=[n_filters, k_depth, k_width, k_height], scale=scale)
            self.b = np.random.normal(size=[n_filters], scale=scale) if self.use_bias else None
        else:
            if self.initialization_type.name == 'Uniform':
                l = self.cfg['convolution_layer']['l']
            elif self.initialization_type.name == 'HeUniform':
                l = np.sqrt(6 / n)
            elif self.initialization_type.name == 'XavierUniform':
                l = np.sqrt(3 / n)
            elif self.initialization_type.name == 'GlorotUniform':
                l = np.sqrt(6 / (n + m))
            else:
                raise Exception
            self.W = np.random.uniform(-l, l, size=[n_filters, k_depth, k_width, k_height])
            self.b = np.random.uniform(-l, l, size=[n_filters]) if self.use_bias else None

    def __call__(self, input, phase='eval'):
        """
        :param input: input tensor, shape=(batch_size, depth, height, width)
        :param phase: ['train', 'eval']
        :return: output of convolutional layer
        """
        weights, stride, padding, bias = self.W, self.stride, self.zero_pad, self.b
        b_size, c, h, w = input.shape
        n_filters, k_depth, k_height, k_width = weights.shape
        output_h = (h + 2 * padding - k_height) // stride + 1
        output_w = (w + 2 * padding - k_width) // stride + 1

        # Convert input tensor to matrix (shape: (x_depth*k_width*k_height, output_width*output_height*batch_size));
        # reshape weights (new shape: (n_filters, depth*k_width*k_height));
        # multiply reshaped weights and converted tensor;
        # reshape output tensor (from (n_filters, output_width*output_height*batch_size) to (n_filters, output_width,
        # output_height, batch_size);
        # transpose: result = reshaped_output.transpose(3, 0, 1, 2).
        converted_input = tensor2matrix(input, k_height, k_width, padding, stride)
        reshaped_weights = weights.reshape((n_filters, -1))
        weights_converted_input_matmul = reshaped_weights @ converted_input
        if bias is not None:
            weights_converted_input_matmul += bias.reshape(-1, 1)

        reshaped_output_tensor = weights_converted_input_matmul.reshape(n_filters, output_h, output_w, b_size)
        transposed_output_tensor = reshaped_output_tensor.transpose(3, 0, 1, 2)

        self.saved_data = {'input': input, 'w': weights, 'b': bias, 'stride': stride, 'padding': padding,
                           'converted_input': converted_input}
        return transposed_output_tensor

    def backward(self, layer_input, dy):
        stride, pad = self.stride, self.zero_pad
        input = self.saved_data['input']
        weights = self.saved_data['w']
        stride = self.saved_data['stride']
        converted_input = self.saved_data['converted_input']
        n_filters,  k_depth, k_height, k_width = weights.shape

        # Reshape input gradients: size = (n_filters, output_w*output_h*batch_size);
        # reshaped_gradients @ converted_tensor.T;
        # reshape output matrix according shape of weights.
        reshaped_input_grads = dy.transpose(1, 2, 3, 0).reshape(n_filters, -1)
        dw = reshaped_input_grads @ converted_input.T
        dw = dw.reshape(weights.shape)
        if self.use_bias:
            db = np.sum(dy, axis=(0, 2, 3))
            dw = (dw, db)
        # reshape weights: size=(n_filters, k_width*k_height*k_depth); (get from forward pass)
        # matmul reshaped weights and reshaped gradients: size=(k_width*k_height*k_depth, output_w*output_h*batch_size);
        # convert matrix to tensor.
        reshaped_weights = weights.reshape(n_filters, -1)
        weights_input_grads_matmul = reshaped_weights.T @ reshaped_input_grads
        dx = matrix2tensor(weights_input_grads_matmul, input.shape, k_height, k_width, pad, stride)
        return dx, dw

class Pooling(BaseLayerClass):
    def __init__(self, kernel_size, stride, type='Max'):
        """
        :param kernel_size (int): kernel size
        :param stride (int): stride
        :param type (string): ['Max', 'Avg']
        """
        self.kernel_size = kernel_size
        self.stride = stride
        self.type = type

    def __call__(self, input, phase='eval'):
        """
       :param input: input tensor, shape=(batch_size, depth, height, width)
       :param phase: ['train', 'eval']
       :return: output of pooling layer
       """
        k_width, k_height = self.kernel_size if isinstance(self.kernel_size, tuple) \
            else (self.kernel_size, self.kernel_size)
        b_size, c, h, w = input.shape
        output_h = (h - k_height) // self.stride + 1
        output_w = (w - k_width) // self.stride + 1

        # Reshape input tensor: x.reshape(batch_size*x_depth, 1, x_height, x_width)
        # Convert input tensor (shape: (1*k_width*k_height, output_width*output_height*batch_size*x_depth));
        # apply max/average pooling for each row;
        # reshape output tensor (from (output_width*output_height*batch_size*x_depth) to (output_width,
        # output_height, batch_size, x_depth);
        # transpose: result = reshaped_output.transpose(2, 3, 0, 1).
        x_reshaped = input.reshape(b_size * c, 1, h, w)
        converted_x = tensor2matrix(x_reshaped, k_height, k_width, padding=0, stride=self.stride)

        if self.type == 'Max':
            out = np.max(converted_x, 0)
        elif self.type == 'Avg':
            out = np.mean(converted_x, 0)
        else:
            raise Exception
        out = out.reshape(output_h, output_w, b_size, c)
        out = out.transpose(2, 3, 0, 1)

        self.saved_data = {'input': input, 'converted_input': converted_x}
        return out

    def backward(self, layer_input, dy):
        converted_x = self.saved_data['converted_input']
        input = self.saved_data['input']
        b_size, c, h, w = input.shape
        k_width, k_height = self.kernel_size if isinstance(self.kernel_size, tuple) \
            else (self.kernel_size, self.kernel_size)

        # Initialize tenzor with zeros, shape=(1*k_width*k_height, output_width*output_height*batch_size*x_depth);
        # transpose input tensor of gradients: (x_width, x_height, batch_size, x_depth);
        # view transposed tensor;
        # apply backward max/average-pooling for each row;
        # convert matrix to tensor;
        # reshape from (batch_size*x_depth, 1, x_height, x_width) to (batch_size, x_depth, x_height, x_width).
        zeros_tensor = np.zeros_like(converted_x)
        transposed_input_grads = dy.transpose(2, 3, 0, 1)
        flattened_transposed_input_grads = transposed_input_grads.flatten()
        if self.type == 'Max':
            # backward max-pooling
            max_ids = np.argmax(converted_x, axis=0)
            matrix_coords = (max_ids, np.arange(len(max_ids)))
            zeros_tensor[matrix_coords] = flattened_transposed_input_grads
        elif self.type == 'Avg':
            # backward average-pooling
            zeros_tensor += np.ones(zeros_tensor.shape) * 1/zeros_tensor.shape[0]

        converted_matrix = matrix2tensor(zeros_tensor, (b_size * c, 1, h, w), k_height, k_width, padding=0, stride=self.stride)
        pooling_grad = converted_matrix.reshape(input.shape)
        return pooling_grad, None


#Currently bluetooth connectivity is not being developed
import bluetooth

class BluetoothReceiver:
    def __init__(self):
        self.available_devices = []
        self.connected_device = None
        self.sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)

    def discover_devices(self):
        try:
            self.available_devices = bluetooth.discover_devices()
        except OSError:
            return "No bluetooth hardware"
        except:
            return "error"
        else:
            for dev in self.available_devices:
                print(f"{dev}: {bluetooth.lookup_name(dev)}")
                services = bluetooth.find_service(address=dev)
                for i in services:
                    print(i)

    def connect_to_device(self):
        bd_addr = "01:23:45:67:89:AB"
        port = 0x1001
        self.sock.connect((bd_addr, port))
        self.sock.send("hello!!")
        self.sock.close()


import numpy as np
import matplotlib.pyplot as plt

# Define the function to be transformed
def f(x):
    return np.sin(x)

# Discretize the function
N = 512
x = np.linspace(0, 2*np.pi, N)
y = f(x)

# Compute the Fourier transform
Y = np.fft.fft(y)

# Plot the original and transformed functions
plt.figure()
plt.plot(x, y)
plt.title("Original function")

plt.figure()
plt.plot(x, np.abs(Y))
plt.title("Fourier transform of the function")

plt.show()


import discord
from discord.ext import commands

class Ban(commands.Cog):

    def __init__(self, client):
        self.client = client

    @commands.command()
    @commands.has_permissions(kick_members=True, ban_members=True)
    async def ban(self, ctx, member : discord.Member, *, reason=None):
        await member.ban(reason=reason)
        await ctx.send(f'banned {member.mention}')

    @commands.command()
    @commands.has_permissions(kick_members=True, ban_members=True)
    async def unban(self, ctx, *, member):
        banned_users = await ctx.guild.bans()
        member_name, member_discriminator = member.split('#')

        for ban_entry in banned_users:
            user = ban_entry.user

            if (user.name, user.discriminator) == (member_name, member_discriminator):
                await ctx.guild.unban(user)
                await ctx.send(f'Unbanned {user.mention}')
                return

    @ban.error
    async def ban_error(self, ctx, error):
        if isinstance(error, commands.MissingPermissions):
            await ctx.send(f'You dont have the required permission.')
        if isinstance(error, commands.MissingRequiredArgument):
            await ctx.send(f'Please pass all the required arguments.')

    @unban.error
    async def unban_error(self, ctx, error):
        if isinstance(error, commands.MissingPermissions):
            await ctx.send(f'You dont have the required permissions.')

        if isinstance(error, commands.MissingRequiredArgument):
            await ctx.send(f'Please mention the member along with the #.')

def setup(client):
    client.add_cog(Ban(client))


import sys

N = int(sys.stdin.readline())
data = [[] for _ in range(101)]

for _ in range(N):

  name, k,e,m = sys.stdin.readline().split()
  k,e,m = int(k),int(e),int(m)

  data[k].append((name,k,e,m))

for i in range(100,0,-1):

  length = len(data[i])

  if length==0 :
    continue

  elif length == 1:
    print(data[i][0][0])
    continue

  else:

    data[i].sort(key=lambda x : x[0])
    data[i].sort(key=lambda x : x[3],reverse=True)
    data[i].sort(key=lambda x : x[2])

    for j in range(length):
      print(data[i][j][0])


##
# Tank Stars
# @author: Chandresh
# @course: ICS3UC
# @date: 22/06/2022
# @teacher: Mr. Reid

# import libraries
import pygame
import random

## Classes
# Player class
class Player(pygame.sprite.Sprite):
    """ The class is the player-controlled sprite. """

    # -- Methods
    def __init__(self, x, y, file, width, height, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.degrees = 270
        # Set height, width
        self.image = pygame.image.load(file).convert()
        self.image = pygame.transform.scale(self.image, (width, height))
        self.image = pygame.transform.rotate(self.image, self.degrees)
        self.image1 = self.image
        self.image.set_colorkey(color)

        # Make our top-left corner the passed-in location.
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        # -- Attributes
        # Set speed vector
        self.change_x = 0
        self.change_y = 0

    def changespeedDown(self, x, y, z):
        """ Change the speed of the player"""
        self.change_x += x
        self.change_y += y
        self.degrees = z
        self.image = pygame.transform.rotate(self.image1, self.degrees)

    def changespeedUp(self, x, y):
        """ Change the speed of the player"""
        self.change_x += x
        self.change_y += y

    def update(self):

        """ Find a new position for the player"""
        self.rect.x += self.change_x
        self.rect.y += self.change_y

# NPC class
class NPC(pygame.sprite.Sprite):
  def __init__(self, x, y, width, height, file, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        # Set height, width
        self.image =  pygame.image.load(file).convert()
        self.image =  pygame.transform.scale(self.image, (width, height))
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        # -- Attributes
        # Set speed vector
        self.change_x = 0
        self.change_y = 0
  def changespeed(self, x, y):
        self.change_x = x
        self.change_y = y

  def update(self):
        """ Find a new position for the player"""
        self.rect.x += self.change_x
        self.rect.y += self.change_y

# Shuriken class
class Shuriken(pygame.sprite.Sprite):
  # -- Methods
    def __init__(self, x, y, file, width, height, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.degrees = 0
        self.image = pygame.image.load(file).convert()
        self.image = pygame.transform.scale(self.image, (width, height))
        self.image = pygame.transform.rotate(self.image, self.degrees)
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.change_x = 0
        self.change_y = 0

    def changespeed(self, x, y, z):
        self.change_x = x
        self.change_y = y
        self.degrees = z
        self.image = pygame.transform.rotate(self.image, self.degrees)

    def update(self):
        """ Find a new position for the player"""
        self.rect.x += self.change_x
        self.rect.y += self.change_y

# Portal Class
class Portal(pygame.sprite.Sprite):
  # -- Methods
    def __init__(self, x, y, file, width, height, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.image = pygame.image.load(file).convert()
        self.image = pygame.transform.scale(self.image, (width, height))
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.change_x = 0
        self.change_y = 0

# Hellfire class
class Hellfire(pygame.sprite.Sprite):
  # -- Methods
    def __init__(self, x, y, file, width, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.image = pygame.image.load(file).convert()
        self.height = 100
        self.image = pygame.transform.scale(self.image, (width, self.height))
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.change_y = 0
        self.change_x = 0

    def changespeed(self, x):
        self.change_y = x

    def update(self):
        """ Find a new position for the player"""
        self.height += self.change_y

# Cannonball Class
class Cannonball(pygame.sprite.Sprite):
    # -- Methods
    def __init__(self, x, y, file, width, height, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.degrees = 0
        self.image = pygame.image.load(file).convert()
        self.image = pygame.transform.scale(self.image, (width, height))
        self.image = pygame.transform.rotate(self.image, self.degrees)
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.change_x = 0
        self.change_y = 0

    def changespeed(self, x, y, z):
        self.change_x = x
        self.change_y = y
        self.degrees = z
        self.image = pygame.transform.rotate(self.image, self.degrees)

    def update(self):
        """ Find a new position for the player"""
        self.rect.x += self.change_x
        self.rect.y += self.change_y

# Healthbar calss
class Healthbar():
  def __init__(self, color, color2):
      """Constructor function"""
      # Call the parent's constructor
      super().__init__()
      # Set height, width
      self.x = 50
      self.y = 25
      self.x2 = 40
      self.y2 = 30
      self.x3 = 50
      self.y3 = 35
      self.x4 = 750
      self.y4 = 35
      self.x5 = 760
      self.y5 = 30
      self.x6 = 750
      self.y6 = 25
      self.outerx4 = 750
      self.outerx5 = 760
      self.outerx6 = 750
      self.color = color
      self.color2 = color2
  def drawing(self, space):
      pygame.draw.polygon(space, self.color, [[self.x, self.y], [self.x2, self.y2], [self.x3, self.y3], [self.x4, self.y4], [self.x5, self.y5], [self.x6, self.y6]])
      pygame.draw.polygon(space, self.color2, [[self.x, self.y], [self.x2, self.y2], [self.x3, self.y3], [self.outerx4, self.y4], [self.outerx5, self.y5], [self.outerx6, self.y6]], 3)

# Generator class
class Generator(pygame.sprite.Sprite):
  def __init__(self, x, y, file, width, height, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.degrees = 0
        self.image = pygame.image.load(file).convert()
        self.image = pygame.transform.scale(self.image, (width, height))
        self.image = pygame.transform.rotate(self.image, self.degrees)
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.change_x = 0
        self.change_y = 0

  def update(self):
      """ Find a new position for the player"""
      self.rect.x += self.change_x
      self.rect.y += self.change_y

# Powerup Class
class Powerup(pygame.sprite.Sprite):
  def __init__(self, x, y, file, width, height, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.degrees = 0
        self.image = pygame.image.load(file).convert()
        self.image = pygame.transform.scale(self.image, (width, height))
        self.image = pygame.transform.rotate(self.image, self.degrees)
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.change_x = 0
        self.change_y = 0

  def update(self):
      """ Find a new position for the player"""
      self.rect.x += self.change_x
      self.rect.y += self.change_y

# Barrier Class
class Barrier(pygame.sprite.Sprite):
  def __init__(self, x, y, file, width, height, color):
        """Constructor function"""
        # Call the parent's constructor
        super().__init__()
        self.image = pygame.image.load(file).convert()
        self.image = pygame.transform.scale(self.image, (width, height))
        self.image.set_colorkey(color)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.change_x = 0
        self.change_y = 0

  def update(self):
      """ Find a new position for the player"""
      self.rect.x += self.change_x
      self.rect.y += self.change_y

##Subprograms
# scene1
def scene1(texts):
  background = pygame.image.load("CptImages/backgroundimageScene1.webp").convert()
  background = pygame.transform.scale(background, (800, 600))
  screen.blit(background, [0, 0])
  screen.blit(texts, [365, 45])

# scene2
def scene2(texts, x, y):
  background1 = pygame.image.load("CptImages/maze.png").convert()
  background1 = pygame.transform.scale(background1, (800, 600))
  screen.blit(background1, [0, 0])
  screen.blit(texts, [x, y])

## Pygame setup
pygame.init()

# set width and height of screen
width = 800
height = 600

# send font of texts
font = pygame.font.SysFont('Helvetica', 15, True, False)

# set base variables to static values
scene = 0
end = 0
health = 700
stopper = "go"
stopper1 = "go"
stopper2 = "go"
stopper3 = "go"
stopper4 = "go"
stopper5 = "go"
stopper6 = "go"
stopper7 = "go"
stopper8 = "go"
stopper9 = "go"
stopper10 = "go"
stopper11 = "go"
stopper12 = "go"
stopper13 = "go"
dontHit = 0
fail = 0
power1 = 0
power2 = 0
power3 = 0
bossShoot1 = 1
bossShoot2 = 1
bossShoot3 = 1
shoot = 0
x1 = 230
y1 = 570
ability = 0
tornado = 0
reset = 0
fire = 0
resetter = 0
connection = 0
reAdd = 0
reAdjust = 1
hellShoot = 0
lifeCounter = 0

#create sounds variables
tornadoSound = pygame.mixer.Sound("CptSounds/tornadoSound.mp3")
cannonballSound = pygame.mixer.Sound("CptSounds/cannonballSound.mp3")

#create the screen
screen = pygame.display.set_mode((width,height))

# set the name of the window
pygame.display.set_caption("Tank Stars!")

## MODEL - Data used in the system
# Define some colors
BLACK    = (0, 0, 0)
WHITE    = (255, 255, 255)
BLUE     = (0, 0, 255)
GREEN    = (0, 255, 0)
RED      = (255, 0, 0)
GREY = (141, 141, 141)
YELLOW = (224,196,87)

# Loop until the user clicks the close button.
done = False

# create text of level boss
text = font.render("Elden Lord",True,WHITE)

# create empty sprite groups
shuriken1SpritesGroup = pygame.sprite.Group()
shuriken2SpritesGroup = pygame.sprite.Group()
shuriken3SpritesGroup = pygame.sprite.Group()
playerSpritesGroup = pygame.sprite.Group()
cannonSpritesGroup = pygame.sprite.Group()
allSpritesGroup1 = pygame.sprite.Group()
allSpritesGroup = pygame.sprite.Group()
npcSpritesGroup = pygame.sprite.Group()
npcSpritesGroup1 = pygame.sprite.Group()
portalSpritesGroup = pygame.sprite.Group()
allSpritesGroup2 = pygame.sprite.Group()
chargerSpritesGroup = pygame.sprite.Group()
charger1SpritesGroup = pygame.sprite.Group()
charger2SpritesGroup = pygame.sprite.Group()
charger3SpritesGroup = pygame.sprite.Group()
powerupSpritesGroup = pygame.sprite.Group()
hellfireSpritesGroup = pygame.sprite.Group()
barrierHorizontal1SpritesGroup = pygame.sprite.Group()
barrierHorizontal2SpritesGroup = pygame.sprite.Group()
barrierVertical1SpritesGroup = pygame.sprite.Group()
barrierVertical2SpritesGroup = pygame.sprite.Group()

# clock to include frames
clock = pygame.time.Clock()

# create the sprites for scene 0
person = Player(500, 550, "CptImages/TopDownTank.png", 50, 50, BLACK)
npc = NPC(700, 510, 90, 90, "CptImages/NPC1.png", WHITE)

# add the sprites for scene 0
allSpritesGroup.add(person)
allSpritesGroup.add(npc)
npcSpritesGroup.add(npc)

# create a healthbar
healthbar = Healthbar(RED, WHITE)

# create the sprites for scene 1
person1 = Player(160, 300, 'CptImages/fightTank.png', 150, 150, BLACK)
playerHitbox = Player(170, 355, 'CptImages/Black.png', 60, 100, BLACK)
person1.changespeedDown(0, 0, 90)
npc1 = NPC(465, 120, 180, 180, "CptImages/NPC1.png", WHITE)
npc1Hitbox = NPC(540, 140, 20, 140, "CptImages/Black.png", BLACK)
shuriken1 = Shuriken(npc1.rect.x, npc1.rect.y+100, "CptImages/shuriken.png", 20, 20, BLACK)
shuriken2 = Shuriken(npc1.rect.x+25, npc1.rect.y+50, "CptImages/shuriken.png", 20, 20, BLACK)
shuriken3 = Shuriken(npc1.rect.x+50, npc1.rect.y+50, "CptImages/shuriken.png", 20, 20, BLACK)
cannonball = Cannonball(person1.rect.x+155, person1.rect.y+7, "CptImages/cannonball.gif", 35, 35, BLACK)
portal = Portal(random.randrange(0,800), 420, "CptImages/portal.png", 100, 100, BLACK)
hellfire = Hellfire(portal.rect.x, -50, "CptImages/hellfire.png", 100, BLACK)
tornadoMove = Shuriken(person1.rect.x+150, person1.rect.y-50, "CptImages/tornadoMove.png", 150, 150, BLACK)
fireball = Cannonball(person1.rect.x+155, person1.rect.y+7, "CptImages/fireball.png", 35, 35, BLACK)
fireball.changespeed(0, 0, 180)
tornadoMoveHitbox = Shuriken(person1.rect.x+212, person1.rect.y-50, "CptImages/Black.png", 30, 150, BLACK)

# add the sprites to scene 1
allSpritesGroup1.add(person1)
allSpritesGroup1.add(playerHitbox)
allSpritesGroup1.add(npc1)
allSpritesGroup1.add(npc1Hitbox)
npcSpritesGroup1.add(npc1Hitbox)
shuriken1SpritesGroup.add(shuriken1)
shuriken2SpritesGroup.add(shuriken2)
shuriken3SpritesGroup.add(shuriken3)

# create the sprites for scene 2
person2 = Player(130, 50, "CptImages/mazePlayer.png", 50, 50, BLACK)
person2.changespeedDown(0, 0, 90)
generator = Generator(610, 470, "CptImages/generator.png", 50, 80, GREY)
charger1 = Generator(610, 50, "CptImages/charger.png", 50, 50, GREY)
charger2 = Generator(135, 500, "CptImages/charger.png", 50, 50, GREY)
charger3 = Generator(70, 100, "CptImages/charger.png", 50, 50, GREY)
tornadoObject = Powerup(670, 100, "CptImages/tornado.gif", 50, 50, BLACK)
text1 = font.render("Connect the 3 chargers to the generators to revive your tank!",True,WHITE)

# 1 version of Horizontal Barrier
barrier1 = Barrier(125, 40, "CptImages/Black.png", 550, 2, BLACK)
barrier4 = Barrier(60, 100, "CptImages/Black.png", 75, 2, BLACK)
barrier7 = Barrier(400, 143, "CptImages/Black.png", 140, 2, BLACK)
barrier17 = Barrier(130, 142, "CptImages/Black.png", 100, 2, BLACK)
barrier18 = Barrier(600, 140, "CptImages/Black.png", 60, 2, BLACK)
barrier21 = Barrier(330, 340, "CptImages/Black.png", 335, 2, BLACK)
barrier22 = Barrier(130, 340, "CptImages/Black.png", 100, 2, BLACK)
barrier24 = Barrier(60, 245, "CptImages/Black.png", 75, 2, BLACK)
barrier25 = Barrier(660, 95, "CptImages/Black.png", 90, 2, BLACK)
barrier26 = Barrier(60, 440, "CptImages/Black.png", 75, 2, BLACK)
barrier27 = Barrier(200, 440, "CptImages/Black.png", 140, 2, BLACK)
barrier28 = Barrier(465, 440, "CptImages/Black.png", 135, 2, BLACK)
barrier29 = Barrier(660, 440, "CptImages/Black.png", 100, 2, BLACK)
barrier30 = Barrier(260, 245, "CptImages/Black.png", 140, 2, BLACK)
barrier31 = Barrier(660, 245, "CptImages/Black.png", 75, 2, BLACK)

# 2 version of Horizontal Barrier
barrier5 = Barrier(130, 95, "CptImages/Black.png", 138, 2, BLACK)
barrier6 = Barrier(400, 95, "CptImages/Black.png", 270, 2, BLACK)
barrier19 = Barrier(130, 300, "CptImages/Black.png", 100, 2, BLACK)
barrier20 = Barrier(330, 310, "CptImages/Black.png", 335, 2, BLACK)
barrier23 = Barrier(60, 200, "CptImages/Black.png", 75, 2, BLACK)
barrier32 = Barrier(250, 210, "CptImages/Black.png", 150, 2, BLACK)
barrier33 = Barrier(660, 200, "CptImages/Black.png", 75, 2, BLACK)
barrier34 = Barrier(60, 400, "CptImages/Black.png", 75, 2, BLACK)
barrier35 = Barrier(270, 400, "CptImages/Black.png", 75, 2, BLACK)
barrier36 = Barrier(470, 400, "CptImages/Black.png", 75, 2, BLACK)
barrier37 = Barrier(660, 400, "CptImages/Black.png", 75, 2, BLACK)
barrier38 = Barrier(60, 500, "CptImages/Black.png", 75, 2, BLACK)
barrier39 = Barrier(340, 500, "CptImages/Black.png", 120, 2, BLACK)
barrier40 = Barrier(660, 500, "CptImages/Black.png", 75, 2, BLACK)
barrier54 = Barrier(125, 550, "CptImages/Black.png", 550, 2, BLACK)

# 1 version of Vertical Barrier
barrier2 = Barrier(140, 40, "CptImages/Black.png", 2, 60, BLACK)
barrier10 = Barrier(660, 95, "CptImages/Black.png", 2, 40, BLACK)
barrier11 = Barrier(60, 90, "CptImages/Black.png", 2, 415, BLACK)
barrier12 = Barrier(130, 95, "CptImages/Black.png", 2, 40, BLACK)
barrier13 = Barrier(258, 95, "CptImages/Black.png", 2, 310, BLACK)
barrier41 = Barrier(130, 200, "CptImages/Black.png", 2, 40, BLACK)
barrier42 = Barrier(130, 400, "CptImages/Black.png", 2, 40, BLACK)
barrier43 = Barrier(400, 200, "CptImages/Black.png", 2, 40, BLACK)
barrier44 = Barrier(330, 400, "CptImages/Black.png", 2, 40, BLACK)
barrier45 = Barrier(460, 500, "CptImages/Black.png", 2, 40, BLACK)
barrier46 = Barrier(130, 500, "CptImages/Black.png", 2, 40, BLACK)
barrier53 = Barrier(660, 300, "CptImages/Black.png", 2, 40, BLACK)

# 2 version of Vertical Barrier
barrier3 = Barrier(665, 50, "CptImages/Black.png", 2, 90, BLACK)
barrier8 = Barrier(395, 95, "CptImages/Black.png", 2, 40, BLACK)
barrier9 = Barrier(730, 95, "CptImages/Black.png", 2, 410, BLACK)
barrier14 = Barrier(530, 95, "CptImages/Black.png", 2, 310, BLACK)
barrier15 = Barrier(200, 140, "CptImages/Black.png", 2, 310, BLACK)
barrier16 = Barrier(590, 140, "CptImages/Black.png", 2, 310, BLACK)
barrier47 = Barrier(130, 300, "CptImages/Black.png", 2, 40, BLACK)
barrier48 = Barrier(330, 300, "CptImages/Black.png", 2, 40, BLACK)
barrier49 = Barrier(470, 400, "CptImages/Black.png", 2, 45, BLACK)
barrier50 = Barrier(330, 500, "CptImages/Black.png", 2, 40, BLACK)
barrier51 = Barrier(660, 200, "CptImages/Black.png", 2, 40, BLACK)
barrier52 = Barrier(660, 400, "CptImages/Black.png", 2, 40, BLACK)
barrier55 = Barrier(660, 500, "CptImages/Black.png", 2, 50, BLACK)

# add all of the barriers to scene 2
allSpritesGroup2.add(barrier1)
allSpritesGroup2.add(barrier2)
allSpritesGroup2.add(barrier3)
allSpritesGroup2.add(barrier4)
allSpritesGroup2.add(barrier5)
allSpritesGroup2.add(barrier6)
allSpritesGroup2.add(barrier7)
allSpritesGroup2.add(barrier8)
allSpritesGroup2.add(barrier9)
allSpritesGroup2.add(barrier10)
allSpritesGroup2.add(barrier11)
allSpritesGroup2.add(barrier12)
allSpritesGroup2.add(barrier13)
allSpritesGroup2.add(barrier14)
allSpritesGroup2.add(barrier15)
allSpritesGroup2.add(barrier16)
allSpritesGroup2.add(barrier17)
allSpritesGroup2.add(barrier18)
allSpritesGroup2.add(barrier19)
allSpritesGroup2.add(barrier20)
allSpritesGroup2.add(barrier21)
allSpritesGroup2.add(barrier22)
allSpritesGroup2.add(barrier23)
allSpritesGroup2.add(barrier24)
allSpritesGroup2.add(barrier25)
allSpritesGroup2.add(barrier26)
allSpritesGroup2.add(barrier27)
allSpritesGroup2.add(barrier28)
allSpritesGroup2.add(barrier29)
allSpritesGroup2.add(barrier30)
allSpritesGroup2.add(barrier31)
allSpritesGroup2.add(barrier32)
allSpritesGroup2.add(barrier33)
allSpritesGroup2.add(barrier34)
allSpritesGroup2.add(barrier35)
allSpritesGroup2.add(barrier36)
allSpritesGroup2.add(barrier37)
allSpritesGroup2.add(barrier38)
allSpritesGroup2.add(barrier39)
allSpritesGroup2.add(barrier40)
allSpritesGroup2.add(barrier41)
allSpritesGroup2.add(barrier42)
allSpritesGroup2.add(barrier43)
allSpritesGroup2.add(barrier44)
allSpritesGroup2.add(barrier45)
allSpritesGroup2.add(barrier46)
allSpritesGroup2.add(barrier47)
allSpritesGroup2.add(barrier48)
allSpritesGroup2.add(barrier49)
allSpritesGroup2.add(barrier50)
allSpritesGroup2.add(barrier51)
allSpritesGroup2.add(barrier52)
allSpritesGroup2.add(barrier53)
allSpritesGroup2.add(barrier54)
allSpritesGroup2.add(barrier55)

# 1 version of Horizontal Barrier
barrierHorizontal1SpritesGroup.add(barrier1)
barrierHorizontal1SpritesGroup.add(barrier4)
barrierHorizontal1SpritesGroup.add(barrier7)
barrierHorizontal1SpritesGroup.add(barrier17)
barrierHorizontal1SpritesGroup.add(barrier18)
barrierHorizontal1SpritesGroup.add(barrier21)
barrierHorizontal1SpritesGroup.add(barrier22)
barrierHorizontal1SpritesGroup.add(barrier24)
barrierHorizontal1SpritesGroup.add(barrier25)
barrierHorizontal1SpritesGroup.add(barrier26)
barrierHorizontal1SpritesGroup.add(barrier27)
barrierHorizontal1SpritesGroup.add(barrier28)
barrierHorizontal1SpritesGroup.add(barrier29)
barrierHorizontal1SpritesGroup.add(barrier30)
barrierHorizontal1SpritesGroup.add(barrier31)

# 2 version of Horizontal Barrier
barrierHorizontal2SpritesGroup.add(barrier5)
barrierHorizontal2SpritesGroup.add(barrier6)
barrierHorizontal2SpritesGroup.add(barrier19)
barrierHorizontal2SpritesGroup.add(barrier20)
barrierHorizontal2SpritesGroup.add(barrier23)
barrierHorizontal2SpritesGroup.add(barrier32)
barrierHorizontal2SpritesGroup.add(barrier33)
barrierHorizontal2SpritesGroup.add(barrier34)
barrierHorizontal2SpritesGroup.add(barrier35)
barrierHorizontal2SpritesGroup.add(barrier36)
barrierHorizontal2SpritesGroup.add(barrier37)
barrierHorizontal2SpritesGroup.add(barrier38)
barrierHorizontal2SpritesGroup.add(barrier39)
barrierHorizontal2SpritesGroup.add(barrier40)
barrierHorizontal2SpritesGroup.add(barrier54)

# 1 version of Vertical Barrier
barrierVertical1SpritesGroup.add(barrier2)
barrierVertical1SpritesGroup.add(barrier10)
barrierVertical1SpritesGroup.add(barrier11)
barrierVertical1SpritesGroup.add(barrier13)
barrierVertical1SpritesGroup.add(barrier16)
barrierVertical1SpritesGroup.add(barrier41)
barrierVertical1SpritesGroup.add(barrier42)
barrierVertical1SpritesGroup.add(barrier43)
barrierVertical1SpritesGroup.add(barrier44)
barrierVertical1SpritesGroup.add(barrier45)
barrierVertical1SpritesGroup.add(barrier46)
barrierVertical1SpritesGroup.add(barrier53)

# 2 version of Vertical Barrier
barrierVertical2SpritesGroup.add(barrier3)
barrierVertical2SpritesGroup.add(barrier8)
barrierVertical2SpritesGroup.add(barrier9)
barrierVertical2SpritesGroup.add(barrier12)
barrierVertical2SpritesGroup.add(barrier14)
barrierVertical2SpritesGroup.add(barrier15)
barrierVertical2SpritesGroup.add(barrier47)
barrierVertical2SpritesGroup.add(barrier48)
barrierVertical2SpritesGroup.add(barrier49)
barrierVertical2SpritesGroup.add(barrier50)
barrierVertical2SpritesGroup.add(barrier51)
barrierVertical2SpritesGroup.add(barrier52)
barrierVertical2SpritesGroup.add(barrier55)

# add the sprites to scene 2
allSpritesGroup2.add(person2)
allSpritesGroup2.add(generator)
allSpritesGroup2.add(charger1)
allSpritesGroup2.add(charger2)
allSpritesGroup2.add(charger3)
chargerSpritesGroup.add(charger1)
chargerSpritesGroup.add(charger2)
chargerSpritesGroup.add(charger3)
charger1SpritesGroup.add(charger1)
charger2SpritesGroup.add(charger2)
charger3SpritesGroup.add(charger3)

## Main Program Loop
while not done:
    ## CONTROL
    # Check for events

    for event in pygame.event.get():
        # User key commands
        if event.type == pygame.QUIT:
            done = True

        # allow scene 0 tank to move in all directions
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_a:
                person.changespeedDown(-4, 0, 180)
                person2.changespeedDown(-4, 0, 0)
            elif event.key == pygame.K_d:
                person.changespeedDown(4, 0, 0)
                person2.changespeedDown(4, 0, 0)
            elif event.key == pygame.K_w:
                person.changespeedDown(0, -4, 90)
                person2.changespeedDown(0, -4, 0)
            elif event.key == pygame.K_s:
                person.changespeedDown(0, 4, 270)
                person2.changespeedDown(0, 4, 0)

        # allow scene 1 tank to move left and right
            elif event.key == pygame.K_RIGHT:
                person1.changespeedUp(5, 0)
                playerHitbox.changespeedUp(5, 0)
            elif event.key == pygame.K_LEFT:
                person1.changespeedUp(-5, 0)
                playerHitbox.changespeedUp(-5, 0)

        # allow scene 0 tank to move in all directions
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_a:
                person.changespeedUp(4, 0)
                person2.changespeedDown(4, 0, 0)
            elif event.key == pygame.K_d:
                person.changespeedUp(-4, 0)
                person2.changespeedDown(-4, 0, 0)
            elif event.key == pygame.K_w:
                person.changespeedUp(0, 4)
                person2.changespeedDown(0, 4, 0)
            elif event.key == pygame.K_s:
                person.changespeedUp(0, -4)
                person2.changespeedDown(0, -4, 0)

          # allow scene 1 tank to move left and right
            elif event.key == pygame.K_RIGHT:
                person1.changespeedUp(-5, 0)
                playerHitbox.changespeedUp(-5, 0)
            elif event.key == pygame.K_LEFT:
                person1.changespeedUp(5, 0)
                playerHitbox.changespeedUp(5, 0)

          # make shoot variable 1 when user hits space on scene 1
            elif event.key == pygame.K_SPACE:
              if(scene == 1):
                 shoot = 1
                 cannonballSound.play()

            # make fire variable 1 when user hits f on scene 1
            elif event.key == pygame.K_f:
              if (scene == 1):
                fire = 1

          # make tornado variable 1 when use hits t when ability variable is 1
            elif event.key == pygame.K_t and ability == 1:
              tornado = 1
              tornadoSound.play()

    # create lists for collisions
    hitList = pygame.sprite.spritecollide(person, npcSpritesGroup, False)
    hitList1 = pygame.sprite.spritecollide(cannonball, npcSpritesGroup1, False)
    hitList2 = pygame.sprite.spritecollide(playerHitbox, shuriken1SpritesGroup, False)
    hitList3 = pygame.sprite.spritecollide(person2, chargerSpritesGroup, False)
    hitList4 = pygame.sprite.spritecollide(generator, charger1SpritesGroup, False)
    hitList5 = pygame.sprite.spritecollide(generator, charger2SpritesGroup, False)
    hitList6 = pygame.sprite.spritecollide(generator, charger3SpritesGroup, False)
    hitList7 = pygame.sprite.spritecollide(person2, powerupSpritesGroup, False)
    hitList8 = pygame.sprite.spritecollide(tornadoMoveHitbox, npcSpritesGroup1, False)
    hitList9 = pygame.sprite.spritecollide(playerHitbox, shuriken2SpritesGroup, False)
    hitList10 = pygame.sprite.spritecollide(playerHitbox, shuriken3SpritesGroup, False)
    hitList11 = pygame.sprite.spritecollide(playerHitbox, hellfireSpritesGroup, False)
    hitList12 = pygame.sprite.spritecollide(fireball, npcSpritesGroup1, False)
    hitList13 = pygame.sprite.spritecollide(person2, barrierHorizontal1SpritesGroup, False)
    hitList14 = pygame.sprite.spritecollide(person2, barrierHorizontal2SpritesGroup, False)
    hitList15 = pygame.sprite.spritecollide(person2, barrierVertical1SpritesGroup, False)
    hitList16 = pygame.sprite.spritecollide(person2, barrierVertical2SpritesGroup, False)

    # if user powered the generator and resets the game
    if(reset == 1):
      person2.rect.x = 300
      person2.rect.y = 100
      stopper = "go"
      stopper1 = "go"
      stopper2 = "go"
      stopper3 = "go"
      stopper4 = "go"
      stopper5 = "go"
      stopper6 = "go"
      stopper7 = "go"
      stopper8 = "go"
      stopper9 = "go"
      stopper10 = "go"
      stopper11 = "go"
      stopper12 = "go"
      dontHit = 0
      fail = 0
      power1 = 0
      power2 = 0
      power3 = 0
      bossShoot1 = 1
      bossShoot2 = 1
      bossShoot3 = 1
      shoot = 0
      x1 = 230
      y1 = 570
      tornado = 0
      bossShoot1 == 1
      bossShoot2 == 1
      bossShoot3 == 1
      scene = 1
      reset = 0
      resetter = 1
      end = 0
      fire = 0
      reAdjust = 1
      hellShoot = 0
      shuriken1.rect.x = npc1.rect.x
      shuriken1.rect.y = npc1.rect.y+100
      shuriken2.rect.x = npc1.rect.x+25
      shuriken2.rect.y = npc1.rect.y+100
      shuriken3.rect.x = npc1.rect.x+50
      shuriken3.rect.y = npc1.rect.y+100
      hellfire.rect.x = portal.rect.x
      hellfire.rect.y = -50

    ## SCENE 0
    if (scene == 0):

      # check if the tank hits the npc to start the minigame
      for npc in hitList:
        scene = 1

      # set background for scene 0
      background = pygame.image.load("CptImages/gameStart.jpg").convert()
      background = pygame.transform.scale(background, (800, 600))
      screen.blit(background, [0, 0])

      # set instructions texts for player to understand the game
      text10 = font.render("Tank Stars", True, YELLOW)
      screen.blit(text10, [380, 20])
      text3 = font.render("Instructions:",True,RED)
      screen.blit(text3, [0, 50])
      text4 = font.render("Use left and right arrow to control your fighter tank. Use spacebar to shoot a cannonball at the enemy.",True,WHITE)
      screen.blit(text4, [0, 100])
      text10 = font.render("You can also use the 'f' key to shoot a fireball!",True,WHITE)
      screen.blit(text10, [0, 125])
      text5 = font.render("If you die, dont worry. You can revive by using your character to power on the generator. Use WASD to control",True,WHITE)
      screen.blit(text5, [0, 175])
      text6 = font.render("this character. Watch out, as soon as you power on the generators you are back into the action!",True,WHITE)
      screen.blit(text6, [0, 200])
      text7 = font.render("If you manage to find the secret tornado powerup, use the key 't' to use the tornado during battle.",True,WHITE)
      screen.blit(text7, [0, 250])
      text8 = font.render("When you are ready, use the tank and go up to the NPC (with WASD), you will be telported to the battlefield",True,WHITE)
      screen.blit(text8, [0, 300])
      text9 = font.render("Good Luck and Have Fun!",True,WHITE)
      screen.blit(text9, [0, 325])

      # update allSpritesGroup
      allSpritesGroup.update()

      # draw allSpritesGroup to the screen
      allSpritesGroup.draw(screen)

    ## SCENE 1
    if (scene == 1):

    # call scene1 function
      scene1(text)

    # dont let player go out of bounds
      if person1.rect.x > 650:
        person1.rect.x = 650
        playerHitbox.rect.x = 640
      elif person1.rect.x < 0:
        person1.rect.x = 0
        playerHitbox.rect.x = 10

    # check if shurikens hit
      for shuriken1 in hitList2:
        if(resetter == 0):
          scene = 2
          end = 1
          allSpritesGroup2.add(generator)
          allSpritesGroup2.add(charger1)
          allSpritesGroup2.add(charger2)
          allSpritesGroup2.add(charger3)
          chargerSpritesGroup.add(charger1)
          chargerSpritesGroup.add(charger2)
          chargerSpritesGroup.add(charger3)
          charger1SpritesGroup.add(charger1)
          charger2SpritesGroup.add(charger2)
          charger3SpritesGroup.add(charger3)
          allSpritesGroup.remove(shuriken1)
          reAdd = 1

      for shuriken2 in hitList9:
        if(resetter == 0):
          scene = 2
          end = 1
          allSpritesGroup2.add(generator)
          allSpritesGroup2.add(charger1)
          allSpritesGroup2.add(charger2)
          allSpritesGroup2.add(charger3)
          chargerSpritesGroup.add(charger1)
          chargerSpritesGroup.add(charger2)
          chargerSpritesGroup.add(charger3)
          charger1SpritesGroup.add(charger1)
          charger2SpritesGroup.add(charger2)
          charger3SpritesGroup.add(charger3)
          allSpritesGroup.remove(shuriken2)
          reAdd = 1

      for shuriken3 in hitList10:
        if(resetter == 0):
          scene = 2
          end = 1
          allSpritesGroup2.add(generator)
          allSpritesGroup2.add(charger1)
          allSpritesGroup2.add(charger2)
          allSpritesGroup2.add(charger3)
          chargerSpritesGroup.add(charger1)
          chargerSpritesGroup.add(charger2)
          chargerSpritesGroup.add(charger3)
          charger1SpritesGroup.add(charger1)
          charger2SpritesGroup.add(charger2)
          charger3SpritesGroup.add(charger3)
          allSpritesGroup.remove(shuriken3)
          reAdd = 1

      resetter = 0

      # if shurikens have not hit yet
      if (end != 1):

        # add shurikens to sprite list
        allSpritesGroup1.add(shuriken1)
        allSpritesGroup1.add(shuriken2)
        allSpritesGroup1.add(shuriken3)

        if (bossShoot1 == 0 and bossShoot2 == 0 and bossShoot3 == 0):

        # make sure shurikens do not go out of bounds
          if (shuriken1.rect.x > 800 or shuriken1.rect.x < 0):
              shuriken1.rect.x = npc1.rect.x
              shuriken1.rect.y = npc1.rect.y+100
              bossShoot1 = 1
              hellShoot = 1
              stopper11 = "go"
          if (shuriken1.rect.y > 700 or shuriken1.rect.y < -100):
              shuriken1.rect.x = npc1.rect.x
              shuriken1.rect.y = npc1.rect.y+100
              bossShoot1 = 1
              hellShoot = 1
              stopper11 = "go"
          if (shuriken2.rect.x > 800 or shuriken2.rect.x < 0):
              shuriken2.rect.x = npc1.rect.x+25
              shuriken2.rect.y = npc1.rect.y+100
              bossShoot2 = 1
          if (shuriken2.rect.y > 600 or shuriken2.rect.y < 0):
              shuriken2.rect.x = npc1.rect.x+25
              shuriken2.rect.y = npc1.rect.y+100
              bossShoot2 = 1
          if (shuriken3.rect.x > 800 or shuriken3.rect.x < 0):
              shuriken3.rect.x = npc1.rect.x+50
              shuriken3.rect.y = npc1.rect.y+100
              bossShoot3 = 1
          if (shuriken3.rect.y > 600 or shuriken3.rect.y < 0):
              shuriken3.rect.x = npc1.rect.x+50
              shuriken3.rect.y = npc1.rect.y+100
              bossShoot3 = 1

        # make shurikens move
        if (bossShoot1 == 1):
          shuriken1.changespeed(random.randrange(-4,4),random.randrange(1,5),90)
          bossShoot1 = 0
        if (bossShoot2 == 1):
          shuriken2.changespeed(-random.randrange(-4,4),random.randrange(1,5),90)
          bossShoot2 = 0
        if (bossShoot3 == 1):
          shuriken3.changespeed(-random.randrange(-4,4),random.randrange(1,5),90)
          bossShoot3 = 0

        # make npc1 move back and forth
        if(stopper3 != "stop"):
          npc1.changespeed(4, 0)
          npc1Hitbox.changespeed(4, 0)

        # make sure npc1 does not go out of bounds
        if npc1.rect.x > 700:
          npc1.changespeed(-4, 0)
          npc1Hitbox.changespeed(-4, 0)
          stopper3 = "stop"
        elif npc1.rect.x < -50:
          npc1.changespeed(4, 0)
          npc1Hitbox.changespeed(4, 0)

        # if the user has not shot the fireball yet
        if (fire == 0):
          fireball.rect.x = person1.rect.x + 155
          fireball.rect.y = person1.rect.y + 7

        # if the user has shot the fireball
        if (fire == 1):
          allSpritesGroup1.add(fireball)
          fireball.changespeed(10, -4, 0)

          if (fireball.rect.x > 800 or fireball.rect.y < 0):
            fire = 0
            fireball.changespeed(0, 0, 0)
            fireball.rect.x = person1.rect.x + 155
            fireball.rect.y = person1.rect.y + 7

        # if fireball hits the enemy
        for npc1Hitbox in hitList12:
          if (fire == 1 and healthbar.x5 >= 30 and healthbar.x4 >= 20 and healthbar.x6 >= 30):
            healthbar.x6 = healthbar.x6 - 7
            healthbar.x5 = healthbar.x5 - 7
            healthbar.x4 = healthbar.x4 - 7
            health = health - 7
            fireball.changespeed(0, 0, 0)
            fireball.rect.x = person1.rect.x + 155
            fireball.rect.y = person1.rect.y + 7
            fire = 0

        # if the user has not shot yet
        if (shoot == 0):
          cannonball.rect.x = person1.rect.x + 155
          cannonball.rect.y = person1.rect.y + 7

        # if the user shot the cannonball
        if (shoot == 1):
          allSpritesGroup1.add(cannonball)
          cannonball.changespeed(10, -4, 90)

          # if cannonball goes out of bounds
          if (cannonball.rect.x > 800 or cannonball.rect.y < 0):
            shoot = 0
            cannonball.changespeed(0, 0, 0)
            cannonball.rect.x = person1.rect.x + 155
            cannonball.rect.y = person1.rect.y + 7

        # if cannonball hits the enemy
        for npc1Hitbox in hitList1:
          if (shoot == 1 and healthbar.x5 >= 30 and healthbar.x4 >= 20 and healthbar.x6 >= 30):
            healthbar.x6 = healthbar.x6 - 5
            healthbar.x5 = healthbar.x5 - 5
            healthbar.x4 = healthbar.x4 - 5
            health = health - 5
            cannonball.changespeed(0, 0, 0)
            cannonball.rect.x = person1.rect.x + 155
            cannonball.rect.y = person1.rect.y + 7
            shoot = 0

        # use tornado move
        if (tornado == 1):
          if (stopper8 == "go"):
            tornadoMove.rect.x = person1.rect.x + 150
            tornadoMoveHitbox.rect.x = person1.rect.x + 212
          allSpritesGroup1.add(tornadoMove)
          allSpritesGroup1.add(tornadoMoveHitbox)
          tornadoMove.changespeed(6, 0, 0)
          tornadoMoveHitbox.changespeed(6, 0, 0)
          dontHit = 0
          stopper8 = "stop"

        # check if tornado hits npc
        for npc1Hitbox in hitList8:
          if (dontHit != 1):
            healthbar.x6 = healthbar.x6 - 15
            healthbar.x5 = healthbar.x5 - 15
            healthbar.x4 = healthbar.x4 - 15
            health = health - 15
            allSpritesGroup1.remove(tornadoMove)
            allSpritesGroup1.remove(tornadoMoveHitbox)
            tornadoMove.changespeed(0, 0, 0)
            tornadoMoveHitbox.changespeed(0, 0, 0)
            tornadoMove.rect.x = person1.rect.x + 150
            tornadoMoveHitbox.rect.x = person1.rect.x + 212
            tornado = 0
            dontHit = 1
            stopper8 = "go"
            npc1.changespeed(4, 0)
            npc1Hitbox.changespeed(4, 0)

        # check if tornado goes out of bounds
        if (tornadoMove.rect.x > 800):
          allSpritesGroup1.remove(tornadoMove)
          allSpritesGroup1.remove(tornadoMoveHitbox)
          tornadoMove.changespeed(0, 0, 0)
          tornadoMoveHitbox.changespeed(0, 0, 0)
          tornadoMove.rect.x = person1.rect.x + 150
          tornadoMoveHitbox.rect.x = person1.rect.x + 212
          tornado = 0
          dontHit = 1
          stopper8 = "go"

        # add portals and hellfire randomly to the map
        if (hellShoot == 1):
          if (stopper11 != "stop"):
            portal.rect.x = random.randrange(0,800)
            hellfire.rect.x = portal.rect.x
            hellfire.rect.y = -50
            stopper11 = "stop"
          hellfire.rect.y = hellfire.rect.y + 5
          allSpritesGroup1.add(portal)
          allSpritesGroup1.add(hellfire)
          hellfireSpritesGroup.add(hellfire)

          # check if hellfire goes out of bounds
          if (hellfire.rect.y == 600):
            allSpritesGroup1.remove(portal)
            allSpritesGroup1.remove(hellfire)
            hellShoot = 0

          # check for hellfire collisions
          for hellfire in hitList11:
            scene = 2
            end = 1
            allSpritesGroup2.add(generator)
            allSpritesGroup2.add(charger1)
            allSpritesGroup2.add(charger2)
            allSpritesGroup2.add(charger3)
            chargerSpritesGroup.add(charger1)
            chargerSpritesGroup.add(charger2)
            chargerSpritesGroup.add(charger3)
            charger1SpritesGroup.add(charger1)
            charger2SpritesGroup.add(charger2)
            charger3SpritesGroup.add(charger3)
            allSpritesGroup1.remove(hellfire)
            hellfireSpritesGroup.remove(hellfire)
            reAdd = 1

        # update the allSpritesGroup1
        allSpritesGroup1.update()

        # draw the healthbar
        healthbar.drawing(screen)

        # draw allSpritesGroup1 to the screen
        allSpritesGroup1.draw(screen)

    ## SCENE 2
    if (scene == 2):
      if (stopper9 == "go"):
        charger1.rect.x = 610
        charger1.rect.y = 50
        charger2.rect.x = 135
        charger2.rect.y = 500
        charger3.rect.x = 70
        charger3.rect.y = 100
        person2.rect.x = 300
        person2.rect.y = 100
        stopper9 = "stop"

      # call scene 2 subprogram
      scene2(text1, x1, y1)
      x1 = 230
      y1 = 570

      # rotate image properly to start
      person2.changespeedDown(0, 0, 90)

      # when user picks up powerup
      if (ability != 1):
        allSpritesGroup2.add(tornadoObject)
        powerupSpritesGroup.add(tornadoObject)
        for tornadoObject in hitList7:
          allSpritesGroup2.remove(tornadoObject)
          text1 = font.render("You have unlocked a new move for your tank!!",True,WHITE)
          x1 = 230
          ability = 1

      # make sure person2 cannot got out of bounds
      for barrier1 in hitList13:
        person2.rect.y = person2.rect.y + 4

      for barrier1 in hitList14:
        person2.rect.y = person2.rect.y - 4

      for barrier1 in hitList15:
        person2.rect.x = person2.rect.x + 4

      for barrier1 in hitList16:
        person2.rect.x = person2.rect.x - 4

      # when user picks up power units
      if(stopper10 == "go"):
        for charger1 in hitList3:
          charger1.rect.x = person2.rect.x + 30
          charger1.rect.y = person2.rect.y + 30
        for charger2 in hitList3:
          charger2.rect.x = person2.rect.x + 30
          charger2.rect.y = person2.rect.y + 30
        for charger3 in hitList3:
          charger3.rect.x = person2.rect.x + 30
          charger3.rect.y = person2.rect.y + 30
        stopper10 = "go"

      # when user adds to generator
      if (reAdd == 0):
        for charger1 in hitList4:
          allSpritesGroup2.remove(charger1)
          if (stopper5 != "stop"):
            power1 = 1
            stopper5 = "stop"

        for charger2 in hitList5:
          allSpritesGroup2.remove(charger2)
          if (stopper6 != "stop"):
            power2 = 1
            stopper6 = "stop"

        for charger3 in hitList6:
          allSpritesGroup2.remove(charger3)
          if (stopper7 != "stop"):
            power3 = 1
            stopper7 = "stop"

      reAdd = 0
      reAdjust = 0

      # update allSpritesGroup2
      allSpritesGroup2.update()

      # draw allSpritesGroup2 to the screen
      allSpritesGroup2.draw(screen)

    # if all the generators are turned on
    if (power1 == 1 and power2 == 1 and power3 == 1):
      reset = 1
      if (stopper12 != "stop"):
        lifeCounter = lifeCounter + 1
        stopper12 = "stop"

    # if the boss is killed
    if (health <= 0):
      if (stopper13 != "stop"):
        lifeCounter = lifeCounter + 1
        stopper13 = "stop"
      lifeCounter = str(lifeCounter)
      background2 = pygame.image.load("CptImages/gameOver.webp").convert()
      background2 = pygame.transform.scale(background2, (800, 600))

      # If user did not die once
      if (lifeCounter == "1"):
        background2 = pygame.image.load("CptImages/winner.jpeg").convert()
        background2 = pygame.transform.scale(background2, (800, 600))
      screen.blit(background2, [0, 0])
      text2 = font.render("It took you "+lifeCounter+" try! Can you do any better!?!?!?",True,RED)
      x2 = 235
      y2 = 400

      # If user did not die once
      if (lifeCounter == "1"):
        text2 = font.render("It took you "+lifeCounter+" try! Congrats! That is the best possible score!",True,WHITE)
        x2 = 215
        y2 = 400

      # If user only died once
      else:
        text2 = font.render("It took you "+lifeCounter+" tries! Can you do any better!?!?!?",True,RED)
        x2 = 235
        y2 = 400
      screen.blit(text2, [x2, y2])

    ## VIEW
    pygame.display.flip()
    clock.tick(60)

# Close the window and quit
pygame.quit()


# -*- coding: UTF-8 -*-
import os
from datetime import datetime
from service import chat_completion, dingtalk_service
from utils.conns import redis_cli
from sanic.log import logger
from conf.config import CONF

def process_message(question, message_id):
    """
    :param question:
    :param message_id:
    :return:
    """
    req_data = redis_cli.get(message_id)
    if not req_data:
        logger.error("消息ID[{}]没有找到缓存数据".format(message_id))
        return

    conversation_id = req_data['conversationId']
    answer = chat_completion(message=question,
                             conversation_id=conversation_id)
    # print("answer", answer)

    if CONF.QALOG:
        sender_staff_id = req_data['senderStaffId']
        log_file_dir = os.path.join(os.path.dirname(__name__), 'logs')
        if not os.path.exists(log_file_dir):
            os.makedirs(log_file_dir)
        log_file = os.path.join(log_file_dir, '{}.log'.format(sender_staff_id))

        create_at = datetime.fromtimestamp(req_data['createAt'] / 1000)
        question_record = '{} {}[{}] 提问: {}\n'.format(create_at.isoformat(),
                                               req_data['senderNick'],
                                                sender_staff_id,
                                               question)
        answer_record = '{} chatGPT[{}] 回答: {}\n'.format(datetime.now().isoformat(),
                                                    CONF.OPENAI_MODEL,
                                                    answer)

        with open(log_file,'a') as fp:
            fp.write(question_record)
            fp.write(answer_record)

    dingtalk_service.webhook_url = req_data['sessionWebhook']
    dingtalk_service.send_text_message(message=answer)

    # sender_staff_id = 'manager7045'
    # dingtalk_service.send_markdown_message(message=answer,title='Test',
    #                                        user_id=sender_staff_id)


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os
import numpy as np
from PIL import Image

RGB_WEIGHTS = [0.2989, 0.5870, 0.1140]

def bytes_to_c_arr(data, lowercase=True):
    return [format(b, '#04x' if lowercase else '#04X') for b in data]

if __name__ == "__main__":
    # get script path
    script_path = os.path.dirname(os.path.realpath(__file__))
    output_path = script_path + '/output-header/'

    # remove output dir if exist and create a fresh one
    if os.path.exists(output_path):
        os.system('rm -rf {}'.format(output_path))
    os.mkdir(output_path)

    # read input image
    with open(sys.argv[1], 'rb') as f:
        input_image = Image.open(f).convert('RGB')

    # read the file and store as a raw image
    raw_image888_array = np.asarray(input_image)

    # convert image to RGB565
    R5 = (raw_image888_array[...,0]>>3).astype(np.uint16) << 11
    G6 = (raw_image888_array[...,1]>>2).astype(np.uint16) << 5
    B5 = (raw_image888_array[...,2]>>3).astype(np.uint16)
    raw_image565_array = R5 | G6 | B5

    # convert image to grayscale
    raw_imageBW_array = np.dot(raw_image888_array[...,:3], RGB_WEIGHTS).astype(np.uint16)

    # uncomment to dump converted images to output
    # raw_image888 = Image.fromarray(raw_image888_array)
    # raw_image888.convert('RGB').save(output_path + 'rgb888.jpg')
    # PIL library is not supporting RGB565 format
    # raw_image565 = Image.fromarray(raw_image565_array)
    # raw_image565.save(output_path + 'rgb565.jpg')
    # raw_imageBW = Image.fromarray(raw_imageBW_array)
    # raw_imageBW.convert('RGB').save(output_path + 'bw.jpg')

    # dump all arrays into C-style header file
    file_path = output_path + "images_raw.h"
    with open(file_path, "w") as f:
        # get filename from full path
        filename = file_path.split('/')[-1]
        f.write("#ifndef {}\n".format(filename.upper().replace('.', '_')))
        f.write("#define {}\n".format(filename.upper().replace('.', '_')))
        f.write("\n")
        f.write("#include <stdint.h>\n")
        f.write("\n")
        f.write("const size_t image_width = {};\n".format(input_image.width))
        f.write("const size_t image_height = {};\n".format(input_image.height))
        f.write("\n")
        f.write("const size_t image_888_raw_size = {};\n".format(raw_image888_array.size))
        f.write("const uint8_t image_888_raw[] = {\n")
        f.write(", ".join(bytes_to_c_arr(raw_image888_array.tobytes())))
        f.write("\n};\n")
        f.write("\n")
        f.write("const size_t image_565_raw_size = {};\n".format(raw_image565_array.size * raw_image565_array.dtype.itemsize))
        f.write("const uint8_t image_565_raw[] = {\n")
        f.write(", ".join(bytes_to_c_arr(raw_image565_array.tobytes())))
        f.write("\n};\n")
        f.write("\n")
        f.write("const size_t image_BW_raw_size = {};\n".format(raw_imageBW_array.size * raw_imageBW_array.dtype.itemsize))
        f.write("const uint8_t image_BW_raw[] = {\n")
        f.write(", ".join(bytes_to_c_arr(raw_imageBW_array.tobytes())))
        f.write("\n};\n")
        f.write("\n")
        f.write("#endif\n")
        f.close()


# search.py
# ---------
# Licensing Information:  You are free to use or extend these projects for
# educational purposes provided that (1) you do not distribute or publish
# solutions, (2) you retain this notice, and (3) you provide clear
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
#
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
# The core projects and autograders were primarily created by John DeNero
# (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu).
# Student side autograding was added by Brad Miller, Nick Hay, and
# Pieter Abbeel (pabbeel@cs.berkeley.edu).

"""
In search.py, you will implement generic search algorithms which are called by
Pacman agents (in searchAgents.py).
"""

import util

class SearchProblem:
    """
    This class outlines the structure of a search problem, but doesn't implement
    any of the methods (in object-oriented terminology: an abstract class).

    You do not need to change anything in this class, ever.
    """

    def getStartState(self):
        """
        Returns the start state for the search problem.
        """
        util.raiseNotDefined()

    def isGoalState(self, state):
        """
          state: Search state

        Returns True if and only if the state is a valid goal state.
        """
        util.raiseNotDefined()

    def getSuccessors(self, state):
        """
          state: Search state

        For a given state, this should return a list of triples, (successor,
        action, stepCost), where 'successor' is a successor to the current
        state, 'action' is the action required to get there, and 'stepCost' is
        the incremental cost of expanding to that successor.
        """
        util.raiseNotDefined()

    def getCostOfActions(self, actions):
        """
         actions: A list of actions to take

        This method returns the total cost of a particular sequence of actions.
        The sequence must be composed of legal moves.
        """
        util.raiseNotDefined()

def tinyMazeSearch(problem):
    """
    Returns a sequence of moves that solves tinyMaze.  For any other maze, the
    sequence of moves will be incorrect, so only use this for tinyMaze.
    """
    from game import Directions
    s = Directions.SOUTH
    w = Directions.WEST
    return  [s, s, w, s, w, w, s, w]

def depthFirstSearch(problem):
    """
    Search the deepest nodes in the search tree first.

    Your search algorithm needs to return a list of actions that reaches the
    goal. Make sure to implement a graph search algorithm.

    To get started, you might want to try some of these simple commands to
    understand the search problem that is being passed in:

    print("Start:", problem.getStartState())
    print("Is the start a goal?", problem.isGoalState(problem.getStartState()))
    print("Start's successors:", problem.getSuccessors(problem.getStartState()))
    """
    "*** YOUR CODE HERE ***"
    from util import Stack

    visited = set()                                             # set to keep track of visited nodes
    fringe = Stack()                                            # stack used as fringe for keeping pairs of (node, path-to-node)
    fringe.push((problem.getStartState(), []))                  # push (Node, [path-from-start-to-node]) pair into the fringe
    curr_node, path_to_node = fringe.pop()

    while(not problem.isGoalState(curr_node)):                  # while not reached a goal state

        if curr_node not in visited:                            # if current node is not visited yet
            visited.add(curr_node)
            successors = problem.getSuccessors(curr_node)       # get the successors of the current node
            for successor in successors:                        # push each successor into the fringe
                child_node = successor[0]
                child_path = successor[1]
                # construct full path to child node
                full_path = path_to_node + [child_path]
                fringe.push((child_node, full_path))

        curr_node, path_to_node = fringe.pop()                  # get the next node from fringe

    return path_to_node                                         # return full path to goal node

def breadthFirstSearch(problem):
    """Search the shallowest nodes in the search tree first."""
    "*** YOUR CODE HERE ***"
    from util import Queue

    visited = set()                                        # set to keep track of visited nodes
    fringe = Queue()                                       # keeping pairs (node, path_to_node) for implementing the BFS
    fringe.push((problem.getStartState(), []))             # push the pair (start_state, []) into the fringe
    curr_node, path_to_node = fringe.pop()                 # get the current node from the fringe (queue)

    while(not problem.isGoalState(curr_node)):             # while not reached a goal state

        if curr_node not in visited:
            visited.add(curr_node)
            successors = problem.getSuccessors(curr_node)
            for successor in successors:
                child_node = successor[0]
                child_path = successor[1]
                full_path = path_to_node + [child_path]     # assign the full path from start to child
                fringe.push((child_node, full_path))        # push the pair (child_node, [full-path-to-child]) into the fringe

        curr_node, path_to_node = fringe.pop()

    return path_to_node                                     # return the full path to goal node

def uniformCostSearch(problem):
    """Search the node of least total cost first."""
    "*** YOUR CODE HERE ***"
    from util import PriorityQueue

    visited = set()                                             # set to keep track of visited nodes
    fringe = PriorityQueue()                                    # implementing the UCS with a PQ
    fringe.push((problem.getStartState(), [], 0), 0)            # pushing into PQ ((initial_state, path_to-initial-state = null, cost-of-this-path=0), priority of item=0)

    curr_node, path_to_node, cost_to_node = fringe.pop()

    while (not problem.isGoalState(curr_node)):                 # while not reached a goal state
        if curr_node not in visited:
            visited.add(curr_node)
            successors = problem.getSuccessors(curr_node)
            for successor in successors:
                child = successor[0]
                child_path = successor[1]
                child_cost = successor[2]
                full_path = path_to_node + [child_path]
                total_cost = cost_to_node + child_cost
                # priority of each node is the total_cost for reaching it
                fringe.push((child, full_path, total_cost), total_cost)
        curr_node, path_to_node, cost_to_node = fringe.pop()

    return path_to_node                                         # return the full path to goal node

def nullHeuristic(state, problem=None):
    """
    A heuristic function estimates the cost from the current state to the nearest
    goal in the provided SearchProblem.  This heuristic is trivial.
    """
    return 0

def aStarSearch(problem, heuristic=nullHeuristic):
    """Search the node that has the lowest combined cost and heuristic first."""
    "*** YOUR CODE HERE ***"
    from util import PriorityQueue

    visited = set()
    fringe = PriorityQueue()                                                                        # implementing the A* using PQ
    fringe.push((problem.getStartState(), [], 0), 0 + heuristic(problem.getStartState(), problem))  # push((current-node, path-to-current-node, path-cost), g(x)=g(x)+h(x))

    curr_node, path_to_node, cost_to_node = fringe.pop()

    while (not problem.isGoalState(curr_node)):                                                     # while not reached a goal state
        if curr_node not in visited:
            visited.add(curr_node)
            successors = problem.getSuccessors(curr_node)
            for successor in successors:
                child = successor[0]
                child_path = successor[1]
                child_cost = successor[2]
                full_path = path_to_node + [child_path]
                total_cost = cost_to_node + child_cost
                priority = total_cost + heuristic(child, problem)                                    # the priority of each item is the combined total_cost value + the heuristic's value
                fringe.push((child, full_path, total_cost), priority)                                # push the new (successor) node into the fringe
        curr_node, path_to_node, cost_to_node = fringe.pop()

    return path_to_node                                                                              # return the full path to goal node

# Abbreviations
bfs = breadthFirstSearch
dfs = depthFirstSearch
astar = aStarSearch
ucs = uniformCostSearch


#-*- coding: utf-8 -*-
import os
import os.path as osp
import glob
import codecs,shutil
import random

import  xml.dom.minidom
try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET
import cv2
import numpy as np
from tqdm import tqdm
from PIL import Image

#from dota2rotation import *
multil_type=['*.jpg','*.png','*.tif']
dataset_annotname=['ship']
dataset_annotname=['A','B','C','D','E','F','G','H','I','J','K']

def str2bool(v):
    return v.lower() in ("yes", "true", "t", "1")

def txt2polygons(txt_path):
    '''
    '''
    polygon_list = list()
    txt_file=codecs.open(txt_path,encoding='utf-8')

    for line in txt_file:
        #import pdb;pdb.set_trace()
        line=line.strip()
        row_list=line.split()
        polygon=[int(float(x)) for x in row_list[:8]]
        annot_name=row_list[-2]

        polygon_list.append((annot_name,polygon))

    txt_file.close()
    return polygon_list
def polygons2rotation(polygon_list):
    rotation_list=[]
    for polygon in polygon_list:
        class_name,points = polygon
        rect_array = np.array(points).reshape((-1, 2))
        rbbox = cv2.minAreaRect(rect_array)
        x, y, w, h, a = rbbox[0][0], rbbox[0][1], rbbox[1][0], rbbox[1][1], rbbox[2]
        #some case angle is not in (0,90]
        while not 0 < a <= 90:
            if a <= 0:
                a += 90
                w, h = h, w
            else:
                a -= 90
                w, h = h, w
        rotation_list.append((class_name,(x,y,w,h,a)))

    return rotation_list

def rotation2scale(rotation_list,imgsize=640,theta_max=90):
    scale_rotation_list=[]
    for rotation in rotation_list:
        class_name,rotation_parm=rotation
        # import pdb;pdb.set_trace()
        (x,y),(w,h),theta=rotation_parm

        x/=imgsize
        y/=imgsize

        w/=imgsize
        h/=imgsize

        theta/=theta_max
        # import pdb;pdb.set_trace()

        scale_rotation_list.append((class_name,(x,y,w,h,theta)))
    return scale_rotation_list

def scalepolygons2txt(save_path,scale_rotation_list):
    save_file = open(save_path, 'w', encoding='utf-8')
    for scale_rotation in scale_rotation_list:
        class_name,rotation_parm=scale_rotation

        clss_id=dataset_annotname.index(class_name)

        x,y,w,h,theta=rotation_parm

        save_file.write('{} {} {} {} {} {}\n'.format(clss_id,x,y,w,h,theta))

    save_file.close()
def vis_rotation_labels(img,scale_rotation_list,visname,theta_max=90):
    show_img=img.copy()
    H,W,C=img.shape
    for scale_rotaion in scale_rotation_list:
        # import pdb;pdb.set_trace()
        annot_name=scale_rotaion[0]
        xc,yc,w,h,theta=scale_rotaion[1]
        #import pdb;pdb.set_trace()
        xc*=W
        yc*=H
        w*=W
        h*=H
        theta*=theta_max
        # import pdb;pdb.set_trace()
        rect=((xc,yc),(w,h),theta)
        box = cv2.boxPoints(rect).reshape(-1,1,2)
        box = np.int0(box)
        cv2.polylines(show_img,[box],isClosed=True,color=(0,255,0),thickness=2)
    #import pdb;pdb.set_trace()
    cv2.imwrite(visname, show_img)
def get_grid_list(img, roi_size=(400, 400), overlap_size=(50, 50)):
    ''' Calculate the bounding edges of cropping grids
    return:' xmin xmax ymin ymax'
    '''
    img_h, img_w = img.shape[0:2]
    #import pdb;pdb.set_trace()
    row_crops = (img_h - overlap_size[1]) // (roi_size[1] - overlap_size[1])
    col_crops = (img_w - overlap_size[0]) // (roi_size[0] - overlap_size[0])

    grid_list = [] # ymin, ymax, xmin, xmax
    for i_iter in range(row_crops * col_crops):

        x_crop_idx = i_iter % col_crops   #行
        y_crop_idx = i_iter//col_crops

        xmin=x_crop_idx*(roi_size[0]-overlap_size[0])
        ymin=y_crop_idx*(roi_size[1]-overlap_size[1])
        xmax=xmin+roi_size[0]
        ymax=ymin+roi_size[1]

        grid_list.append((xmin,xmax,ymin,ymax))

    return grid_list

def polygons2rect(polygon_list):
    rect_list=[]
    for polygon in polygon_list:
        annot_name=polygon[0]
        rect=polygon[1:]
        rect_array=np.array(rect).reshape((-1,2))
       # import pdb;pdb.set_trace()
        xmin=rect_array[:,0].min()
        xmax=rect_array[:,0].max()
        ymin=rect_array[:,1].min()
        ymax=rect_array[:,1].max()

        rect_list.append((annot_name,(xmin,ymin,xmax,ymax)))

    return rect_list

def rect2txt(rect_list,out_path):
    out_file=open(out_path,'w',encoding='utf-8')
    for rect in rect_list:
        annot_name=rect[0]
        clss_id=dataset_annotname.index(annot_name)
        rect_area=rect[1] #xmin,ymin,xmax,ymax
        out_file.write('{} {} {} {} {}\n'.format(clss_id,rect_area[0],rect_area[1],rect_area[2],rect_area[3]))
    out_file.close()

def match_grid_polygon(cur_grid,polygon):
    '''
    if rect in grid then return True and the new rect in subpatch
    cur_grid: [xmin:xmax,ymin:ymax]
    rect :[xmin,ymin,xmax,ymax]
    return
    rect_in_grid : type bool
    new_rect : new rect position in subpatch
    '''
    annot_name=polygon[0]
    #import pdb;pdb.set_trace()
    x1,y1,x2,y2,x3,y3,x4,y4=polygon[1]

    rect=polygon[1:]
    rect_array=np.array(rect).reshape((-1,2))
    # import pdb;pdb.set_trace()
    xmin=rect_array[:,0].min()
    xmax=rect_array[:,0].max()
    ymin=rect_array[:,1].min()
    ymax=rect_array[:,1].max()

    grid_xmin,grid_xmax,grid_ymin,grid_ymax=cur_grid
    rect_in_grid=False
    new_polygon=None
    if xmax<grid_xmax and xmin>grid_xmin and ymax<grid_ymax and ymin >grid_ymin:
        new_polygon=[annot_name,[x1-grid_xmin,y1-grid_ymin,x2-grid_xmin,y2-grid_ymin,x3-grid_xmin,y3-grid_ymin,x4-grid_xmin,y4-grid_ymin]]
        rect_in_grid=True
    return rect_in_grid,new_polygon

def match_grid_polygonv2(cur_grid,polygon):
    '''
    if rect in grid then return True and the new rect in subpatch
    cur_grid: [xmin:xmax,ymin:ymax]
    rect :[xmin,ymin,xmax,ymax]
    return
    rect_in_grid : type bool
    new_rect : new rect position in subpatch
    '''
    annot_name=polygon[0]
    #import pdb;pdb.set_trace()
    x1,y1,x2,y2,x3,y3,x4,y4=polygon[1]

    rect=polygon[1:]
    rect_array=np.array(rect).reshape((-1,2))
    # import pdb;pdb.set_trace()
    xmin=rect_array[:,0].min()
    xmax=rect_array[:,0].max()
    ymin=rect_array[:,1].min()
    ymax=rect_array[:,1].max()
    xc=(xmin+xmax)/2
    yc=(ymin+ymax)/2

    grid_xmin,grid_xmax,grid_ymin,grid_ymax=cur_grid
    rect_in_grid=False
    new_polygon=None
    if xc<grid_xmax and xc>grid_xmin and yc<grid_ymax and yc >grid_ymin:
        new_polygon=[annot_name,[x1-grid_xmin,y1-grid_ymin,x2-grid_xmin,y2-grid_ymin,x3-grid_xmin,y3-grid_ymin,x4-grid_xmin,y4-grid_ymin]]
        rect_in_grid=True
    return rect_in_grid,new_polygon

def vis_labels(img,rect_list,visname):
    show_img=img.copy()
    for rect in rect_list:
        # import pdb;pdb.set_trace()
        annot_name=rect[0]
        x1,y1,x2,y2=rect[1]
        #import pdb;pdb.set_trace()
        w=abs(x2-x1)
        h=abs(y2-y1)
        cv2.rectangle(show_img,(x1,y1),(x2,y2),(0,0,255),5)
        w_h='{}*{}'.format(w,h)
        cv2.putText(show_img,w_h,(x1-2,y1-2),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),2)
    #import pdb;pdb.set_trace()
    cv2.imwrite(visname, show_img)

def rect2scale(rect,cur_shape):
    Height,Width=cur_shape
    annot_name,rect_points=rect
    x1,y1,x2,y2=rect_points

    xc=(x1+x2)/2
    yc=(y1+y2)/2

    width=abs(x2-x1)
    height=abs(y2-y1)
    # import pdb;pdb.set_trace()
    xc/=Width
    yc/=Height

    width/=Width
    height/=Height

    # import pdb;pdb.set_trace()

    return [annot_name,[xc,yc,width,height]]

def cutimage2detect(input_imgdir,input_annotdir,save_imgdir,save_annotdir,cut_size=512,overlap=50,save_nage_ratio=0.1,vis=True,show_origin=False):
    '''
    input_imgdir: images
    input_annotdir: txt format annots
    save_dir:output dataset dir
            ./train.txt
            ./test.txt
            ./images
            ./labels
    '''
    # target_lable_dir=os.path.join(save_dir,'labelDota')

    target_image_dir=save_imgdir
    target_lable_dir=save_annotdir
    dirname=osp.dirname(osp.dirname(target_image_dir))
    # import pdb;pdb.set_trace()

    dirs=[target_image_dir,target_lable_dir]

    for sub_dir in dirs:
        if osp.exists(sub_dir):
            shutil.rmtree(sub_dir)
            os.makedirs(sub_dir)
        else:
            os.makedirs(sub_dir)
    if vis:
        vis_dir=osp.join(dirname,'vis_labels')
        if not osp.exists(vis_dir):
            os.mkdir(vis_dir)
        else:
            shutil.rmtree(sub_dir)
            os.mkdir(sub_dir)
    # if not os.path.exists(target_labletrain_dir):
    #     os.makedirs(target_labletrain_dir)
    # if not os.path.exists(target_imagetrain_dir):
    #     os.makedirs(target_imagetrain_dir)

    # if not os.path.exists(target_imagetest_dir):
    #     os.makedirs(target_imagetest_dir)
    # if not os.path.exists(target_labletest_dir):
    #     os.makedirs(target_labletest_dir)
    #show origin labels
    imglist=[]
    for imgtype in multil_type:
        imglist+=glob.glob(osp.join(input_imgdir,imgtype))

    assert len(imglist) >0,'imgdir is empty please check your image directory path!'
    save_img_nums=0
    print(f'total ori images : {len(imglist)}')
    for imgpath in tqdm(imglist):

        basename,suffix=osp.splitext(osp.basename(imgpath))
        annotpath=osp.join(input_annotdir,'{}.txt'.format(basename))

        img=cv2.imread(imgpath)

        polygon_list=txt2polygons(annotpath)

        # rect_list=polygons2rect(polygon_list)

        grid_list=get_grid_list(img,(cut_size,cut_size),(overlap,overlap))

        #
        for i,cur_grid in enumerate(grid_list):
            exsit_object=False
            new_polygonlist=[]
            #match rect withe grid
            for polygon in polygon_list:
                rect_in_grid,new_polygon=match_grid_polygonv2(cur_grid,polygon)
                if rect_in_grid:
                    exsit_object=True
                    # scale_new_polygon=rect2scale(new_polygon,(cut_size,cut_size))
                    new_polygonlist.append(new_polygon)
                   #import pdb;pdb.set_trace()
            if exsit_object:
                # save_name=basename+'{}'.format(i)
                rotation_list=polygons2rotation(new_polygonlist)
                # import pdb;pdb.set_trace()
                scale_rotation_list= rotation2scale(rotation_list,imgsize=cut_size)

                save_labelname=osp.join(target_lable_dir,'{}_{}.txt'.format(basename,i))
                save_imgname=osp.join(target_image_dir,'{}_{}.jpg'.format(basename,i))
                save_img=img[cur_grid[2]:cur_grid[3],cur_grid[0]:cur_grid[1],:]

                #print('save image {}'.format(save_imgname))
                scalepolygons2txt(save_labelname,scale_rotation_list)

                cv2.imwrite(save_imgname,save_img)

                if vis:
                    vis_name=osp.join(vis_dir,'{}_{}.jpg'.format(basename,i))
                    vis_rotation_labels(save_img,scale_rotation_list,vis_name,theta_max=90)
                save_img_nums+=1
            else:
                #continue
                #print('save img ',i)
                if random.random()>save_nage_ratio:
                    continue
                save_imgname=osp.join(target_image_dir,'{}_{}.jpg'.format(basename,i))
                save_img=img[cur_grid[2]:cur_grid[3],cur_grid[0]:cur_grid[1],:]
                cv2.imwrite(save_imgname,save_img)
                save_img_nums+=1
    print(f'total images: {save_img_nums} !')
    #split_traintest(target_image_dir,save_dir)

if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser(description="cut remote images into small patches ")

    parser.add_argument('--input_imgdir',help="directory of input images")
    parser.add_argument('--input_annotdir',help="directory of the txt format annot")
    parser.add_argument('--saveimg_dir',help="directory of images output")
    parser.add_argument('--saveannot_dir',help="directory of annot output")
    parser.add_argument('--save_nage_ratio',type=float,help="save nagetive samples ratio of total nagetive samples")
    parser.add_argument('--vis_label',default=False,type=str2bool,help="show origin labels")
    parser.add_argument('--resplit',default=False,type=str2bool,help="do not cut the dataset ,but only resplit train and testdata")
    parser.add_argument('-c','--cut_size',type=int,default=512,help="cut patch sizes")
    parser.add_argument('-l','--overlap',type=int,default=50,help="overlap for cut")

    args = parser.parse_args()
    # if osp.exists(args.save_dir):
    #     shutil.rmtree(args.save_dir)
    # else:
    #only resplit the dataset,do not recut
    if args.resplit:
        target_image_dir=os.path.join(args.save_dir,'image')
        #split_traintest(target_image_dir,args.save_dir)
    else:
        cutimage2detect(args.input_imgdir,args.input_annotdir,args.saveimg_dir,args.saveannot_dir,args.cut_size,args.overlap,args.save_nage_ratio,args.vis_label)


# Breadth First Search

graph = {"5": ["3", "7"], "3": ["2", "4"], "7": ["8"], "2": [], "4": ["8"], "8": []}

# This uses a list when a queue would be better
def bfs(graph, node):
    visited = []
    queue = []
    visited.append(node)
    queue.append(node)

    while queue:
        print(queue[0])
        for node in graph[queue[0]]:
            if node not in visited:
                queue.append(node)
                visited.append(node)
        queue.pop(0)
    return visited

from collections import deque

# distance to check how long
def bfs_queue(graph, start):
    visited = []
    queue = deque([(start, 0)])

    while queue:
        node, dist = queue.popleft()
        if node in visited:
            continue

        visited.append(node)
        for node in graph[node]:
            queue.append((node, dist + 1))

    return visited, node, dist

print(bfs_queue(graph, "3"))


from xivo_dao.resources.call_permission import dao as call_permission_dao
from xivo_dao.resources.group import dao as group_dao

from .resource import GroupCallPermissionAssociation
from .service import build_service

class Plugin:
    def load(self, dependencies):
        api = dependencies['api']
        service = build_service()

        api.add_resource(
            GroupCallPermissionAssociation,
            '/groups/<int:group_uuid>/callpermissions/<int:call_permission_id>',
            '/groups/<uuid:group_uuid>/callpermissions/<int:call_permission_id>',
            endpoint='group_call_permissions',
            resource_class_args=(service, group_dao, call_permission_dao),
        )