import subprocess
import os
import shutil
from torchvision.io import read_image
import sys

def main():

    # num_files = get_num_files()
    # num_files = ('010', '01')
    num_files = (sys.argv[1], sys.argv[2])
    main_dir = os.getcwd()
    print()

    if not os.path.exists('google-landmark'):
        os.makedirs('google-landmark')
        subprocess.run(['git', 'clone', 'https://github.com/cvdfoundation/google-landmark.git'])
        print('google-landmark directory created\n')

    train_path = os.path.join('google-landmark', 'train')
    os.makedirs(train_path, exist_ok=True)
    test_path = os.path.join('google-landmark', 'test')
    os.makedirs(test_path, exist_ok=True)
    print('Train and Test directories created under google-landmark\n')

    script_path = os.path.join('..', 'download-dataset.sh')  # Path for the download script

    os.chdir(train_path)
    subprocess.run(['bash', script_path, 'train', num_files[0]])
    print('Downloaded training images\n')
    os.chdir(main_dir)  # Go back

    os.chdir(test_path)
    subprocess.run(['bash', script_path, 'test', num_files[1]])
    print('Downloaded test images\n')
    os.chdir(main_dir)

    # Since the train and test folders contain images within many sub-folders,
    # Get all images and move them to a single level folder...
    train_path_new = os.path.join('google-landmark', 'train_new')
    os.makedirs(train_path_new, exist_ok=True)
    test_path_new = os.path.join('google-landmark', 'test_new')
    os.makedirs(test_path_new, exist_ok=True)

    print(f"Relocated {relocate_jpg_files(train_path, train_path_new)} training files.")
    print(f"Relocated {relocate_jpg_files(test_path, test_path_new)} testing files.\n")

    # Remove the original folders
    subprocess.run(['rm', '-rf', train_path])
    print(f'Removed {train_path}')
    subprocess.run(['rm', '-rf', test_path])
    print(f'Removed {test_path}\n')

    # Keep only those images which have a 600*800 size...
    # Get a list of paths for each images
    train = [os.path.join(train_path_new, file) for file in os.listdir(train_path_new)]
    test = [os.path.join(test_path_new, file) for file in os.listdir(test_path_new)]

    # Create dictionaries to store the image paths and their corresponding shapes
    train_dict = get_shape_paths(train, desired_shape=(3, 600, 800))
    test_dict = get_shape_paths(test, desired_shape=(3, 600, 800))

    # Move these files to the original folders
    os.makedirs(train_path)
    os.makedirs(test_path)
    print(f"Relocated {relocate_from_dict(train_dict, train_path)} training images of size 600 * 800")
    print(f"Relocated {relocate_from_dict(test_dict, test_path)} testing images of size 600 * 800\n")

    # Remove the new folders
    subprocess.run(['rm', '-rf', train_path_new])
    print(f'Removed {train_path_new}')
    subprocess.run(['rm', '-rf', test_path_new])
    print(f'Removed {test_path_new}')

def get_num_files():
    """
    Return the number of TAR files to download for training and testing by the user's input
    :return: tuple for the number of training files and the number of testing files.
    """
    train_num = input('Enter the number training TAR files to fetch (000-499): ')
    test_num = input('Enter the number testing TAR files to fetch (00-19): ')

    return str(train_num), str(test_num)

def relocate_jpg_files(src, dst, move=True):
    """
    Move or Cpy images from the `src` directory to the `dst` directory.
    This is done recursively, meaning that the function will move (or copy) all files in all subdirectories of `src`.
    :param src: The source directory
    :param dst: The final directory
    :param move: Whether to move files (True) or copy them (False)
    :return: The number of files moved
    """
    num = 0
    for root, dirs, files in os.walk(src):
        for file in files:
            if file.lower().endswith('.jpg'):
                src_file = os.path.join(root, file)
                dst_file = os.path.join(dst, file)

                if move:
                    shutil.move(src_file, dst_file)
                else:
                    shutil.copy(src_file, dst_file)
                num += 1

    return num

def get_shape_paths(paths, desired_shape):
    """
    Given a list of image paths, return a dictionary of the path (key) and its shape (value).
    Only return those images which have the `desired_shape`.
    Using shape() from torchvision.io.read_image
    :param paths: A list of image paths
    :param desired_shape: A tuple for the desired shape
    :return: dict of paths to their shapes
    """
    paths_dict = {}

    for path in paths:
        # Read the image and get its shape
        shape = read_image(path).shape

        # Check if the shape matches the desired shape
        if shape == desired_shape:
            # Store the shape in the dictionary with the image path as the key
            paths_dict[path] = shape

    return paths_dict

def relocate_from_dict(img_dict, new_path, move=True):
    """
    Relocate files given in the keys of `img_dict` to the `new_path`.
    :param img_dict: dict for the images (as keys)
    :param new_path: str for the new path for those images
    :param move: bool for whether to move (True) or copy (False)
    :return: int indicating the number of files moved
    """
    num = 0
    for path in img_dict.keys():
        file = os.path.split(path)[1]

        if move:
            shutil.move(path, os.path.join(new_path, file))
        else:
            shutil.copy(path, os.path.join(new_path, file))

        num += 1
    return num

if __name__ == '__main__':
    main()


"""
A collection of rolling-apply functions that operates on two Series instead of
a DataFrame. The two Series must have identical index. The (aggregate) function
to be applied should take these two Series as input.
"""

import numpy as np
import pandas as pd

def rolling_apply_pd(
    s1: pd.Series,
    s2: pd.Series,
    window: int,
    func,
    *args):
    """
    Apply an aggregate function to each rolling subset and return the function
    output in a series.

    This function uses pandas, which is slow and memory-non-efficent. Not
    suitable for large datasets.

    Parameters
    ----------
    s1, s2 : pandas Series
        Often are two returns series.
    window : int
        Number of periods to roll back.
    func : callable (aggregate) function to apply to each rolling subframe.
    *args
        Additional arguments for the callable function.
    """

    res = pd.Series(np.nan, index=s1.index)
    for i in range(1, len(s1)+1):
        # get a subsample, excluding the ith index since iloc starts at 0
        ss1 = s1.iloc[max(i-window, 0):i]
        ss2 = s2.iloc[max(i-window, 0):i]
        if len(ss1) >= window:
            idx = s1.index[i-1]
            res[idx] = func(ss1, ss2, *args)
    return res

def rolling_apply_np(
    s1: pd.Series,
    s2: pd.Series,
    window: int,
    func,
    *args):
    """
    Apply an aggregate function to each rolling subset and return the function
    output in a series.

    This function uses numpy `ndarray` and `as_strided`, which are faster and
    memory-efficent. However, it can still be slow if the implementation of
    the aggregate function is slow; the aggregate function is the bottleneck.

    Parameters
    ----------
    s1, s2 : pandas Series
        Often are two returns series.
    window : int
        Number of periods to roll back.
    func : callable (aggregate) function to apply to each rolling subframe.
    *args
        Additional arguments for the callable function.
    """
    from numpy.lib.stride_tricks import as_strided

    result = np.ndarray(shape=s1.shape, dtype=float)
    l = len(s1)
    ls = s1.values.strides[0]
    result[0:window-1] = np.nan
    s1_arr = as_strided(s1.values, shape=(l - window + 1, window), strides=(ls, ls))
    s2_arr = as_strided(s2.values, shape=(l - window + 1, window), strides=(ls, ls))
    for row in range(window-1, l):
        result[row] = func(s1_arr[row - window+1], s2_arr[row - window+1], *args)
    return pd.Series(data=result, index=s1.index)


from django.urls import path
from . import views
urlpatterns = [
    path('',views.getRoutes,name="allRoutesAPI"),
    path('tasks',views.getTasks,name="allTasksAPI"),
    path('task/<str:pk>',views.getTask,name="getTaskAPI"),
    path('createTag',views.createTag,name="createTagAPI"),
    path('createTask',views.createTask,name="createTaskAPI"),
    path('updateTag/<str:pk>',views.updateTag,name="updateTagAPI"),
    path('updateTask/<str:pk>',views.updateTask,name="updateTaskAPI"),
    path('deleteTag/<str:pk>',views.deleteTag,name="deleteTagAPI"),
    path('deleteTask/<str:pk>',views.deleteTask,name="deleteTaskAPI"),
    path('tags',views.getTags,name="allTagsAPI"),
    path('tag/<str:pk>',views.getTag,name="getTagAPI"),
]


import re
import subprocess
import os
import sys

canvasSizePattern = re.compile(r'    \<canvas id=\'canvas\' width=\'(-?\d+(\.\d+)?)\' height=\'(-?\d+(\.\d+)?)\'\>\<\/canvas\>')
transformPattern = re.compile(r'\tctx.transform\(.+')
moveToPattern = re.compile(r'\tctx.moveTo\((-?\d+\.\d+), (-?\d+\.\d+)\);')
lineToPattern = re.compile(r'\tctx.lineTo\((-?\d+\.\d+), (-?\d+\.\d+)\);')
curveToPattern = re.compile(r'\tctx.bezierCurveTo\((-?\d+\.\d+), (-?\d+\.\d+), (-?\d+\.\d+), (-?\d+\.\d+), (-?\d+\.\d+), (-?\d+\.\d+)\);')

def convertToCanvas(svgFilepath):
	svgDirpath, svgFilename = os.path.split(svgFilepath)
	htmlDirpath = os.path.join(svgDirpath, 'html')
	htmlFilepath = os.path.join(htmlDirpath, svgFilename.replace('.svg', '.html'))
	os.makedirs(htmlDirpath, exist_ok=True)
	subprocess.run([
		'inkscape',
		svgFilepath,
		'-o',
		htmlFilepath,
	])
	return htmlFilepath

def cleanNum(numstr):
	outstr = str(float(numstr))
	if outstr.endswith('.0'):
		outstr = outstr[:-2]
	return outstr

def generatePath(filepath):
	path = []
	with open(filepath, 'r') as fin:
		for line in fin.readlines():
			line = line.rstrip()
			# print(line)
			m = canvasSizePattern.match(line)
			if m:
				# MPV's ASS alignment centering crops the path itself.
				# For the path to retain position in the SVG viewbox,
				# we need to "move" to the corners of the viewbox.
				cmd = 'm 0 0' # Top Left
				path.append(cmd)
				w = cleanNum(m.group(1))
				h = cleanNum(m.group(3))
				cmd = 'm {} {}'.format(w, h) # Bottom Right
				# print('size', cmd)
				path.append(cmd)
				continue
			m = transformPattern.match(line)
			if m:
				print("[error] filepath:", filepath)
				print("Cannot parse ctx.transform()")
				print("Please ungroup path to remove transormation")
				sys.exit(1)
			m = moveToPattern.match(line)
			if m:
				x = cleanNum(m.group(1))
				y = cleanNum(m.group(2))
				cmd = 'm {} {}'.format(x, y)
				# print('moveTo', cmd)
				path.append(cmd)
				continue
			m = lineToPattern.match(line)
			if m:
				x = cleanNum(m.group(1))
				y = cleanNum(m.group(2))
				cmd = 'l {} {}'.format(x, y)
				# print('lineTo', cmd)
				path.append(cmd)
				continue
			m = curveToPattern.match(line)
			if m:
				x1 = cleanNum(m.group(1))
				y1 = cleanNum(m.group(2))
				x2 = cleanNum(m.group(3))
				y2 = cleanNum(m.group(4))
				x = cleanNum(m.group(5))
				y = cleanNum(m.group(6))
				cmd = 'b {} {} {} {} {} {}'.format(x1, y1, x2, y2, x, y)
				# print('curveTo', cmd)
				path.append(cmd)
				continue
	return '   '.join(path)

def printIcon(name, htmlFilename):
	path = generatePath(htmlFilename)
	print(r'local ' + name + r' = "{\\p1}' + path + r'{\\p0}"')

def genIconPath(name, svgFilepath):
	htmlFilepath = convertToCanvas(svgFilepath)
	printIcon(name, htmlFilepath)

print('---- Icons')
print('-- 44x44')
genIconPath('tethysIcon_play', 'tethys_play.svg')
genIconPath('tethysIcon_pause', 'tethys_pause.svg')
genIconPath('mpvOsdIcon_close', 'mpv_osd_close.svg')
genIconPath('mpvOsdIcon_maximize', 'mpv_osd_maximize.svg')
genIconPath('mpvOsdIcon_minimize', 'mpv_osd_minimize.svg')
genIconPath('mpvOsdIcon_restore', 'mpv_osd_restore.svg')
print('-- 32x28')
genIconPath('mpvOsdIcon_fs_enter', 'mpv_osd_fs_enter.svg')
genIconPath('mpvOsdIcon_fs_exit', 'mpv_osd_fs_exit.svg')
print('-- 28x28')
genIconPath('tethysIcon_ch_prev', 'tethys_ch_prev.svg')
genIconPath('tethysIcon_ch_next', 'tethys_ch_next.svg')
genIconPath('tethysIcon_pip_enter', 'tethys_pip_enter.svg')
genIconPath('tethysIcon_pip_exit', 'tethys_pip_exit.svg')
genIconPath('tethysIcon_pl_prev', 'tethys_pl_prev.svg')
genIconPath('tethysIcon_pl_next', 'tethys_pl_next.svg')
genIconPath('tethysIcon_skipback', 'tethys_skipback.svg')
genIconPath('tethysIcon_skipfrwd', 'tethys_skipfrwd.svg')
genIconPath('tethysIcon_speed', 'tethys_speed.svg')
genIconPath('tethysIcon_vol_033', 'tethys_vol_033.svg')
genIconPath('tethysIcon_vol_066', 'tethys_vol_066.svg')
genIconPath('tethysIcon_vol_100', 'tethys_vol_100.svg')
genIconPath('tethysIcon_vol_101', 'tethys_vol_101.svg')
genIconPath('tethysIcon_vol_mute', 'tethys_vol_mute.svg')


import logging
from time import sleep

from munch import munchify
from spsdk.debuggers.debug_probe import DebugProbe
from spsdk.exceptions import SPSDKError

logger = logging.getLogger(__name__)

class DebugMailboxError(RuntimeError):
    """Class for DebugMailboxError."""

class DebugMailbox:
    """Class for DebugMailbox."""
    def __init__(self, debug_probe: DebugProbe, reset: bool = True, moredelay: float = 1.0) -> None:
        """Initialize DebugMailbox object."""
        # setup debug port / access point

        self.debug_probe = debug_probe
        self.reset = reset
        self.moredelay = moredelay
        # setup registers and register bitfields
        self.registers = REGISTERS

        # Proceed with initiation (Resynchronisation request)

        # The communication to the DM is initiated by the debugger.
        # It does so by writing the RESYNCH_REQ bit of the CSW (Control and Status Word)
        # register to 1. It then needs to reset the chip so that ROM code can observe
        # this request.
        # In order to reset the chip, the debugger can either pull the
        # reset line of the chip, or set the CHIP_RESET_REQ (This can be done at the
        # same time as setting the RESYNCH_REQ bit).

        logger.debug(f"No reset mode: {self.reset!r}")
        if self.reset:
            self.debug_probe.dbgmlbx_reg_write(
                addr=self.registers.CSW.address,
                data=self.registers.CSW.bits.RESYNCH_REQ |
                self.registers.CSW.bits.CHIP_RESET_REQ
            )

        # Acknowledgement of initiation

        # After performing the initiation, the debugger must readback the CSW register.
        # The DM will stall the debugger until the ROM code has serviced the resynchronization request.
        # The ROM does this by performing a soft reset of the DM block, thus resetting
        # the request bit/s which were set by the debugger.
        # Therefore, the debugger must read back 0x0 in CSW to know that the initiation
        # request has been serviced.

        if self.moredelay > 0.001:
            sleep(self.moredelay)

        ret = None
        retries = 20
        while ret is None or (ret & self.registers.CSW.bits.REQ_PENDING):
            try:
                ret = self.debug_probe.dbgmlbx_reg_read(addr=self.registers.CSW.address)
            except SPSDKError as e:
                retries -= 1
                if retries == 0:
                    retries = 20
                    raise IOError("TransferTimeoutError limit exceeded!")
                # if isinstance(e, TransferTimeoutError):
                sleep(0.05)

    def close(self) -> None:
        """Close session."""
        self.debug_probe.close()

    def spin_read(self, reg: int) -> int:
        """Read."""
        ret = None
        while ret is None:
            try:
                ret = self.debug_probe.dbgmlbx_reg_read(addr=reg)
            except SPSDKError as e:
                logger.error(str(e))
                logger.error(f"read exception  {reg:#08X}")
                sleep(0.01)
        return ret

    def spin_write(self, reg: int, value: int) -> None:
        """Write."""
        while True:
            try:
                self.debug_probe.dbgmlbx_reg_write(addr=reg, data=value)
                # wait for rom code to read the data
                while True:
                    ret = self.debug_probe.dbgmlbx_reg_read(addr=self.registers.CSW.address)
                    if (ret & self.registers.CSW.bits.REQ_PENDING) == 0:
                        break

                return
            except SPSDKError as e:
                logger.error(str(e))
                logger.error(f"write exception addr={reg:#08X}, val={value:#08X}")
                sleep(0.01)

REGISTERS = munchify({
    # Control and Status Word (CSW) is used to control
    # the Debug Mailbox communication
    'CSW': {
        'address': 0x00,
        'bits': {

            # Debugger will set this bit to 1 to request a resynchronrisation
            'RESYNCH_REQ': (1 << 0),

            # Request is pending from debugger (i.e unread value in REQUEST)
            'REQ_PENDING': (1 << 1),

            # Debugger overrun error
            # (previous REQUEST overwritten before being picked up by ROM)
            'DBG_OR_ERR': (1 << 2),

            # AHB overrun Error (Return value overwritten by ROM)
            'AHB_OR_ERR': (1 << 3),

            # Soft Reset for DM (write-only from AHB,
            # not readable and self-clearing).
            # A write to this bit will cause a soft reset for DM.
            'SOFT_RESET': (1 << 4),

            # Write only bit. Once written will cause the chip to reset
            # (note that the DM is not reset by this reset as it is
            #   only resettable by a SOFT reset or a POR/BOD event)
            'CHIP_RESET_REQ': (1 << 5),
        }
    },

    # Request register is used to send data from debugger to device
    'REQUEST': {
        'address': 0x04,
    },

    # Return register is used to send data from device to debugger
    # Note: Any read from debugger side will be stalled until new data is present.
    'RETURN': {
        'address': 0x08,
    },

    # IDR register is used to identify the access port
    'IDR': {
        'address': 0xFC,
        'expected': 0x002A0000,
    }
})


#Program to capture a video from a camera, filter is, and display it live on the screen
#Gerald Schuller, October 2014

import numpy as np
import scipy.signal
import cv2

#Downsample factor:
N=8

cap = cv2.VideoCapture(0)
[retval, frame] = cap.read()
[r,c,d]=frame.shape
print(r,c)
Dsy=np.zeros((r,c));

#2D-Mask to set to zero the 7/8 highest frequencies,
#only kep the 1/8 lowest frequencies in each direction:
M=np.ones((r,c))
M[(r/16.0):(r-r/16.0),(c/16.0):(c-c/16.0)]=np.zeros((7.0/8.0*r,7.0/8.0*c));
#Conmpute space-domain/inverse 2D Fourier transform of Low Pass filter:
h=np.abs(np.fft.ifft2(M))
hc=np.concatenate((h[:,(c/2):c],h[:,0:(c/2)]),axis=1)
hc=np.concatenate((hc[(r/2):r,:],hc[0:(r/2),:]))
#Only keep the part with the biggest components to reduce computations:
hc=hc[(r/2-4):(r/2+4),(c/2-4):(c/2+4)]
#filt=hc

#High pass filter kernel for edge detection:
#filt=np.matrix([[-1.0,-1.0,-1],[-1,8,-1],[-1,-1,-1]])/1.0;
#Low Pass Kernel:
#rectangular filter kernel:
filt1=np.ones((8,8))/8;
#Triangular filter kernel:
filt2=scipy.signal.convolve2d(filt1,filt1)/8

filteron=False;
#Filter type:
rectfilt=True;

while(True):
    # Capture frame-by-frame
    [ret, frame] = cap.read()
    Y=(0.114*frame[:,:,0]+0.587*frame[:,:,1]+0.299*frame[:,:,2])/256;
    cv2.imshow('Original Y Signal',Y)

    if rectfilt==True:
       filt=filt1;
    else:
       filt=filt2;

    if filteron == True:
       Y=scipy.signal.convolve2d(Y,filt,mode='same')
    #Downsample filtered frame:
    Dsy[0::N,::N]=Y[0::N,::N];
    # Display the resulting filtered frame
    #cv2.imshow('Y LP filtered, down- and upsampled',Dsy)
    #low pass filter the downsampled version to fill picture:

    if filteron==True:
       yfilt=scipy.signal.convolve2d(Dsy,filt,mode='same')
    else:
       yfilt=Dsy.copy()
    #print(Dsy[0:8,0:8])
    cv2.putText(yfilt,"Down - and upsampling and LP filtering Demo", (20,50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,128,128))
    cv2.putText(yfilt,"Toggle LP filter on/off: key f", (20,100), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,128,128))
    cv2.putText(yfilt,"Toggle rect and triang filt. kernel: key t", (20,150), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,128,128))
    cv2.putText(yfilt,"Quit: key q", (20,200), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,128,128))
    cv2.imshow('(LP filtered,) down- and upsampled (, and LP filtered)',yfilt)
    key=cv2.waitKey(1) & 0xFF;
    if key == ord('f'):
       filteron= not filteron;
    if key == ord('t'):
       rectfilt= not rectfilt;
    if key == ord('q'):
        break

# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()


import Radius_Basis_Function_Network
import sys
sys.path.append('D:\Machine-Learning\Random_Data_Generator')
import RandGen
import matplotlib.pyplot as plt
import numpy as np

Generator = RandGen.RandomDataGenerator()
DataList = Generator.GeneratePolyData(100,[1,-4,3,5],noise=0.1,normalNoise=True,center=20,radius=5)
#DataList = Generator.GenerateBinaryData(100,[1,-2,-3,1],Poly=True,noise=0.1)

test = Radius_Basis_Function_Network.RBF_Network(DataList,5,1,Kernel="Gaussian")

test.Train()

### Regression test
InputX = []
InputY = []
for i in DataList:
    InputX.append(i[0])
    InputY.append(i[1])
plt.plot(InputX,InputY,"bo")
testInput = [16,17,18,19,20,21,22,23,24]
for i in testInput:
    plt.plot([i],[test.Predict([i])],"ro")
###

### Classification test
# testInput = Generator.GenerateBinaryData(50,[1,-2,-3,1],Poly=True,noise=0)
# InputXp = []
# InputYp = []
# InputXn = []
# InputYn = []
# for i in DataList:
#     if i[-1]==1:
#         InputXp.append(i[0])
#         InputYp.append(i[1])
#     if i[-1]==-1:
#         InputXn.append(i[0])
#         InputYn.append(i[1])
#
# plt.plot(InputXp,InputYp,"bo")
# plt.plot(InputXn,InputYn,"ro")
#
# def plotresult(Input):
#     result = test.Predict(Input,type="Classification")
#     if result == 1:
#         plt.plot([i[0]],[i[1]],"go")
#     if result == -1:
#         plt.plot([i[0]],[i[1]],"yo")
#
# for i in testInput:
#     plotresult(i)
###

plt.show()


from matrixGenerator import createMatrix
import fileOperations as fo
import decoding as d
import numpy as np

def encodeFile(matrix):
    content = open('input.txt', 'r').read()
    print(content)
    contentBinary = fo.stringToByteArray(content)
    encoded = d.encodeToString(contentBinary, matrix)
    fo.saveStringToFile("encoded", encoded)

def decodeToFile(matrix):
    encodedWordLen = len(matrix) + 8
    encodedString = fo.loadStringFromFile("encoded")
    wordCount = int(len(encodedString)/encodedWordLen)

    encodedArray = []
    for i in range(wordCount):
        wordStr = encodedString[(encodedWordLen * i): ((i + 1) * encodedWordLen): 1]
        wordArray = []
        for j in range(encodedWordLen):
            wordArray.append(int(wordStr[j]))
        encodedArray.append(wordArray)

    decoded = []
    while encodedArray:
        encodedWord = d.dekodujSlowo(encodedArray.pop(0), matrix)
        decoded.append(encodedWord)

    decodedBytesString = ""
    while decoded:
        word = decoded.pop(0)
        wordStr = ""
        for char in word:
            wordStr += str(char)
        decodedBytesString += wordStr
    output = ''.join(chr(int(''.join(x), 2)) for x in zip(*[iter(decodedBytesString)]*8))
    fo.saveStringToFile("output", output)

# ----------------- PROBLEM JEDNEGO BLEDNEGO BITU -----------------

matrix = createMatrix(4, False)
# print(matrix)
# matrix = np.asarray([[1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0],
#                      [0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0],
#                      [1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0],
#                      [1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1]])
#
#
# encodeFile(matrix)
# decodeToFile(matrix)

# ----------------- PROBLEM DWOCH BLEDNYCH BITOW -----------------

# matrixTwoBadBits = createMatrix(4, True)
# matrixTwoBadBits = np.asarray([[0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0],
#                                [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0],
#                                [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0],
#                                [1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1]])
# print(matrixTwoBadBits)
# textStr = "hello"
# print(textStr)
# textBit = fo.stringToByteArray(textStr)
# print(textBit)
# textEncoded = d.zakodujSlowo(textBit, matrixTwoBadBits)


import smtplib
from pathlib import Path
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.utils import COMMASPACE
from email import encoders
from src.settings import EMAIL_CONFIG
from src.logger import Logger

logger = Logger(__name__)

def send_email(send_from, send_to, subject, body, signature, files=[]):
    msg = MIMEMultipart()
    msg['From'] = send_from
    msg['To'] = COMMASPACE.join(send_to)
    msg['Subject'] = subject

    msg.attach(MIMEText(body + signature, 'plain'))

    for path in files:
        part = MIMEBase('application', "octet-stream")
        with open(path, 'rb') as file:
            part.set_payload(file.read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition',
                        'attachment; filename={}'.format(Path(path).name))
        msg.attach(part)

    try:
        server = smtplib.SMTP(EMAIL_CONFIG["host"], EMAIL_CONFIG["port"])
        server.ehlo()
        server.starttls()
        server.login(EMAIL_CONFIG["user"], EMAIL_CONFIG["pass"])
        text = msg.as_string()
        server.sendmail(send_from, send_to, text)
        server.quit()
    except Exception as e:
        logger.exception(e)


import pymysql as db
import os
connection = db.connect('localhost','root','Sniper','prospect_encore_analysis')
cursor = connection.cursor()
import Admin

def add_new_prospect():
    propsId = input('Enter PropsId:')
    prospName = input('Enter ProspName:')
    prospPhone = input('Enter Phone:')
    prospAddress = input('Enter ProspAddress:')
    interestedModel = input('Enter Instrested Model:')
    intrestedColor = input('Enter Intrested Color:')
    dateOfVisit = input('Enter Date of Visit:')
    priority = input('Enter Priority: ')
    query = f"""insert into prospect values(
        {propsId},'{prospName}','{prospPhone}','{prospAddress}','{interestedModel}',
        '{intrestedColor}','{dateOfVisit}','{priority}'
    )
    """
    cursor.execute(query)
    connection.commit()

def change_password(username):
        userpass = input('Enter new userpass: ')
        query = f"""update employee set userPass = '{userpass}' where userName ='{username}' """
        cursor.execute(query)
        connection.commit()

def update_prospect():
    def update_name():
        prospId = input("Enter ProspId: ")
        prospName = input('Enter ProspName:')
        query = f"""update prospect set prospName = '{prospName}' where prospId = {prospId}"""
        cursor.execute(query)
        connection.commit()
    def update_Phone():
        prospId = input("Enter ProspId: ")
        prospPhone = input('Enter ProspPhone:')
        query = f"""update prospect set prospPhone = '{prospPhone}' where prospId = {prospId}"""
        cursor.execute(query)
        connection.commit()
    def update_Address():
        prospId = input("Enter ProspId: ")
        prospAddress = input('Enter ProspAddress:')
        query = f"""update prospect set prospAddress = '{prospAddress}' where prospId = {prospId}"""
        cursor.execute(query)
        connection.commit()
    def update_model():
        prospId = input("Enter ProspId: ")
        instrestedModel = input('Enter InstrestedModel:')
        query = f"""update prospect set intrestedmodel = '{instrestedModel}' where prospId = {prospId}"""
        cursor.execute(query)
        connection.commit()
    def update_color():
        prospId = input("Enter ProspId: ")
        instrestedColor = input('Enter InstrestedColor:')
        query = f"""update prospect set intrestedColor = '{instrestedColor}' where prospId = {prospId}"""
        cursor.execute(query)
        connection.commit()
    def update_date_of_visit():
        prospId = input('Enter ProspId:')
        dateOfVisit = input('Enter Date Of Visit:')
        query = f"""update prospect set intrestedColor = '{dateOfVisit}' where prospId = {prospId}"""
        cursor.execute(query)
        connection.commit()
    def update_priority():
        prospId = input('Enter ProspId:')
        priority = input('Enter Priority:')
        query = f"""update prospect set intrestedColor = '{priority}' where prospId = {prospId}"""
        cursor.execute(query)
        connection.commit()
    while True:
        os.system('cls')
        choice = int(input("""-------------------  MAIN MENU   ----------------------
        1) Update Name
        2) Update Phone
        3) Update Address
        4) Update Model
        5) Update Color
        6) Update Date Of Visit
        7) Update Priority
        8) Exit
        Enter your choice: """))

        if choice == 1:
            update_name()
        elif choice == 2:
            update_Phone()
        elif choice == 3:
            update_Address()
        elif choice == 4:
            update_model()
        elif choice == 5:
            update_color()
        elif choice == 6:
            update_date_of_visit()
        elif choice == 7:
            update_priority()
        elif choice == 8:
            break
        else:
            print("Invalid Choice")
        os.system("pause")

def mainmenu(username):
    while True:
        os.system('cls')
        choice = int(input("""-------------------  MAIN MENU   ----------------------
        1) Add New Prospect
        2) View All Prospect
        3) Update Prospect
        4) Change Password
        5) Search By Prospect
        6) SignOut
        Enter your choice:
        """))

        if choice == 1:
            add_new_prospect()
        elif choice == 2:
            Admin.view_all_propects()
        elif choice == 3:
            update_prospect()
        elif choice == 4:
            change_password(username)
        elif choice == 5:
            Admin.search_prospect()
        elif choice == 6:
            break
        else:
            print("Invalid Choice")
        os.system("pause")
    cursor.close()
    connection.close()


import torch
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel
import logging
from tqdm import tqdm

from ..misc import utils, Storage
from .loss_handler import LossHandler
from ...config.setting.recorder_setting import (
    TO_RECORD_LIST,
    TO_SAVE_LIST
)

class BaseTrainer:
    """
    ``BaseTrainer`` takes charge of training.

    ``storage`` holds all the intermediate variables such as ``data``, ``embeddings``, etc. All other modules, such as ``collectors``, ``selectors``, etc, access thier own parameter list to get the corresponding parameters.

    ``loss_handler`` will compute weighted loss.

    Args:
        batch_size (int):
            Batch size.
        freeze_trunk_batchnorm (bool):
            Whether to freeze batch normalization layer.
        dataset_num_workers (int):
            Number of process to load data.
        is_distributed (bool):
            Whether to use distibuted mode.
    """
    def __init__(
        self,
        batch_size,
        wrapper_params,
        freeze_trunk_batchnorm=False,
        dataset_num_workers=8,
        is_distributed=False,
    ):
        self.batch_size = batch_size
        self.freeze_trunk_batchnorm = freeze_trunk_batchnorm
        self.dataset_num_workers = dataset_num_workers
        self.is_distributed = is_distributed

        self.epochs = 0
        self.storage = Storage(wrapper_params)
        self.initiate_property()

    """
    Initialization
    """

    def initiate_property(self):
        # to save list (for recorders)
        setattr(
            self,
            TO_SAVE_LIST,
            ["models", "collectors", "losses"]
        )
        # recordable list
        self.recordable_object_list = ["models", "losses"]
        # trainable list
        self.trainable_object_list = [
            "models",
            "collectors"
        ]

    @property
    def models_forward_order(self):
        return ["trunk", "embedder"]

    """
    Set and Get
    """

    def set_batch_size(self, batch_size):
        self.batch_size = batch_size

    def set_distributed(self, flag=True):
        self.is_distributed = flag

    def set_activated_optims(self, optim_list=None):
        self.optim_list = (
            list(self.optimizers.keys())
            if optim_list is None
            else optim_list
        )
        logging.info("Set activated optims: {}".format(self.optim_list))

    """
    Train
    """

    def prepare(
        self,
        collectors,
        selectors,
        losses,
        models,
        optimizers,
        datasets,
        schedulers=None,
        gradclipper=None,
        samplers=None,
        collatefns=None,
        device=None,
        recorders=None
    ):
        """
        Load modules to prepare training.
        """
        # pass parameters
        self.collectors = collectors
        self.selectors = selectors
        self.losses = losses
        self.models = models
        self.optimizers = optimizers
        self.datasets = datasets
        self.schedulers = schedulers
        self.gradclipper = gradclipper
        self.samplers = samplers
        self.collatefns = collatefns
        self.device = device
        self.loss_handler = LossHandler()
        self.recorders = utils.get_default(recorders, "recorders")

        self.optim_list = list(self.optimizers.keys())

    def train(self, epochs=None):
        """
        Start training.

        Args:
            epochs (int):
                Epoch to start.
        """
        # start to train
        if epochs is not None:
            self.epochs = epochs
        self.initiate_dataloader()
        logging.info("TRAIN EPOCH: {}".format(self.epochs))
        self.pipeline()
        self.epochs += 1
        # self.release_memory()

    def pipeline(self):
        # use global collector
        self.use_global_collector()
        self.set_to_train()
        self.show_lr()

        # start training
        if dist.is_initialized():
            rank = dist.get_rank()
        else:
            rank = 0
        self.pbar = tqdm(range(self.iterations_per_epoch), disable=(rank!=0))
        for self.iteration in self.pbar:
            self.prepare_forward()
            self.forward_pipeline()
            self.backward_and_update()
            self.update_record(self.recorders)
            self.pbar.set_description(
                "Loss={:.4f}".format(
                    self.loss_handler.get_total(),
                )
            )
        self.loss_handler.average_losses()
        self.step_schedulers(metrics=self.loss_handler.get_total())

    def show_lr(self):
        default_idx = 0
        for k, v in self.optimizers.items():
            lr = v.param_groups[default_idx]["lr"]
            logging.info("{} optimizer's lr: {}".format(k, lr))

    def forward_pipeline(self):
        self.forward_models()
        self.forward_collectors()
        self.forward_selectors()
        self.forward_losses()

    def initiate_dataloader(self):
        logging.info(
            "{}: Initiating dataloader".format(
                self.__class__.__name__
            )
        )
        if hasattr(self.datasets["train"], "get_grouped_idx"):
            logging.info("resetting train dataset...")
            self.datasets["train"].get_grouped_idx()
            if dist.is_initialized():
                grouped_idx = self.datasets["train"].grouped_idx.cuda(self.device)
                dist.broadcast(grouped_idx, 0)
                self.datasets["train"].grouped_idx = grouped_idx.cpu()

        # more initialization
        self.sampler, self.collate_fn = None, None
        self._initiate_dataloader_sampler_collatefn()

        # get dataloader
        self.dataloader = torch.utils.data.DataLoader(
            dataset=self.datasets["train"],
            batch_size=int(self.batch_size),
            sampler=self.sampler,
            drop_last=True,
            pin_memory=True,
            shuffle=self.samplers is None,
            num_workers=self.dataset_num_workers,
            collate_fn=self.collate_fn
        )
        self.iterations_per_epoch = len(self.dataloader)
        self.dataloader_iter = iter(self.dataloader)

    def _initiate_dataloader_sampler_collatefn(self):
        # extract the sampler
        if self.is_distributed:
            if self.samplers is not None:
                logging.warn("class samplers can't be used in distribued training mode!")
            self.samplers = {}
            self.samplers["train"] = torch.utils.data.distributed.DistributedSampler(
                dataset=self.datasets["train"],
                shuffle=True,
            )
            self.sampler = self.samplers["train"]
            logging.info("Get distributed sampler {}".format(
                self.sampler.__class__.__name__,
            ))
        else:
            self.sampler = (
                self.samplers["train"]
                if self.samplers is not None
                else None
            )

        # extract the collate_fn
        self.collate_fn = (
            self.collatefns["train"]
            if self.collatefns is not None
            else None
        )

    def set_to_train(self):
        for trainable_name in self.trainable_object_list:
            trainable_object = getattr(self, trainable_name, None)
            if trainable_object is None:
                logging.warn(
                    "{} is not a member of trainer".format(
                        trainable_name
                    )
                )
            else:
                for v in trainable_object.values():
                    v.train()
        # maybe some parameters should be frozen
        self._maybe_freeze_batchnorm()

    def _maybe_freeze_batchnorm(self):
        if self.freeze_trunk_batchnorm:
            self.models["trunk"].apply(
                utils.set_layers_to_eval("BatchNorm")
            )

    def use_global_collector(self):
        for collector in self.collectors.values():
            if isinstance(collector, DistributedDataParallel):
                collector = collector.module
            if collector.is_global_collector:
                logging.info("Global collector updating...")
                collector.global_update(self)
                # self.release_memory()

    def prepare_forward(self):
        # set sampler
        self._prepare_forward_set_sampler()

        # zero loss-values
        self.loss_handler.zero_losses()

        # zero grad
        for v in self.models.values():
            v.zero_grad()
        for v in self.optimizers.values():
            v.zero_grad()

        # get batch
        self._prepare_forward_get_batch()

    def _prepare_forward_set_sampler(self):
        if self.is_distributed:
            self.sampler.set_epoch(self.epochs)

    @property
    def info_dict_to_device(self):
        return [
            "data",
            "labels"
        ]

    def _prepare_forward_get_batch(self):
        info_dict = next(self.dataloader_iter)
        # print(info_dict["data"].shape)
        # print(info_dict["labels"].shape)
        for key in info_dict.keys():
            setattr(self.storage, key, info_dict[key])
        self.storage.tensors_to_device(self.info_dict_to_device, self.device)

    def forward_models(self):
        # get data and labels
        data = self.storage.get("data")
        labels = self.storage.get("labels")

        # forward backbone (trunk model)
        features = self.models["trunk"](data)

        # if distributed
        if self.is_distributed:
            features, labels = utils.distributed_gather_objects(
                features, labels
            )
        # print(features.shape)
        # print(labels.shape)

        self.storage.features = features
        self.storage.labels = labels
        self.storage.indices_dict["models"] = {"embedder": {"": {}}}

        self.storage.update(self.models["embedder"], cur_module="models")

    def forward_collectors(self):
        # update collector
        self.update_collectors()
        # forward collector
        self.storage.update(self.collectors, cur_module="collectors")

    def update_collectors(self):
        for collector in self.collectors.values():
            if isinstance(collector, (torch.nn.DataParallel, torch.nn.parallel.DistributedDataParallel)):
                collector.module.update(self)
            else:
                collector.update(self)

    def forward_selectors(self):
        self.storage.update(self.selectors, cur_module="selectors")

    def forward_losses(self):
        value_dict = self.storage.update(self.losses, cur_module="losses")

        # update loss_values
        self.loss_handler.update_losses(self.storage.return_loss_dict())

    def backward_and_update(self):
        # self._backward_and_update_preprocess()

        self.loss_handler.backward()
        # clip gradients
        if self.gradclipper is not None:
            for v in self.gradclipper.values():
                v()
        # step optimizers
        for k in self.optim_list:
            self.optimizers[k].step()

    def _backward_and_update_preprocess(self):
        # for distributed
        if self.is_distributed:
            # dist.barrier()
            # dist.all_reduce(
            #     tensor=self.loss_values["total_loss"],
            #     op=dist.ReduceOp.SUM
            # )
            dist.all_reduce(
                tensor=self.loss_handler.loss_values["total"],
                op=dist.ReduceOp.SUM
            )

    def step_schedulers(self, **kwargs):
        """
        All schedulers step at the end of each epoch for the moment
        """
        if self.schedulers is not None:
            for k in self.optim_list:
                if k in self.schedulers:
                    v = self.schedulers[k]
                    if isinstance(v, torch.optim.lr_scheduler.ReduceLROnPlateau):
                        v.step(**kwargs)
                    else:
                        v.step()

    def update_record(self, recorders=None):
        if recorders is not None:
            total_iterations = (
                self.epochs * self.iterations_per_epoch
                + self.iteration
            )
            # update loss
            self.loss_handler.record_losses(recorders, total_iterations)

            # update other statistics
            for recordable_name in self.recordable_object_list:
                recordable_object = getattr(self, recordable_name)
                for k, v in recordable_object.items():
                    if k == "trunk": # TODO:
                        continue
                    to_record_obj = (
                        v.module
                        if isinstance(v, torch.nn.DataParallel) or isinstance(v, torch.nn.parallel.DistributedDataParallel)
                        else v
                    )
                    data, _ = recorders.get_data(to_record_obj, k)
                    recorders.update(data, total_iterations)

    def release_memory(self):
        torch.cuda.empty_cache()


#! /usr/bin/env python
#coding=utf-8
from django.db.models import Q
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, Http404
from django.shortcuts import render_to_response
from django.views.decorators.csrf import csrf_protect
from django.template import RequestContext
from .models import Album, Genre
from accounts.forms import AlbumForm
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

@csrf_protect
@login_required
def add(request):
    if request.method == 'POST':
        form = AlbumForm(request.POST, request.FILES)
        if form.is_valid():
            form.save(commit=False)
            Album.objects.create(
                title = form.cleaned_data['title'],
                description = form.cleaned_data['description'],
                pic = form.pic,
                for_sale = True,
                price = form.cleaned_data['price'],
                active = True,
                amount = form.cleaned_data['amount'],
                singer = form.cleaned_data['singer'],
                year = form.cleaned_data['year'],
                genre = form.cleaned_data['genre'],
                isbn = form.cleaned_data['isbn'],
                company = form.cleaned_data['company'],
                store = form.cleaned_data['store'])
            return HttpResponse('success')
    else:
        form = AlbumForm()
    return render_to_response('album_form.html', {
        'form': form,
        }, context_instance=RequestContext(request))

def genre(request, genre_name):
    try:
        genre = Genre.objects.get(name=genre_name)
        album_list = Album.objects.all().filter(genre=genre)
    except:
        album_list = None
    if  album_list:
        return render_to_response(
            'album_list.html',
            {
             'object_list': my_pageination(request, album_list),
             'genre': genre_name
             },
            context_instance=RequestContext(request))
    else:
        return render_to_response(
            'album_list.html',
            {
            'object_list': album_list,
            'genre': genre_name
            },
            context_instance=RequestContext(request))

def search(request, key_word=''):
    try:
        album_list = Album.objects.all().filter(Q(title__icontains=key_word)|
                                                Q(singer__icontains=key_word)|
                                                Q(description__icontains=key_word))
    except:
        album_list = None
    if album_list:
        return render_to_response('search_result.html', {
            'object_list': my_pageination(request, album_list),
            'key_word': key_word},
            context_instance=RequestContext(request))
    else:
        return render_to_response('search_result.html', {
            'object_list': album_list,
            'key_word': key_word},
            context_instance=RequestContext(request))

def indexview(request):
    album_list = Album.objects.all()
    return render_to_response(
        'album_list.html',
        {'object_list': my_pageination(request, album_list)},
        context_instance=RequestContext(request))

def albumdetailview(request, album_id):
    try:
        album = Album.objects.get(id=album_id)
    except Album.DoesNotExist:
        raise Http404
    buy = False # Flag determines that if a cusomer has bought this album before
    if '_auth_user_id' in request.session:
        user = request.user
        for order in user.order_set.all():
            try:
                order.orderdetail_set.get(album=album)
                buy = True
            except:
                pass
    return render_to_response('album_detail.html', {
        'album': album,
        'buy': buy,
        }, context_instance=RequestContext(request))

def my_pageination(request, object_list):
    paginator = Paginator(object_list, 10)
    page = request.GET.get('page')
    try:
        objects = paginator.page(page)
    except PageNotAnInteger:
    # If page is not an integer, deliver first page.
        objects = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        objects = paginator.page(paginator.num_pages)
    return objects