import json
import os.path

class ProfileManager:
    def __init__(self, file_path) -> None:
        self.file_path = file_path

        config = self.get_config()
        self.active_profile = config["active"]
        self.all_profiles = config["profiles"]
        self.activate_on = config["activate_on"]
        self.activate_on_arr = self.get_activate_on_arr()
        self.remaps = self.all_profiles.get(self.active_profile["profile"], {})

        self.cp_edit = False

        if self.get_win_startup():
            self.set_win_startup()

    def config_file_exists(self, path: str) -> bool:
        if os.path.isfile(path):
            return True
        else:
            return False

    def get_default_config(self) -> dict:
        return {
            "active": {
                "profile": "Default",
                "suppress": False
            },
            "profiles": {},
            "activate_on": {}
        }

    def get_config(self) -> dict:
        if self.config_file_exists(self.file_path):
            with open(self.file_path) as f:
                try:
                    data = json.load(f)
                except ValueError as e:
                    return self.get_default_config()

                test_active = data.get("active", False)
                if False != test_active:
                    test_profile = test_active.get("profile", False)
                    test_profiles = data.get("profiles", False)
                    test_activate = data.get("activate_on", False)

                    test_suppress = test_active.get("suppress", False)
                    data['active']['suppress'] = test_suppress

                    if False != test_profile and False != test_profiles:
                        active_profile_exists = False
                        for key in test_profiles:
                            if test_profile == key:
                                active_profile_exists = True

                        if False == active_profile_exists:
                            data["active"]["profile"] = "Default"

                        if False == test_activate:
                            data["activate_on"] = {}

                        return data

        return self.get_default_config()

    def json_write_to_file(self, file: str, data: dict) -> None:
        with open(file, 'w') as f:
            json.dump(data, f, indent=4)

    def save_active_profile_settings(self) -> None:
        data = self.get_config()
        data["active"] = self.active_profile
        self.json_write_to_file(self.file_path, data)

    def save_activate_on_profile_settings(self) -> None:
        data = self.get_config()
        data["activate_on"][self.active_profile["profile"]] = self.activate_on.get(self.active_profile["profile"], {})
        self.json_write_to_file(self.file_path, data)
        self.activate_on_arr = self.get_activate_on_arr()

    def save_profile(self) -> None:
        self.cp_edit = False
        data = self.get_config()
        data["active"] = self.active_profile
        data["profiles"][self.active_profile["profile"]] = self.remaps
        data["activate_on"][self.active_profile["profile"]] = self.activate_on.get(self.active_profile["profile"], {})
        self.json_write_to_file(self.file_path, data)

    def get_activate_on_arr(self) -> list:
        new_list = []
        if self.activate_on.get(self.active_profile["profile"], False):
            for key, path in self.activate_on[self.active_profile["profile"]].items():
                for ch in ["/", "\\"]:
                    if ch in path:
                        path = path.replace(ch, '')
                path = path.lower()
                path = path.strip()
                new_list.append(path)
        return new_list

    def change_active_profile(self, new_profile: str) -> None:
        self.cp_edit = False
        self.active_profile["profile"] = new_profile
        self.remaps = self.all_profiles.get(self.active_profile["profile"], {})
        self.activate_on_arr = self.get_activate_on_arr()

    def new_profile(self, new_profile_name: str, copy: bool = False) -> None:
        previous_profile = self.active_profile["profile"]
        self.active_profile["profile"] = new_profile_name

        if True == copy:
            self.all_profiles[new_profile_name] = self.remaps
            self.activate_on[new_profile_name] = self.activate_on.get(previous_profile, {})
        else:
            self.all_profiles[new_profile_name] = {}
            self.activate_on[new_profile_name] = {}
            self.remaps = {}

        self.activate_on_arr = self.get_activate_on_arr()
        self.save_profile()

    def delete_current_profile(self) -> None:
        data = self.get_config()
        data["active"]["profile"] = "Default"
        del data["profiles"][self.active_profile["profile"]]
        del data["activate_on"][self.active_profile["profile"]]
        self.activate_on_arr = self.get_activate_on_arr()

        self.json_write_to_file(self.file_path, data)

        self.activate_on.pop(self.active_profile["profile"], None)
        self.cp_edit = False
        self.active_profile["profile"] = "Default"
        self.remaps = {}

    def add_activate_on_item(self, filename: str) -> bool:
        item_added = False
        if '' != filename and filename not in self.activate_on[self.active_profile["profile"]].values():

            items = list(self.activate_on[self.active_profile["profile"]].keys())
            if 0 == len(items):
                new_key = 0
            else:
                new_key = int(items[-1]) + 1

            self.activate_on[self.active_profile["profile"]][str(new_key)] = filename
            self.save_activate_on_profile_settings()
            item_added = True

        return item_added

    def remove_activate_on_item(self, file_path: str) -> bool:
        item_removed = False
        if self.activate_on.get(self.active_profile["profile"], False):
            delete_key = -1
            for key, path in self.activate_on[self.active_profile["profile"]].items():
                if file_path == path:
                    delete_key = int(key)
                    break

            if delete_key >= 0:
                del self.activate_on[self.active_profile["profile"]][str(delete_key)]
                self.save_activate_on_profile_settings()
                item_removed = True

        return item_removed

    def get_fixed_activate_on(self) -> list:
        return self.activate_on_arr

    def get_remaps(self) -> dict:
        return self.remaps

    def get_suppress_lock_state(self) -> bool:
        return self.active_profile['suppress']

    def toggle_suppress_lock_key(self) -> bool:
        self.active_profile['suppress'] = not self.active_profile['suppress']
        self.save_active_profile_settings()
        return self.active_profile['suppress']

    def has_unsaved_changes(self) -> bool:
        unsaved_changes = False
        if True == self.cp_edit and self.active_profile["profile"] not in ["Default", "default"]:
            unsaved_changes = True

        return unsaved_changes

    def get_win_startup(self) -> bool:
        path = os.path.join('C:' + os.sep, 'Users', os.getlogin(), 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'capsmapper_start.bat')
        return os.path.exists(path)

    def set_win_startup(self) -> None:
        file_name = 'CapsMapper.exe'
        output = os.popen('wmic process get description, processid').read()
        output = output.splitlines()

        for line in output:
            line = ' '.join(line.split())

            if len(line) > 0 and line.startswith(file_name):
                path = os.popen('wmic process where "ProcessId=' + line.split()[1] + '" get ExecutablePath').read()
                for ch in ["b'ExecutablePath", "ExecutablePath", "\\r\\n", "\\" + file_name]:
                    if ch in path:
                        path = path.replace(ch, '')

                path = path[:-1]
                path = path.strip()

                file_path = os.path.join('C:' + os.sep, 'Users', os.getlogin(), 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'capsmapper_start.bat')

                with open(file_path, 'w') as f:
                    f.write(r'cd %s' % path)
                    f.write('\n')
                    f.write(r'start "" "%s"' % file_name)

                break

    def toggle_win_startup(self) -> bool:
        path = os.path.join('C:' + os.sep, 'Users', os.getlogin(), 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup', 'capsmapper_start.bat')
        is_set = self.get_win_startup()
        if is_set:
            os.remove(path)
        else:
            self.set_win_startup()


## Requires Library
import numpy as np

## Modified Vigenere's Table
def vigenerTable():
  table = np.zeros(shape=(10,10), dtype='uint64')

  for n in range(10):
    row = []
    for i in range(10):
      row.append((i+n)%10)
    table[n] = row

  return table

## Encryption
def encrypt(message, key):
  textLen = len(message)
  keyLen = len(key)

  table = vigenerTable()

  cipherText = ""
  for pos in range(textLen):

    colNum = int(message[pos])
    rowNum = int(key[pos % keyLen])

    codeChar = table[colNum, rowNum]
    if (pos+1)%2 == 0:
      codeChar = table[codeChar, rowNum]

    cipherText += str(codeChar)
  return cipherText

## Decryption
def decrypt(cipherText, key):
  textLen = len(cipherText)
  keyLen = len(key)

  table = vigenerTable()

  message = ""
  for pos in range(textLen):
    rowNum = int(key[pos % keyLen])
    colVal = int(cipherText[pos])
    row = list(table[rowNum])

    plain = row.index(colVal)
    if (pos+1)%2 == 0:
      plain = row.index(plain)

    message += str(plain)

  return message

######################################################
######################################################

## IMPLEMENTATION
plainText = str(pow(10,16))
key = '0433'

# Encryption
cipherText = encrypt(plainText, key)
print("After Enctryption: \n \
1. Plaintext: {}\n 2. Key: {} \n\n\
=> Ciphertext: {}".format(plainText, key, cipherText))

# Decryption
message = decrypt(cipherText, key)
print("\n\nAfter Dectryption: \n \
1. Ciphertext: {}\n 2. Key: {} \n\n\
=> Retrieved Plaintext: {}".format(cipherText, key, message))

print("\n"+"="*40)
# Varification
print("\nWhether actual & retrived messages are same?", end="  ")
print('Yes' if plainText == message else 'No')


from django.forms import ModelForm, ModelChoiceField, Form, Select, Textarea, ChoiceField, ModelMultipleChoiceField, \
    RadioSelect, inlineformset_factory, TextInput

from topics.models import Topic
from displays.models import Line, MyDisplayModel, Display

from accounts.models import Customer

class DisplayForm(ModelForm):
    class Meta:
        model = Display
        exclude = ('id', )

        font_size = ChoiceField(label='', choices=('1','2'), widget=Select(attrs={'class': 'regDropDown'}))
        widgets = {
            # 'font_size': Select(attrs={'class': 'form-group row form-control'}),
            'friendly_name': TextInput(attrs={'class': 'form-group row form-control'}),
        }

class LineForm(ModelForm):
    class Meta:
        model = Line
        exclude = ('id', )

        widgets = {
            'line': TextInput(attrs={'class': 'form-control', 'readonly': True}),
            'user_text': TextInput(attrs={'class': 'form-control'}),
            'topic': Select(attrs={'class': 'form-control'}),
        }

DisplayLineFormSet = inlineformset_factory(Display, Line, form=LineForm, extra=0)


#HIV_DB: EXTRACT

import csv, os.path

def remove_duplicates(List):
    output = []
    seen = set()
    for value in List:
        if value not in seen:
            output.append(value)
            seen.add(value)
    return output

#Getting the data
#Takes in a list and a filename/path
def get_list(ls, fl):
    with open(fl, 'r') as f:
        reader = csv.reader(f)
        for i in reader:
            ls = list(reader)
    return ls

def extract_mut(ID,d,region):
    #Making region header
    for i in range(len(ID)):
        with open("Variants_{0}.txt".format(ID[i]), 'a') as f:
            f.write("\n" +region + "\n")
    #Extracting mutations
    for i in range(len(d)): #Tracks current sample per ID
        mut = ''
        for j in range(len(ID)): #Tracks current ID
            if ((ID[j] == d[i][0]) and (d[i][1] == region)): #if ID is the same and Gene hasn't changed...
                mut += (str(d[i][4]) + str(d[i][5]) + str(d[i][6]) +"\n") #...We'll add it to the gene list
                break #No need to iterate the whole list again...so we stop, and move on to the next sample.
        if (mut != ''):         #If the last ID had any mutations....
            with open("Variants_{0}.txt".format(ID[j]), 'a') as f:
                f.write(mut)    #We add those mutaions to the files.
#Main
user = input("Enter file with path: \n")
while(not os.path.exists(user)):
    user = input("File not found! Enter file with path or Ctrl-z to exit: ")

d = []
d = get_list(d,user)
ID = [i[0] for i in d]
ID = remove_duplicates(ID)

mut_RT = 'RT'
mut_PR = 'PR'
mut_IN = 'IN'

extract_mut(ID,d,mut_RT)
extract_mut(ID,d,mut_PR)
extract_mut(ID,d,mut_IN)


import numpy as np
import os
import argparse

def main(waymo_root):
    segs = sorted(os.listdir(waymo_root))

    for seg in segs:
        path = os.path.join(waymo_root,seg,'PC_ng')
        files = sorted(os.listdir(path))
        output_dir = os.path.join(waymo_root,seg,'point')
        os.makedirs(output_dir,exist_ok=True)
        first_frame = np.load(os.path.join(path,files[0]))
        initial_coord = first_frame['global_pc1'].mean(axis=0)

        for i in range(0,len(files)-1):
            tmp_path1 = os.path.join(path,files[i])
            tmp_path2 = os.path.join(path,files[i+1])
            global_p1 = np.load(tmp_path1)['global_pc1']
            global_p2 = np.load(tmp_path2)['global_pc1']
            camera_projections1 = np.load(tmp_path1)['cp_point']
            vehicle_p1 = np.load(tmp_path1)['point'][:,:3]
            global_gt = np.load(tmp_path1)['point'][:,3:]
            name = 'frame_'+ '%06d' % i + '.npz'
            global_p1 = global_p1-initial_coord
            global_p2 = global_p2-initial_coord
            # 10Hz (0.1s)
            if i==0:
                global_gt = global_gt*0.0
            else:
                global_gt = global_gt*0.1
            cp1 = camera_projections1
            np.savez(os.path.join(output_dir,name),p1=global_p1,p2=global_p2,gt = global_gt,cp1 = cp1)
        print('finish:',seg)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--waymo_root',default='/data/yuqi_wang/waymo_v1.2/waymo_lsmol',help='path to the waymo open dataset')
    args = parser.parse_args()

    main(args.waymo_root)


import scipy.stats
import numpy as np
import math

def p_value_if_weibull(samples):
    results = scipy.stats.kstest(samples, 'weibull_min', scipy.stats.weibull_min.fit(samples))
    p_value = results[1]
    return p_value

def p_value_if_exp(samples):
    results = scipy.stats.kstest(samples, 'expon', scipy.stats.expon.fit(samples))
    p_value = results[1]
    return p_value

def p_value_if_lognorm(samples):
    results = scipy.stats.kstest(samples, 'lognorm', scipy.stats.lognorm.fit(samples))
    p_value = results[1]
    return p_value

def p_value_if_norm(samples):
    results = scipy.stats.kstest(samples, 'norm', scipy.stats.norm.fit(samples))
    p_value = results[1]
    return p_value

def possible_distributions(p_values_dict):
    """
    Checks which distributions have p-value of at least 0.05,
    so it's possible the samples are from that distribution.
    :param p_values_dict:
    :return:
    """
    significant_p_values = dict()
    for distribution, p_val in p_values_dict.items():
        if p_val > 0.05:
            significant_p_values[distribution] = p_val

    return significant_p_values

def get_exp_parameters(parameters):
    return [1/parameters[1]]

def get_lognorm_parameters(parameters):
    return [np.log(parameters[2]), parameters[0]]

def get_weibull_parameters(parameters):
    return [parameters[2], parameters[0]]

def get_normal_parameters(parameters):
    return [parameters[0], parameters[1]]

def determine_distribution(samples):
    p_values = dict()

    p_values['EXP'] = p_value_if_exp(samples)
    p_values['LOGNORM'] = p_value_if_lognorm(samples)
    p_values['WEIBULL'] = p_value_if_weibull(samples)
    p_values['NORMAL'] = p_value_if_norm(samples)

    # print('P values: ' + str(p_values))
    p_values = possible_distributions(p_values)

    best_fit_distribution = []

    if len(p_values) > 1:
        if 'LOGNORM' in p_values.keys() and 'NORMAL' in p_values.keys():
            del p_values['LOGNORM']
            # print('Deleting LOGNORM...')
        if 'WEIBULL' in p_values.keys() and 'NORMAL' in p_values.keys():
            del p_values['WEIBULL']
            # print('Deleting WEIBULL...')
        if 'WEIBULL' in p_values.keys() and 'EXP' in p_values.keys():
            del p_values['WEIBULL']
            # print('Deleting WEIBULL...')

    if len(p_values) == 1:
        distribution = p_values.popitem()
        # Name of distribution
        best_fit_distribution.append(distribution[0])
        if distribution[0] == 'EXP':
            best_fit_distribution.extend(get_exp_parameters(scipy.stats.expon.fit(samples)))

        if distribution[0] == 'LOGNORM':
            best_fit_distribution.extend(get_lognorm_parameters(scipy.stats.lognorm.fit(samples)))

        if distribution[0] == 'WEIBULL':
            best_fit_distribution.extend(get_weibull_parameters(scipy.stats.weibull_min.fit(samples)))

        if distribution[0] == 'NORMAL':
            best_fit_distribution.extend(get_normal_parameters(scipy.stats.norm.fit(samples)))
    else:
        best_fit_distribution.append('UNIDENTIFIED DISTRIBUTION')

    return best_fit_distribution

# Able to determine the above shown distribution from samples. However weibull cannot be identified sometimes.
# And sometimes exp is identified as both exp and weibull since exp is a weibull with a beta of 1.
# Fix these.
# Lognorm seems to work all the time, more testing though
# Sometimes with low sample size, even 1000 is low enough, LOGNORM, WEIBULL, and NORMAL are all identified,
# when it should be NORMAL

def calculate_mttf_or_mttr_from_distribution(distribution):
    mttf = 0
    if distribution[0] == 'NORMAL':
        mttf = distribution[1]
    if distribution[0] == 'EXP':
        mttf = 1/distribution[1]
    if distribution[0] == 'LOGNORM':
        mu = distribution[1]
        sigma = distribution[2]
        mttf = math.exp(mu + (0.5 * sigma ** 2))
    if distribution[0] == 'WEIBULL':
        scale = distribution[1]
        shape = distribution[2]
        mttf = scale * math.gamma(1 / shape + 1)

    return mttf