import pickle
import textwrap
import tkinter as tk
from tkinter import filedialog
from tkinter import ttk

from Character_Sheet.reference.backgrounds import *
from Character_Sheet.reference.feats import feat_list, unpack_desc
from Character_Sheet.reference.glossary import common_languages, \
    exotic_languages, attrs
from Character_Sheet.reference.races import race_list
from Character_Sheet.reference.subclasses import *

# from Character_Sheet.reference.equipment import Martial, Simple, Ranged, Melee

global current_race_instance, current_subrace_instance

# System Functions

def export(character):
    name = character["Name"]

    if name == "":
        name = "Empty_Character"

    loc = f'saves/{name}'

    with open(loc + '.pkl', "wb") as file:
        pickle.dump(character, file, pickle.HIGHEST_PROTOCOL)
    file.close()

def import_info(filename):
    file = open(filename, "rb")
    info = pickle.load(file)
    file.close()

    return info

def update_character_info():
    character_data = {}
    for name, item in character.items():

        if isinstance(item, (list, tuple)):
            print("Alternative methods required")
        elif isinstance(item, dict):
            character_data[name] = {}
            for key, value in item.items():
                if not isinstance(value[0], (list, tuple)):
                    character_data[name][key] = [val.get() for val in value]
                else:
                    character_data[name][key] = [[v.get() for v in val] for val
                                                 in value]
        else:
            character_data[name] = item.get()

    return character_data

def save():
    data = update_character_info()
    export(data)

def load():
    filename = tk.filedialog.askopenfilename(initialdir="saves/",
                                             title="Select save file",
                                             filetypes=(
                                                 ("Pickled Files", "*.pkl"),
                                                 ("all files", "*.*")))

    character_data = import_info(filename)

    second_stage_imports = []

    for key, value in character_data.items():
        try:
            input = character_data[key]
            destination = character[key]
            if isinstance(destination, tk.ttk.Combobox) or isinstance(
                    destination, tk.StringVar):
                destination.set(input)
            elif isinstance(destination, dict):
                second_stage_imports.append((key, value))
            else:
                destination.delete(0, tk.END)
                destination.insert(0, input)
        except Exception as print_error:
            print(key)
            print(character[key])
            print(value)
            print(print_error)

    for aspect in second_stage_imports:
        key, value = aspect
        for subkey, subvalue in value.items():
            update_character_info()
            if not isinstance(subvalue[0], (list, tuple)):
                for n, variable in enumerate(character[key][subkey]):
                    variable.set(subvalue[n])
            else:
                for i, sublist in enumerate(character[key][subkey]):
                    for j, variable in enumerate(sublist):
                        variable.set(subvalue[i][j])

    resize_tabs()

def save_and_close():
    save()
    window.destroy()

def close():
    window.destroy()

def exit():
    def exit_save_and_close():
        save_and_close()
        exit_window.destroy()

    def exit_close():
        close()
        exit_window.destroy()

    exit_window = tk.Tk()
    exit_label = tk.Label(exit_window, text="Would you like to save?",
                          font=default_font + " 10")
    exit_label.pack()
    exit_buttons = tk.Frame(exit_window)
    yes_button = tk.Button(exit_buttons, width=8, text="Yes",
                           command=exit_save_and_close)
    no_button = tk.Button(exit_buttons, width=8, text="No", command=exit_close)
    cancel_button = tk.Button(exit_buttons, width=8, text="Cancel",
                              command=exit_window.destroy)

    yes_button.grid(row=1, column=0, padx=4)
    no_button.grid(row=1, column=1, padx=4, pady=8)
    cancel_button.grid(row=1, column=2, padx=4)

    exit_buttons.pack()

# Helper Functions

def get_chosen_skills(character):
    return [character[choice].get() for choice in character.keys() if
            "Skill" in choice]

def text_join(text, capitalise, sentence_end):
    if len(text) > 2:
        text = ", ".join(text[:-1]) + f", and {text[-1]}"
    else:
        text = " and ".join(text)

    text += sentence_end

    if capitalise:
        text = text[0].upper() + text[1:]

    return text

# Creation of Frames

def Title():
    title = tk.Label(character_creator,
                     text='Character Creator',
                     bd=8,
                     font=default_font + " 14 bold")

    title.pack(side=tk.TOP)

    main_menu = tk.Menu(character_creator)

    file_menu = tk.Menu(main_menu, tearoff=0)
    file_menu.add_command(label="Save", command=save)
    file_menu.add_command(label="Load", command=load)
    file_menu.add_separator()
    file_menu.add_command(label="Exit", command=exit)
    main_menu.add_cascade(label="File", menu=file_menu)

    window.config(menu=main_menu)

def Info():
    info_frame = tk.Frame(info_tab,
                          relief=tk.SUNKEN,
                          borderwidth=4,
                          )

    ### Character Name

    name_frame = tk.Frame(info_frame)

    name_label = tk.Label(name_frame,
                          text='Character name',
                          font=default_font + " 12 bold")
    name_entry = tk.Entry(name_frame,
                          width=24,
                          justify="center")

    name_label.pack()
    name_entry.pack()

    name_frame.pack(padx=8, pady=(8, 16))

    ### Character info

    data_frame = tk.Frame(info_frame)

    #### Age

    age_frame = tk.Frame(data_frame)

    age_label = tk.Label(age_frame,
                         text="Age",
                         font=default_font + " 10 bold")
    age_entry = tk.Entry(age_frame,
                         width=8,
                         justify="center")

    age_label.grid(row=0)
    age_entry.grid(row=1)

    #### Gender

    gender_frame = tk.Frame(data_frame)

    gender_label = tk.Label(gender_frame,
                            text="Gender",
                            font=default_font + " 10 bold")
    gender_entry = tk.Entry(gender_frame,
                            width=8,
                            justify="center")

    gender_label.grid(row=0)
    gender_entry.grid(row=1)

    #### Physical Attributes

    appearance_aspects = ["Skin Colour",
                          "Hair Colour",
                          "Eye Colour"]
    size_aspects = ["Height",
                    "Weight",
                    "Build"]

    appearance_frame = tk.Frame(data_frame)
    size_frame = tk.Frame(data_frame)

    physicals = {}

    for j in range(len(appearance_aspects)):

        frames = appearance_frame, size_frame
        aspects = appearance_aspects, size_aspects

        for k, frame in enumerate(frames):
            label = tk.Label(frame,
                             text=aspects[k][j],
                             font=default_font + " 10 bold")
            entry = tk.Entry(frame,
                             width=10,
                             justify="center")

            physicals[label.cget("text")] = entry

            label.grid(row=1, column=j, pady=(2, 1), padx=4)
            entry.grid(row=2, column=j, padx=8)

    ### Add all data to frame

    age_frame.grid(row=0, column=0, padx=(0, 4), sticky="E")
    gender_frame.grid(row=0, column=1, padx=(4, 0), sticky="W")
    appearance_frame.grid(row=2, column=0, columnspan=2, padx=(0, 8))
    size_frame.grid(row=4, column=0, columnspan=2, padx=(8, 0))

    data_frame.pack()

    ### Faith

    faith_frame = tk.Frame(info_frame)

    faith_label = tk.Label(faith_frame,
                           text='Faith',
                           font=default_font + " 10 bold")
    faith_entry = tk.Entry(faith_frame,
                           width=16,
                           justify="center")

    faith_label.pack()
    faith_entry.pack()

    faith_frame.pack(pady=2)

    ### Alignment

    alignment_frame = tk.Frame(info_frame)

    alignment_label = tk.Label(faith_frame,
                               text='Alignment',
                               font=default_font + " 10 bold")

    alignment_entry_frame = tk.Frame(alignment_frame)

    ethics = ttk.Combobox(alignment_entry_frame,
                          values=["Lawful", "Neutral", "Chaotic"],
                          state="readonly",
                          width=8)
    morality = ttk.Combobox(alignment_entry_frame,
                            values=["Good", "Neutral", "Evil"],
                            state="readonly",
                            width=8)

    ethics.grid(row=0, column=1, padx=(0, 2))
    morality.grid(row=0, column=2, padx=(2, 0))

    alignment_label.pack()
    alignment_entry_frame.pack()

    alignment_frame.pack(pady=2)

    ### Character Info for Export

    character_info = {"Name": name_entry,
                      "Age": age_entry,
                      "Gender": gender_entry,
                      "Faith": faith_entry,
                      "Ethics": ethics,
                      "Morality": morality
                      }

    character_info.update(physicals)

    character.update(character_info)

    return info_frame

def Race():
    def update_race_info():

        for key, value in race_data.items():
            if key != "Race" and key != "Subrace":
                value.set("")

        size = current_race_instance.size
        speed = current_race_instance.speed
        race_size_text.set(size)
        race_speed_text.set(f'{speed} ft.')

        if current_race_instance.__subclasses__():
            subrace_choice.pack()
            if current_subrace.get() != '':
                subrace_choice.set(current_subrace.get())
            else:
                subrace_choice.set(subrace_choice_prompt)
            subclass = True

        else:
            current_subrace.set(None)
            subrace_choice.pack_forget()
            subclass = False
            subrace_choice.set("")

        race_info.pack_forget()
        race_info.pack()
        race_languages_choice.grid_forget()
        race_base_info.update()
        languages()

        if not subclass:
            ASI()
            other_features()

    def languages():

        language_list = [language for language in
                         current_race_instance.languages]

        choices_num = language_list.count("choice")

        if choices_num > 0:
            language_list.remove("choice")
            race_languages_choice.grid(row=3, column=2, stick="W", padx=(2, 0))

            divider.grid(rowspan=race_base_info.grid_size()[1])

            known_languages = language_list
            language_choices = ["None"] + common_languages + exotic_languages
            for lang in known_languages:
                language_choices.remove(lang)
            race_languages_choice["values"] = language_choices

        language_list = "\n".join(language_list)

        race_language_text.set(language_list)

    def ASI():

        bottom_divider.grid(column=0, row=5, columnspan=3, sticky="EW",
                            pady=(4, 0))

        if current_subrace_instance is None:
            ASI = current_race_instance.ASI
        else:
            ASI = current_subrace_instance.ASI

        num_choices = 0
        choice_options = []

        for i in ASI:
            if i[0] == "choice":
                num_choices += 1
                choice_options.append(i[1])

        ASI_automatic = dict(ASI)

        if num_choices > 0:
            del ASI_automatic["choice"]

        if ASI_automatic:
            asi_attributes_frame.grid(row=1)
            for asi_value in asi_automatic_values:
                asi_value.configure(text="")

            for attribute, attr_value in ASI_automatic.items():
                attr_index = attrs.index(attribute.__name__)

                text_value = f'{attr_value:+d}'

                asi_automatic_values[attr_index].configure(text=text_value)
        else:
            asi_attributes_frame.grid_forget()

        asi_choice_frame.grid_forget()

        if num_choices > 0:
            asi_choice_label["text"] = "Choose ability score to increase by +1:"
            asi_choice_frame.grid(row=2)
            asi_choice_2.grid_forget()
            if choice_options[0] == "any":
                asi_options = attrs.copy()
            else:
                asi_options = attrs.copy()
                asi_options.remove(choice_options[0])

            asi_choice_1_val = tk.StringVar()
            asi_choice_1["textvariable"] = asi_choice_1_val
            asi_choice_1["values"] = asi_options
        if num_choices == 2:
            asi_choice_label[
                "text"] = "Choose ability scores to increase by +1:"
            asi_choice_2.grid(row=2)
            asi_choice_2_val = tk.StringVar()
            asi_choice_2["textvariable"] = asi_choice_2_val

            def check_asi_1_choices():
                second_choice = asi_choice_2.get()
                if not second_choice:
                    pass
                else:
                    first_options = asi_options.copy()
                    first_options.remove(second_choice)
                    asi_choice_1["values"] = first_options

            def check_asi_2_choices():
                first_choice = asi_choice_1.get()
                second_options = asi_options.copy()
                second_options.remove(first_choice)
                asi_choice_2["values"] = second_options

            asi_choice_1["postcommand"] = check_asi_1_choices
            asi_choice_2["postcommand"] = check_asi_2_choices

        asi_frame.grid(row=2, column=0)

    def other_features():

        race_features_frame.grid_forget()
        divider_2.grid_forget()

        for widget in race_features_internal_frame.winfo_children():
            widget.pack_forget()

        if current_race_instance.features:
            race_features = current_race_instance.features.copy()
            try:
                race_features += current_race_instance.features_chosen
            except:
                pass
        else:
            race_features = None

        if current_subrace_instance and current_subrace_instance.features:
            subrace_features = current_subrace_instance.features.copy()
            try:
                subrace_features += current_subrace_instance.features_chosen
            except:
                pass
        else:
            subrace_features = None

        if not (race_features or subrace_features):
            return
        else:
            race_info.update()
            race_features_frame.grid(row=0, column=2, rowspan=8, sticky="N")
            divider_2.grid(column=1, row=0, sticky="NS", rowspan=8)

        if race_features:
            features_checker(current_race_instance, race_features)

        if subrace_features:
            features_checker(current_subrace_instance, subrace_features)

    def feature_switch(entity, feature):
        if feature == "skills":
            skills(entity.skills)
        elif feature == "feat":
            feat(entity.feats)
        elif feature == "other":
            others(entity.other_features)
        elif feature == "choice":
            feature_choice(entity)

    def features_checker(entity, features):

        for feature in features:
            feature_switch(entity, feature)

    def feature_choice(entity):

        options = list(entity.choice_features.keys())

        if entity.__subclasses__():
            if race_feature_chosen.get() not in options:
                race_feature_chosen.set("")
            race_feature_chooser["values"] = options
            subrace_feature_chooser_label.pack_forget()
            subrace_feature_chooser.pack_forget()

        else:
            if subrace_feature_chosen.get() not in options:
                subrace_feature_chosen.set("")
            subrace_feature_chooser["values"] = options
            race_feature_chooser_label.pack_forget()
            race_feature_chooser.pack_forget()

        race_feature_chooser_frame.pack()

    def skills(valid_skills):

        def choose_race_skill():

            chosen_skills = get_chosen_skills(character)

            for n, valid_skill in enumerate(valid_skills):

                invalid_skills = chosen_skills.copy()

                invalid_skills.remove(race_skills_choices[n].get())

                if isinstance(valid_skill, str) and valid_skill == "any":
                    valid_choices = [skill for skill in skills_list if
                                     skill not in invalid_skills]

                race_skill_choosers[n]["value"] = valid_choices

        skill_chooser_1["postcommand"] = choose_race_skill
        skill_chooser_2["postcommand"] = choose_race_skill

        for n, skill_chooser in enumerate(race_skill_choosers):
            try:
                if valid_skills[n] != "any" and skill_chooser.get() not in \
                        valid_skills[n]:
                    race_skills_choices[n].set(skill_chooser.get())
                skill_chooser.pack()
            except:
                skill_chooser.set("")
                skill_chooser.pack_forget()

        skill_chooser_frame.pack()

    def feat(valid_feats):

        feat_chooser.set("")

        if valid_feats == "any":
            valid_feats_name_list = tuple([name for name in feat_list.keys()])
        else:
            valid_feats_name_list = tuple()

        feat_chooser["values"] = valid_feats_name_list
        feat_chooser_frame.pack()

    def others(other_features):
        text = ""
        for name, feature in other_features.items():
            text += f'{name}:'
            text += f'\n{feature.desc}\n\n'
        text = text[:-1]
        general_features_text.set(text)
        general_features_frame.pack()

    # Chosing Race and Subrace
    race_frame = tk.Frame(race_tab,
                          relief=tk.SUNKEN,
                          borderwidth=4,
                          )
    race_label = tk.Label(race_frame,
                          text="Race",
                          font=default_font + " 12 bold")
    race_label.pack(pady=(8, 8))
    current_race = tk.StringVar()
    race_choice = ttk.Combobox(race_frame,
                               values=[race for race in race_list],
                               state="readonly",
                               width=16,
                               textvariable=current_race)

    race_choice_prompt = "Choose race: "
    race_choice.set(race_choice_prompt)

    subrace_choice_prompt = "Choose subrace: "
    current_subrace = tk.StringVar()

    def get_subclasses():
        subrace_choice["values"] = [subrace.subrace_name for subrace in
                                    current_race_instance.__subclasses__()]

    subrace_choice = ttk.Combobox(race_frame,
                                  postcommand=get_subclasses,
                                  values=[],
                                  state="readonly",
                                  width=16,
                                  textvariable=current_subrace)

    # Race Info Stuff
    race_info = tk.Frame(race_frame)

    race_info_label = tk.Label(race_info,
                               text="Racial Stats:",
                               font=default_font + " 10 bold")

    race_size_text = tk.StringVar()
    race_speed_text = tk.StringVar()
    race_language_text = tk.StringVar()

    race_size_text.set("Medium")
    race_speed_text.set(f'{30} ft.')
    race_language_text.set("Common")

    race_base_info = tk.Frame(race_info)

    race_size_label = tk.Label(race_base_info,
                               text="Size",
                               font=default_font + " 10 bold")
    race_speed_label = tk.Label(race_base_info,
                                text="Speed",
                                font=default_font + " 10 bold")
    divider = ttk.Separator(race_base_info,
                            orient=tk.VERTICAL)

    race_size_value = tk.Label(race_base_info,
                               textvariable=race_size_text,
                               font=default_font + " 10")

    race_speed_value = tk.Label(race_base_info,
                                textvariable=race_speed_text,
                                font=default_font + " 10")

    race_languages_label = tk.Label(race_base_info,
                                    text="Languages",
                                    font=default_font + " 10 bold")

    race_languages_value = tk.Label(race_base_info,
                                    textvariable=race_language_text,
                                    font=default_font + " 10",
                                    justify=tk.LEFT)

    race_languages_choice = ttk.Combobox(race_base_info,
                                         values=[],
                                         state="readonly",
                                         width=16)

    race_languages_choice.set("Choose language: ")

    race_size_label.grid(row=0, column=0, sticky="E")
    race_size_value.grid(row=0, column=2, sticky="W")
    race_speed_label.grid(row=1, column=0, sticky="E")
    race_speed_value.grid(row=1, column=2, sticky="W")
    race_languages_label.grid(row=2, column=0, sticky="NE")
    race_languages_value.grid(row=2, column=2, sticky="NW")

    divider.grid(column=1, row=0, rowspan=race_base_info.grid_size()[1],
                 sticky="NS")

    bottom_divider = ttk.Separator(race_base_info)
    bottom_divider.grid(column=0, row=5, columnspan=3, sticky="EW", pady=(4, 0))

    race_info_label.grid(row=0)
    race_base_info.grid(row=1, column=0, sticky="N")

    # Tracing Race/Subrace values
    def changed_race(*args):
        global current_race_instance
        if current_race.get() != race_choice_prompt:  # essential to call the variable once, no idea why
            current_race_instance = race_list[current_race.get()]
            asi_frame.grid_forget()
            bottom_divider.grid_forget()
            current_subrace.set(subrace_choice_prompt)
            update_race_info()
        else:
            current_race_instance = None

        resize_tabs()

    current_race.trace('w', changed_race)

    def changed_subrace(*args):
        global current_subrace_instance

        if current_race.get() != 'Choose race: ':

            if race_list[
                current_race.get()].__subclasses__() and current_subrace.get() != subrace_choice_prompt:
                current_subrace_instance = \
                    {subrace.subrace_name: subrace for subrace in
                     current_race_instance.__subclasses__()}[
                        current_subrace.get()]
                asi_frame.grid_forget()
                bottom_divider.grid_forget()
                ASI()
                other_features()

            elif current_subrace.get() == subrace_choice_prompt:
                race_features_frame.grid_forget()
                current_subrace_instance = None

            resize_tabs()

    current_subrace.trace('w', changed_subrace)

    # ASI Stuff
    asi_frame = tk.Frame(race_info)

    asi_label = tk.Label(asi_frame,
                         text="Ability score increases",
                         font=default_font + " 8 bold")

    asi_label.grid(row=0, sticky="N")

    asi_attributes_frame = tk.Frame(asi_frame)

    asi_automatic_values = [None] * 6
    for n, attribute in enumerate(attrs):
        label = tk.Label(asi_attributes_frame,
                         text=attribute,
                         font=default_font + " 10 bold")
        label.grid(row=n, column=0, sticky="E")

        value = tk.Label(asi_attributes_frame,
                         font=default_font + " 10")

        value.grid(row=n, column=2, sticky="W")
        asi_automatic_values[n] = value

    ttk.Separator(asi_attributes_frame,
                  orient=tk.VERTICAL).grid(column=1, row=0, sticky="NS",
                                           rowspan=
                                           asi_attributes_frame.grid_size()[1])

    asi_choice_frame = tk.Frame(asi_frame)

    asi_choice_label = tk.Label(asi_choice_frame,
                                text="Choose ability score to increase by +1:")

    asi_choice_1 = ttk.Combobox(asi_choice_frame,
                                state="readonly",
                                width=12)

    asi_choice_2 = ttk.Combobox(asi_choice_frame,
                                state="readonly",
                                width=12)

    asi_choice_label.grid(row=0)
    asi_choice_1.grid(row=1)
    asi_choice_2.grid(row=2)

    asi_attributes_frame.grid(row=1)
    asi_choice_frame.grid(row=2)

    race_choice.pack()

    # Race Features
    race_features_frame = tk.Frame(race_info)

    race_features_label = tk.Label(race_features_frame,
                                   text="Racial Features:",
                                   font=default_font + " 10 bold",
                                   anchor="n")
    race_features_label.grid(row=0, sticky="N")

    # race_features_frame.grid(row=0, column=2, sticky="N")

    divider_2 = ttk.Separator(race_info,
                              orient=tk.VERTICAL)

    # divider_2.grid(column=1, row=0, sticky="NS", rowspan=2)

    race_features_internal_frame = tk.Frame(race_features_frame)
    race_features_internal_frame.grid(row=1)

    skill_chooser_frame = tk.Frame(race_features_internal_frame)

    skill_chooser_label = tk.Label(skill_chooser_frame,
                                   text="Choose skill proficiency:",
                                   font=default_font + " 8")

    race_skill_choice_1 = tk.StringVar()
    race_skill_choice_2 = tk.StringVar()

    skill_chooser_1 = ttk.Combobox(skill_chooser_frame,
                                   state="readonly",
                                   textvariable=race_skill_choice_1)
    skill_chooser_label.pack(pady=(4, 0))
    skill_chooser_1.pack()

    skill_chooser_2 = ttk.Combobox(skill_chooser_frame,
                                   state="readonly",
                                   textvariable=race_skill_choice_2)
    skill_chooser_2.pack()

    race_skill_choosers = [skill_chooser_1, skill_chooser_2]
    race_skills_choices = [race_skill_choice_1, race_skill_choice_2]

    feat_chooser_frame = tk.Frame(race_features_internal_frame)

    feat_chooser_label = tk.Label(feat_chooser_frame,
                                  text="Choose feat:",
                                  font=default_font + " 8")
    feat_chosen = tk.StringVar()
    feat_chooser = ttk.Combobox(feat_chooser_frame,
                                textvariable=feat_chosen,
                                state="readonly")

    def changed_feat(*args):

        if feat_chosen.get():
            description = feat_list[feat_chosen.get()].desc
            description_formatted = unpack_desc(description)
            feat_description.set(description_formatted)

        resize_tabs()

    feat_chosen.trace('w', changed_feat)
    feat_description = tk.StringVar()

    feat_description_label = tk.Label(feat_chooser_frame,
                                      textvariable=feat_description,
                                      wraplength=400,
                                      justify=tk.LEFT,
                                      font=default_font + " 8")

    feat_chooser_label.pack()
    feat_chooser.pack()
    feat_description_label.pack()

    general_features_frame = tk.Frame(race_features_internal_frame)

    general_features_text = tk.StringVar()

    general_feature_label = tk.Label(general_features_frame,
                                     textvariable=general_features_text,
                                     wraplength=400,
                                     justify=tk.LEFT,
                                     anchor="w",
                                     font=default_font + " 8"
                                     )

    general_feature_label.pack(side=tk.LEFT)

    race_feature_chooser_frame = tk.Frame(race_features_internal_frame)
    race_feature_chooser_label = tk.Label(race_feature_chooser_frame,
                                          text="Choose racial feature:",
                                          font=default_font + " 8")

    subrace_feature_chooser_label = tk.Label(race_feature_chooser_frame,
                                             text="Choose subrace feature:",
                                             font=default_font + " 8")

    race_feature_chosen = tk.StringVar()
    subrace_feature_chosen = tk.StringVar()
    race_feature_chooser = ttk.Combobox(race_feature_chooser_frame,
                                        state="readonly",
                                        textvariable=race_feature_chosen)
    subrace_feature_chooser = ttk.Combobox(race_feature_chooser_frame,
                                           state="readonly",
                                           textvariable=subrace_feature_chosen)

    def race_feature_changed(*args):

        if race_feature_chosen.get():

            feature_value = current_race_instance.choice_features[
                race_feature_chosen.get()]

            current_race_instance.features_chosen = []

            if isinstance(feature_value, str):
                current_race_instance.features_chosen.append(feature_value)
                other_features()

            else:
                other_features()
                text = general_features_text.get()
                text += f'\n{race_feature_chosen.get()}:'
                text += f'\n{feature_value.desc}\n'
                general_features_text.set(text)
                general_features_frame.pack()

        resize_tabs()

    def subrace_feature_changed(*args):
        if subrace_feature_chosen.get():

            feature_value = current_subrace_instance.choice_features[
                subrace_feature_chosen.get()]

            current_subrace_instance.features_chosen = []

            if isinstance(feature_value, str):
                current_subrace_instance.features_chosen.append(feature_value)
                other_features()

            else:
                other_features()
                text = general_features_text.get()
                text += f'\n{subrace_feature_chosen.get()}:'
                text += f'\n{feature_value.desc}\n'
                general_features_text.set(text)
                general_features_frame.pack()

        resize_tabs()

    race_feature_chosen.trace("w", race_feature_changed)
    subrace_feature_chosen.trace("w", subrace_feature_changed)

    race_feature_chooser_label.pack(pady=(4, 0))
    race_feature_chooser.pack()
    subrace_feature_chooser_label.pack()
    subrace_feature_chooser.pack()

    race_info.update()

    race_data = {"Race": race_choice,
                 "Subrace": subrace_choice,
                 "Languages": race_languages_choice,
                 "ASI Choice 1": asi_choice_1,
                 "ASI Choice 2": asi_choice_2,
                 "Race Skill 1": skill_chooser_1,
                 "Race Skill 2": skill_chooser_2,
                 "Race Feat": feat_chooser,
                 "Race Feature": race_feature_chooser,
                 "Subrace Feature": subrace_feature_chooser
                 }

    character.update(race_data)

    return race_frame

def Class():
    def class_choice_changed(*args):
        global current_class_instance

        if current_class_choice.get() != "":
            current_class_instance = class_list[current_class_choice.get()]()
            class_ = current_class_instance
            class_.base_features()
            class_top_label["text"] = f"{class_.name} features:"
            text = ""

            text += f"Primary Attribute{'s' if len(current_class_instance.primary_attr) > 1 else ''}:\n"
            text += " and ".join([attr.name for attr in
                                  current_class_instance.primary_attr]) + "\n"

            text += f"\nSaving Throw Proficiencies:\n" \
                    f"{', '.join([class_.name if class_ else 'None' for class_ in class_.saving_throws])}\n"

            text += f"\nArmour Proficiencies:\n" \
                    f"{', '.join([class_.__name__ if class_ else 'None' for class_ in class_.armour_proficiencies])}\n"

            text += f"\nWeapon Proficiencies:\n" \
                    f"{', '.join([class_.name if class_ else 'None' for class_ in class_.weapon_proficiencies])}\n"

            text += f"\nTool Proficiencies:\n" \
                    f"{', '.join([class_.name if class_ else 'None' for class_ in class_.tool_proficiencies])}\n"

            text += f"\nHit Dice: {class_.hit_die}\n"
            text += f"Level up HP: {class_.lvl_up_hp}\n"

            class_basic_info_text["text"] = text

            class_left_frame.grid(column=0, row=0, sticky="n", padx=4)
            class_central_frame.grid(column=2, row=0, sticky="n", padx=4)
            class_right_frame.grid(column=4, row=0, sticky="n", padx=4)

            class_divider_1.grid(column=1, row=0, rowspan=10, sticky="NS")
            class_divider_2.grid(column=3, row=0, rowspan=10, sticky="NS")

            skill_choosers(current_class_instance)
            equipment_selection(current_class_instance)

            class_description.set(f'{class_.desc}\n\n\n{class_.rpgbot}')

            subclass_list = "\n".join(
                sorted([subclass.name for subclass in
                        current_class_instance.__class__.__subclasses__()],
                       key=str.lower))

            class_subclass_lvl_label[
                "text"] = f'\n{class_.name}s choose their {class_.subclass_name} at level {class_.subclass_lvl}, selecting from:\n\n{subclass_list}'

            resize_tabs()

    def skill_choosers(current_class):
        for chooser in class_skill_choosers_list:
            chooser.grid_forget()

        for n, option in enumerate(class_skill_choices):
            try:
                if current_class.skills[n] == "choose":
                    class_skill_choosers_list[n][
                        "postcommand"] = class_skill_chooser_prep
                    class_skill_choosers_list[n].grid(row=n + 1, pady=1, padx=4)
                else:
                    pass

                if option.get() not in [skill.name for skill in
                                        current_class.valid_skills]:
                    option.set("")
            except:
                option.set("")

    def class_skill_chooser_prep():

        chosen_skills = get_chosen_skills(character)

        current_class_instance.base_features()

        if isinstance(current_class_instance.valid_skills, str):
            valid_skills = [skill.key() for skill in skills_list]
        else:
            valid_skills = [skill.name for skill in
                            current_class_instance.valid_skills]

        for n, skill_chooser in enumerate(class_skill_choosers_list):
            current_chooser_choice = class_skill_choices[n].get()
            invalid_choices = chosen_skills.copy()
            invalid_choices.remove(current_chooser_choice)

            skills_options = [skill for skill in valid_skills if
                              skill not in invalid_choices]
            class_skill_choosers_list[n]["values"] = skills_options

    def equipment_selection(current_class):

        class equipment_selection:

            def __init__(self, index, choices):
                self.index = index
                self.tracking_variable = tk.IntVar()
                self.frame = tk.Frame(class_equipment_internal_frame)
                equipment_choices["selections"].append(self.tracking_variable)
                equipment_choices["selection_options"].append(choices)
                for i, options in enumerate(choices):
                    text = []
                    for j, item in enumerate(options):
                        if isinstance(item[0], tuple):
                            conditions = item[0]
                            # This assumes only two conditions, may prove to not be correct in the future but hey.
                            parent_1 = set(list(conditions[0].__bases__))
                            parent_2 = set(list(conditions[1].__bases__))

                            common_parent = list(parent_1.intersection(
                                parent_2))  # assume only one common parent

                            text_entry = f'{conditions[0].syntax_start(item[1])} {conditions[1].__name__} {common_parent[0].syntax_end(item[1])}'.lower()
                        elif item[0].__subclasses__():
                            text_entry = f'{item[0].syntax_start(item[1])} {item[0].__bases__[0].syntax_end(item[1])}'.lower()
                        else:
                            text_entry = item[0].syntax(item[1])

                        text.append(text_entry)

                    text = text_join(text, True, "")

                    button = tk.Radiobutton(self.frame,
                                            value=i,
                                            variable=self.tracking_variable,
                                            text=text,
                                            command=self.changed_selection)
                    button.grid(row=i, sticky=tk.W)
                    if i == 0:
                        button.select()

                    self.tracking_variable.trace("w",
                                                 self.changed_selection_auto)

                self.frame.update()

                self.frame.grid(row=index * 2, column=0, sticky=tk.W)
                ttk.Separator(class_equipment_internal_frame).grid(
                    row=index * 2 + 1, column=0, columnspan=3,
                    sticky=tk.EW)

            def changed_selection(self):
                get_choice_options(self.index)

            def changed_selection_auto(self, *args):
                self.tracking_variable
                get_choice_options(self.index)

        def get_choice_options(selection_index):
            current_selection_choice = equipment_choices["selections"][
                selection_index].get()

            current_selection_options = \
                equipment_choices["selection_options"][selection_index][
                    current_selection_choice]

            for child_widget in class_equipment_internal_frame.grid_slaves():
                if int(child_widget.grid_info()["column"]) > 0 and int(
                        child_widget.grid_info()["row"]) == 2 * selection_index:
                    for grandchild in child_widget.winfo_children():
                        grandchild.destroy()
                    child_widget.grid_forget()

            chooser_frame = tk.Frame(class_equipment_internal_frame)

            choices = []

            for option in current_selection_options:

                for n in range(option[1]):

                    if isinstance(option[0], tuple):

                        option_choice = tk.StringVar()
                        choices.append(option_choice)

                        conditions = option[0]
                        choice_options = ([condition.name for condition in
                                           conditions[0].__subclasses__() if
                                           issubclass(condition,
                                                      conditions[1])])

                        chooser = ttk.Combobox(chooser_frame,
                                               state="readonly",
                                               values=choice_options,
                                               textvariable=option_choice)
                        chooser.set(
                            f"Choose {conditions[0].__bases__[0].__name__.lower()}:")
                        chooser.pack(pady=2)

                    else:
                        if option[0].__subclasses__():
                            option_choice = tk.StringVar()
                            choices.append(option_choice)

                            choice_options = ([option.name for option in
                                               option[0].__subclasses__()])

                            chooser = ttk.Combobox(chooser_frame,
                                                   state="readonly",
                                                   values=choice_options,
                                                   textvariable=option_choice)
                            chooser.set(
                                f"Choose {option[0].__bases__[0].__name__.lower()}:")
                            chooser.pack(pady=2)

            chooser_frame.grid(row=selection_index * 2, column=1)

            equipment_choices["choices"][selection_index] = choices

            resize_tabs()

        for child_widget in class_equipment_internal_frame.winfo_children():
            child_widget.destroy()

        equipment_list = current_class.equipment

        equipment_choices = {"selections": [],
                             "selection_options": [],
                             "choices": []}

        num_selections = 0
        final_text = []
        final_equipment.pack_forget()

        for selection in equipment_list:
            num_options = len(selection)
            if num_options > 1:
                equipment_selection(num_selections, selection)
                equipment_choices["choices"].append([])
                get_choice_options(num_selections)
                num_selections += 1
            else:
                num_items = selection[0][1]
                final_text.append(selection[0][0]().syntax(num_items))

        final_equipment["text"] = text_join(final_text, True, "")
        final_equipment.pack(anchor="w")

        export_choices = equipment_choices.copy()
        del export_choices["selection_options"]

        character["equipment_choices"] = export_choices

    class_frame = tk.Frame(class_tab,
                           relief=tk.SUNKEN,
                           borderwidth=4)

    class_label = tk.Label(class_frame,
                           text="Class",
                           font=default_font + " 12 bold")

    class_label.pack(pady=8)

    class_choice_frame = tk.Frame(class_frame)
    class_choice_label = tk.Label(class_choice_frame,
                                  text="Choose starting class:",
                                  font=default_font + " 8")

    current_class_choice = tk.StringVar()
    class_choice_chooser = ttk.Combobox(class_choice_frame,
                                        values=list(class_list.keys()),
                                        state="readonly",
                                        textvariable=current_class_choice)

    class_choice_label.pack()
    class_choice_chooser.pack()
    class_choice_frame.pack()

    current_class_choice.trace("w", class_choice_changed)

    class_info_frame = tk.Frame(class_frame)

    class_top_label = tk.Label(class_info_frame,
                               font=default_font + " 10 bold")

    class_description = tk.StringVar()

    class_description_label = tk.Label(class_info_frame,
                                       textvariable=class_description,
                                       font=default_font + " 8",
                                       wraplength=640)
    class_description_label.pack(pady=8)

    class_features_internal_frame = tk.Frame(class_info_frame)

    class_left_frame = tk.Frame(class_features_internal_frame)

    class_basic_info_text = tk.Label(class_left_frame,
                                     font=default_font + " 8",
                                     justify=tk.CENTER,
                                     wraplength=160
                                     )

    class_basic_info_label = tk.Label(class_left_frame,
                                      text="Class Info:",
                                      font=default_font + " 8 bold")

    class_basic_info_label.pack()
    class_basic_info_text.pack()

    class_divider_1 = ttk.Separator(class_features_internal_frame,
                                    orient=tk.VERTICAL)

    class_divider_2 = ttk.Separator(class_features_internal_frame,
                                    orient=tk.VERTICAL)

    class_central_frame = tk.Frame(class_features_internal_frame)

    class_primary_attributes = tk.StringVar()

    class_skills_frame = tk.Frame(class_central_frame)

    class_skills_label = tk.Label(class_skills_frame,
                                  text="Class Skills:",
                                  font=default_font + " 8 bold"
                                  )

    class_skills_choosers_frame = tk.Frame(class_skills_frame)

    class_skills_chooser_label = tk.Label(class_skills_choosers_frame,
                                          text="Choose skill proficiencies:",
                                          font=default_font + " 8")

    class_skill_1 = tk.StringVar()
    class_skill_2 = tk.StringVar()
    class_skill_3 = tk.StringVar()
    class_skill_4 = tk.StringVar()

    class_skill_chooser_1 = ttk.Combobox(class_skills_choosers_frame,
                                         state="readonly",
                                         textvariable=class_skill_1)
    class_skill_chooser_2 = ttk.Combobox(class_skills_choosers_frame,
                                         state="readonly",
                                         textvariable=class_skill_2)
    class_skill_chooser_3 = ttk.Combobox(class_skills_choosers_frame,
                                         state="readonly",
                                         textvariable=class_skill_3)
    class_skill_chooser_4 = ttk.Combobox(class_skills_choosers_frame,
                                         state="readonly",
                                         textvariable=class_skill_4)

    class_skill_choosers_list = [class_skill_chooser_1, class_skill_chooser_2,
                                 class_skill_chooser_3,
                                 class_skill_chooser_4]
    class_skill_choices = [class_skill_1, class_skill_2, class_skill_3,
                           class_skill_4]

    for n, skill_choice in enumerate(class_skill_choices):
        name = f'Class Skill {n + 1}'
        character[name] = skill_choice

    class_skills_chooser_label.grid(row=0)
    class_skill_chooser_1.grid(row=1, pady=1, padx=4)
    class_skill_chooser_2.grid(row=2, pady=1)
    class_skill_chooser_3.grid(row=3, pady=1)
    class_skill_chooser_4.grid(row=4, pady=1)

    class_skills_label.pack()
    class_skills_frame.pack()
    class_skills_choosers_frame.pack()

    class_subclass_lvl_frame = tk.Frame(class_central_frame)
    class_subclass_lvl_label = tk.Label(class_subclass_lvl_frame,
                                        font=default_font + " 8",
                                        wraplength=200)
    class_subclass_lvl_label.pack()
    class_subclass_lvl_frame.pack()

    class_right_frame = tk.Frame(class_features_internal_frame)

    class_equipment_frame = tk.Frame(class_right_frame)

    tk.Label(class_equipment_frame,
             text="Class Equipment:",
             font=default_font + " 8 bold").pack()

    tk.Label(class_equipment_frame,
             text="You recieve the following items, plus any provided by your background:",
             font=default_font + " 8",
             wraplength=220).pack()

    class_equipment_internal_frame = tk.Frame(class_equipment_frame)

    class_equipment_frame.pack()
    class_equipment_internal_frame.pack(fill="x", expand=True)

    final_equipment = tk.Label(class_equipment_frame,
                               justify=tk.LEFT)

    class_info_frame.pack()
    class_top_label.pack()
    class_features_internal_frame.pack()

    class_data = {"Class Choice": class_choice_chooser}

    character.update(class_data)

    class_label.pack()

    return class_frame

def Background_():
    def changed_background(*args):
        background_choice_instance = background_list[background_choice.get()]()

        background_name_text_var.set(background_choice_instance.name)

        background_desc_text.delete('1.0', tk.END)
        background_desc_text.insert(tk.END, background_choice_instance.feature)

        # Check text height and adjust box
        text_height = (len(textwrap.wrap(background_choice_instance.feature,
                                         background_desc_text['width']))
                       + background_choice_instance.feature.count("\n"))
        background_desc_text['height'] = text_height

        background_desc_text.update()

        background_name_text.pack()
        background_desc_text.pack(pady=4)

        resize_tabs()

    background_frame = tk.Frame(background_tab,
                                relief=tk.SUNKEN,
                                borderwidth=4,
                                )

    background_label = tk.Label(background_frame,
                                text="Background",
                                font=default_font + " 12 bold")

    background_label.pack(pady=8)

    background_chooser_frame = tk.Frame(background_frame)

    background_chooser_label = tk.Label(background_chooser_frame,
                                        text="Choose background template:\n"
                                             "(Name and description can be "
                                             "altered if desired)",
                                        font=default_font + " 8")

    background_choice = tk.StringVar()
    background_choice.trace("w", changed_background)
    background_chooser = ttk.Combobox(background_chooser_frame,
                                      state='readonly',
                                      values=[bg.name for bg in
                                              Background.__subclasses__()],
                                      textvariable=background_choice)

    background_chooser_label.pack()
    background_chooser.pack()
    background_chooser_frame.pack()

    background_features_frame = tk.Frame(background_frame)
    background_name_text_var = tk.StringVar()
    background_name_text = tk.Entry(background_features_frame,
                                    textvariable=background_name_text_var,
                                    justify="center")

    background_desc_text = tk.Text(background_features_frame,
                                   height=10,
                                   width=80,
                                   wrap=tk.WORD)

    background_features_frame.pack(pady=8)

    background_features_dict = {'background_choice': background_chooser}

    return background_frame

window = tk.Tk()
window.title("Character Creator")
default_font = "Verdana"

# Character Creation Stuff

character_creator = tk.Frame(window)

tab_manager = ttk.Notebook(character_creator)

info_tab = ttk.Frame(tab_manager,
                     relief=tk.FLAT,
                     borderwidth=5)
race_tab = ttk.Frame(tab_manager,
                     relief=tk.FLAT,
                     borderwidth=5)
class_tab = ttk.Frame(tab_manager,
                      relief=tk.FLAT,
                      borderwidth=5)

background_tab = ttk.Frame(tab_manager,
                           relief=tk.FLAT,
                           borderwidth=5)

tab_manager.add(info_tab, text="Info")

tab_manager.add(race_tab, text="Race")

tab_manager.add(class_tab, text="Class")

tab_manager.add(background_tab, text="Background")

def change_tabs(event):
    event.widget.update_idletasks()
    current_tab = event.widget.nametowidget(event.widget.select())
    event.widget.configure(height=current_tab.winfo_reqheight(),
                           width=current_tab.winfo_reqwidth())

    character_creator.update_idletasks()
    tab_manager.update_idletasks()
    if character_creator.winfo_width() > tab_manager.winfo_reqwidth():
        tab_manager.configure(width=character_creator.winfo_width())

        current_tab_contents = \
            tab_manager.nametowidget(tab_manager.select()).winfo_children()[0]

        current_tab_contents.pack(fill="both", expand=True)

def resize_tabs():
    tab_manager.update_idletasks()
    current_tab = tab_manager.nametowidget(tab_manager.select())
    tab_manager.configure(height=current_tab.winfo_reqheight(),
                          width=current_tab.winfo_reqwidth())

tab_manager.bind("<<NotebookTabChanged>>", change_tabs)

character = {}

Title()

Info().pack()
Class().pack()
Race().pack()
Background_().pack()

tab_manager.pack()
character_creator.pack()

style = ttk.Style(window)
style.configure('TNotebook', tabposition='n')

# Move window to centre

windowWidth = window.winfo_reqwidth()
windowHeight = window.winfo_reqheight()
position_right = int(window.winfo_screenwidth() / 3 - windowHeight / 2)
position_down = int(window.winfo_screenheight() / 4 - windowHeight / 2)
window.geometry(f"+{position_right}+{position_down}")

window.mainloop()


# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-

from __future__ import absolute_import
import json
import re
import copy
from urllib.parse import urljoin

from svtplay_dl.service import Service, OpenGraphThumbMixin
from svtplay_dl.fetcher.http import HTTP
from svtplay_dl.error import ServiceError

class Sr(Service, OpenGraphThumbMixin):
    supported_domains = ['sverigesradio.se']

    def get(self):
        data = self.get_urldata()

        match = re.search(r'data-audio-type="publication" data-audio-id="(\d+)">', data)  # Nyheter
        if match:
            dataurl = "https://sverigesradio.se/sida/playerajax/" \
                      "getaudiourl?id={0}&type={1}&quality=high&format=iis".format(match.group(1), "publication")
            data = self.http.request("get", dataurl).text
            playerinfo = json.loads(data)
            yield HTTP(copy.copy(self.config), playerinfo["audioUrl"], 128)
            return
        match = re.search(r'href="(/topsy/ljudfil/\d+-mp3)"', data)  # Ladda ner
        if match:
            yield HTTP(copy.copy(self.config), urljoin("https://sverigesradio.se", match.group(1)), 128)
            return
        else:
            match = re.search(r'data-audio-type="episode" data-audio-id="(\d+)"', data)  # Ladda ner med musik
            match2 = re.search(r'data-audio-type="secondary" data-audio-id="(\d+)"', data)  # Ladda ner utan musik
            if match:
                aid = match.group(1)
                type = "episode"
            elif match2:
                aid = match2.group(1)
                type = "secondary"
            else:
                yield ServiceError("Can't find audio info")
                return

        dataurl = "https://sverigesradio.se/sida/playerajax/" \
                  "getaudiourl?id={0}&type={1}&quality=high&format=iis".format(aid, type)
        data = self.http.request("get", dataurl).text
        playerinfo = json.loads(data)
        yield HTTP(copy.copy(self.config), playerinfo["audioUrl"], 128, output=self.output)


#Exercício 2) (parecida com exercício 11 dos slides) Implemente um programa que converta da notação
#de 24 horas para a notação de 12 horas. Por exemplo, o programa deve converter 14:25 em 2:25 P.M.
#Outro exemplo, converte 8:30 em 8:30 A.M. A entrada é dada em dois inteiros (horas e minutos).
#Inclua um loop que permita que o usuário repita esse cálculo para novos valores de entrada todas as
#vezes que desejar.

def Horas(H,M):
    if H < 12:
        print (f"O Horáro convertido é {H}:{M} A.M")
    else:
        h = 12 - H

        h = h *-1
        print (f"O Horário convertido é {h}:{M} P.M")
######################################################

while True :

    horas = int(input("Digite as horas:"))
    minutos = int(input("Digite os minutos:"))

    Horas(horas,minutos)

    cont = input("Digite N para encerrar e S para continuar:")

    if cont == "N":
        break
    if cont == "n":
        break


from django.urls import path, include
from . import views
from django.conf import settings
from django.conf.urls.static import static

app_name = "framesharings"

urlpatterns = [
    path('frame',views.frame,name='frame'),
    path('framedetail/<int:id>',views.framedetail,name='framedetail'),
    path('framecreate', views.framecreate,name='framecreate'),
    path('frameupdate/<int:id>', views.frameupdate,name='frameupdate'),
    path('framedelete/<int:id>',views.framedelete,name='framedelete'),
    path('like_ajax/', views.like_ajax, name = 'like_ajax'),
    path('save_ajax/', views.save_ajax, name = 'save_ajax'),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)


class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        '''
        !! 다른 사람의 풀이를 참고하였기 때문에 답안에 설명을 첨부합니다. !!

        현재 타이밍에 가능한 행동은 총 4가지가 있습니다 (사실 3가지여도 될 거 같은데, 확인해봐야 함)
        1 주식을 산다
        2 주식을 사지 않는다
        3 주식을 판다
        4 주식을 팔지 않는다

        이때 2와 4 행동의 경우 아무것도 하지 않는다는 점에서 동일하지만, 현재 주식을 보유하고 있는지에 따라 달라집니다.

        각각의 행동을 취할 때 기존의 탐색된 결과를 바탕으로 최적의 결과를 얻으려면 다음과 같이 식을 세워 dynamic programming으로 풀 수 있습니다.
        1 주식을 산다 : 주식을 판 다음에는 바로 살 수 없기 때문에 주식을 사지 않았을 때의 결과에서 주식의 가격만큼 빼줍니다.
        2 주식을 사지 않는다 : 주식을 사지 않는 행동을 위해서는 주식을 사지 않았거나, 주식을 팔았을 때의 최고 결과를 가져옵니다.
        3 주식을 판다 : 주식을 팔기 위해서는 주식을 보유해야 하므로, 주식을 샀거나, 팔지 않았을 때의 최고 결과에서 주식 가격을 더해줍니다.
        4 주식을 팔지 않는다 : 주식을 팔기 위해서는 주식을 보유해야 하므로, 주식을 샀거나, 팔지 않았을 때의 최고 결과를 가져옵니다.

        위의 네 개의 식으로 dynamic programming으로 전체 주식 가격에 대해 탐색 한 후, 모든 주식을 다 판 결과가 최고의 결과이므로, 주식을 사지 않거나, 팔았을 때 결과 중 최고를 리턴해주면 됩니다.

        '''
        if len(prices) < 2: return 0

        # buy not buy sell not sell
        reward = [-prices[0], 0, 0, -prices[0]]

        for price in prices[1:]:
            reward = reward[1] - price, max(reward[1], reward[2]), max(reward[0], reward[3]) + price, max(reward[0], reward[3])

        return max(reward[1], reward[2])


# Authors   : Rayan Oukani
# Emails    : roukani@umass.edu
# Spire IDs : 34462508

# This is a starting example. You do NOT need to modify this function
def clear_tasks(list):
  list.clear()

# Create an empty list
to_do_list = []

# Below please implement the add_task, delete_task, and move_task functions
# ----- YOUR CODE STARTS HERE -----

N = 0

def add_task(lst, strng):
    lst.append(strng)
    ack = F"Task successfully added. {int(len(lst))} tasks remaining."
    return ack

def delete_task(lst, strng):
    lst.remove(strng)
    ack = F"Task successfully deleted. {int(len(lst))} tasks remaining."
    return ack

def move_task(lst, from_index, to_index):
    old = lst[from_index]
    lst.pop(from_index)
    lst.insert(to_index,old)

    ack = F"Task '{old}' successfully moved to index {to_index}"
    return ack

# ===== YOUR CODE ENDS HERE =====

# You can use the code below to help test your functions

# Uncomment the following 5 lines (i.e. remove the # characters on each line) to test add_task
print(add_task(to_do_list, 'zybook reading'))
print(add_task(to_do_list, 'do laundry'))
print(add_task(to_do_list, 'cics110 lab 3'))
print(add_task(to_do_list, 'math homework'))
print(add_task(to_do_list, 'grocery shopping'))

# Uncomment the following 2 lines (i.e. remove the # characters on each line) to test delete_task
print(delete_task(to_do_list, 'zybook reading'))
print(delete_task(to_do_list, 'math homework'))

# Uncomment the following 5 lines (i.e. remove the # characters on each line) to test move_task
to_do_list = ['zybook reading', 'do laundry', 'cics110 lab 3', 'math homework', 'grocery shopping']
print(move_task(to_do_list, 2, 0))
print('Current to-do list:', to_do_list, '\n')
print(move_task(to_do_list, 1, 4))
print('Current to-do list:', to_do_list, '\n')


import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler

dataset = pd.read_csv('Position_Salaries.csv')
X = dataset.iloc[:, 1:-1].values
y = dataset.iloc[:, -1].values
# print(X)
# print(y)

y = y.reshape(len(y), 1)
# print(y)

# Feature Scaling

sc_X = StandardScaler()
sc_y = StandardScaler()

X = sc_X.fit_transform(X)
y = sc_y.fit_transform(y)

print(X)
print(y)

y = y.flatten()
print(y)

svr = SVR(kernel='rbf')
svr.fit(X, y)

y_pred = svr.predict(sc_X.transform([[6.5]]))  # We have to transform our 6.5 value as well
print(y_pred)

y_pred = sc_y.inverse_transform(y_pred)
print(y_pred)

# Polynomial Linear Regression

plt.scatter(sc_X.inverse_transform(X), sc_y.inverse_transform(y), color='red')
plt.scatter(sc_X.inverse_transform(X), sc_y.inverse_transform(svr.predict(X)), color='orange')
plt.plot(sc_X.inverse_transform(X), sc_y.inverse_transform(svr.predict(X)), color='blue')
plt.title('Salary vs Position Level (SVR)')
plt.xlabel('Position Level')
plt.ylabel('Salary')
plt.show()

X_grid = np.arange(min(sc_X.inverse_transform(X)), max(sc_X.inverse_transform(X)), 0.1)
X_grid = X_grid.reshape((len(X_grid), 1))
plt.scatter(sc_X.inverse_transform(X), sc_y.inverse_transform(y), color='red')
plt.plot(X_grid, sc_y.inverse_transform(svr.predict(sc_X.transform(X_grid))), color='blue')
plt.title('Salary vs Position Level (SVR)')
plt.xlabel('Position level')
plt.ylabel('Salary')
plt.show()


import bisect
N = int(input())

# O(nlogn)
nums = set()
for _ in range(N):
    nums.add(int(input()))
nums = sorted(list(nums))

twoSum = set()
# (N^2 + nlogn)
for i in range(N):
    for j in range(i,N):
        twoSum.add(nums[i]+nums[j])
twoSum = sorted(list(twoSum))

def solve():
    # O(n^2logn)
    for i in reversed(range(N)):
        for j in range(i,-1,-1):
            targetNum = nums[i]-nums[j]
            findNum = twoSum[bisect.bisect_left(twoSum,targetNum)]
            if targetNum==findNum:
                return nums[i]
print( solve() )


from lightkurve import search_lightcurve, LightCurveCollection, read
from lightkurve.correctors import CBVCorrector
import numpy as np
from lightkurve import KeplerLightCurveFile
import matplotlib.pyplot as plt

lc_collection = search_lightcurve('KIC 10001167', author='Kepler').download_all()
test = CBVCorrector(lc_collection[5])
period = 120.3903
print(test.cbvs)
plt.plot(np.mod(test.cbvs[0].time.value, period)/period, test.cbvs[0].to_designmatrix().X+np.array([1-x/16 for x in range(0, 16)]))
plt.figure()
plt.plot(np.mod(lc_collection[5].time.value, period)/period, lc_collection[5].normalize().flux, 'k--')
plt.show()
test.cbvs[0].plot()
plt.show()
# for i in range(0, len(lc_collection)):
#     lc = lc_collection[i]
#     cbvCorrector = CBVCorrector(lc, )
#     cbvCorrector.correct(cbv_indices=[np.arange(1, 4)])
    # lc_collection[i] = cbvCorrector.corrected_lc
#     cbvCorrector.corrected_lc.to_fits(f"lc_{i}.fits", overwrite=True)
# plt.figure()
ax = plt.axes()
period = 120.3903
for i in range(0, len(lc_collection)):
    if i==6:
        break
    lc = read(f"lc_{i}.fits")
    phase = np.mod(lc.time.value, period)/period
    plt.plot(phase, lc.normalize().flux, '.', markersize=1)
plt.show()


"""empty message

Revision ID: fca1eb7ab8bf
Revises: b2381d05d435
Create Date: 2021-06-25 11:54:33.884309

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'fca1eb7ab8bf'
down_revision = 'b2381d05d435'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('files', sa.Column('folder', sa.String(length=512), nullable=True))
    op.execute("UPDATE files SET folder = '/h/'")
    op.alter_column('files', 'folder', nullable=False)
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('files', 'folder')
    # ### end Alembic commands ###


# Brute forces all dates in a century to find dates which are palindromes (the same value forwards and backwards, i.e. 9/13/19)
# Ben Rongey

count = 0
m = list(range(1, 13))
d = list(range(1, 31))
y = list(range(0, 101))

for year in y:

    for day in d:

        for month in m:

            og_date = str(month) +str(day) +str(year)
            palin_num = og_date[::-1]

            if og_date == palin_num:
                print (str(month) + '/' + str(day) + '/' + str(year))
                count = count + 1

print ("In a century there are " + str(count) + " palindrome dates!")


import tkinter as tk

class MyFrame(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master, width=800, height=500, bg="gray")
        self.master = master

        # menu
        menubar = tk.Menu(self)
        menu_file = tk.Menu(menubar, tearoff=False)
        menu_file.add_command(label="test", command=lambda text="    ":self.update_text(text))
        menu_file.add_command(label="exit", command=self.master.destroy)
        menubar.add_cascade(label="file", menu=menu_file)
        self.master.config(menu=menubar)

        # prefix part
        self.frame = tk.Frame(master=self,)
        self.frame.pack(anchor=tk.W, padx=10, pady=10)

        self.prefix_label = tk.Label(master=self.frame, text="Prefix")
        self.prefix_label.pack(side=tk.LEFT, padx=10, pady=10)

        self.prefix_entry = tk.Entry(master=self.frame, text="> ")
        self.prefix_entry.insert(tk.END, '> ')
        self.prefix_entry.pack(side=tk.LEFT, padx=10, pady=10)

        a = """Ctrl + Y -> change background color to yellow
b
c
d
        """
        self.usage_label = tk.Label(master=self, text=a)
        self.usage_label.pack(side=tk.RIGHT, padx=10, pady=10)

        self.frame2 = tk.Frame(master=self)
        self.frame2.pack(anchor=tk.W, padx=10, pady=10)

        self.text_box = tk.Text(master=self.frame2, height=10)
        self.text_box.insert(1.0, '--example\naa\nbb')
        self.text_box.pack(fill=tk.X, padx=10, pady=10)

        self.text_box2 = tk.Text(master=self.frame2, height=10)
        self.text_box2.pack(fill=tk.X, padx=10, pady=10)

        self.button2 = tk.Button(master=self.frame2, text="update", command=self.update_text_with_entry)
        self.button2.pack(side=tk.RIGHT, anchor=tk.NE)

        self.update_text_with_entry()

        self.button = tk.Button(master=self.frame2, text="button", command=self.read_text)
        self.button.pack(side=tk.RIGHT, anchor=tk.NE)

        self.master.bind('<Control-Key-q>', lambda event, text="    ": self.update_text(text))
        self.master.bind('<Control-Key-e>', lambda event, text="> ": self.update_text(text))
        self.master.bind('<Control-Key-y>', lambda event, color="yellow": self.update_frame_color(color))

        self.pack(padx=10, pady=10, ipadx=10, ipady=10)
        # self.pack_propagate(0)

    def read_text(self):
        # print(self.text_box.get('1.0', 'end-1c')
        for line in self.text_box.get('1.0', 'end-1c').splitlines():
            print('> ', line)

    def update_text_bind(self, event):
        self.update_text("    ")

    def update_text_with_entry(self):
        self.update_text(self.prefix_entry.get())

    def update_text(self, text):
        self.text_box2.config(state=tk.NORMAL)
        self.text_box2.delete(1.0, tk.END)
        # for line in self.text_box.get('1.0', 'end-1c'):
        #     self.text_box2.insert(line)
        self.text_box2.insert(1.0,
                              "".join([text + line + '\n' for line in self.text_box.get('1.0', 'end-1c').splitlines()]))

        self.text_box2.config(state=tk.DISABLED)

    def update_frame_color(self, color):
        self.frame["bg"] = color

app = tk.Tk()
app.geometry("1600x900")
frame = MyFrame(master=app)
frame.mainloop()


import logging

import pandas as pd
from nltk import tokenize
from nltk.sentiment.vader import SentimentIntensityAnalyzer

from lib.settings import DATA_DIR, LOG_LEVEL
from lib.characters import findAllMentionedCharacters

comments = pd.read_csv(DATA_DIR / 'comments.csv')
comments['Body'] = comments['Body'].fillna('')
analyzer = SentimentIntensityAnalyzer()
logging.basicConfig(level=LOG_LEVEL)
logger = logging.getLogger(__name__)

character_mention_data = {
	'Name': [],
	'Comment ID': [],
	'Sentence': [],
	'Compound Score': []
}

comment_sentiment_data = {
	'Comment ID': [],
	'Compound Score': []
}

for comment in comments.itertuples(name='Comment', index=False):
	id = comment[0]
	body = comment[4]
	logger.info("Processing comment {}: {}".format(id, body))

	sentence_list = tokenize.sent_tokenize(body)
	comment_scores = []
	for sentence in sentence_list:
		score = analyzer.polarity_scores(sentence)
		comment_scores.append(score['compound'])

		mentions = findAllMentionedCharacters(sentence)
		for mention in mentions:
			logger.info("Found mention of {} in comment {}. Sentence: {}".format(mention, id, sentence))
			character_mention_data['Name'].append(mention)
			character_mention_data['Comment ID'].append(id)
			character_mention_data['Sentence'].append(sentence)
			character_mention_data['Compound Score'].append(score['compound'])

	mean_comment_score = pd.np.mean(comment_scores) if len(comment_scores) != 0 else 0.0
	logger.info("Scores for comment {}: {}".format(id, mean_comment_score))
	comment_sentiment_data['Comment ID'].append(id)
	comment_sentiment_data['Compound Score'].append(mean_comment_score)

character_mentions = pd.DataFrame(character_mention_data)
comment_sentiments = pd.DataFrame(comment_sentiment_data)

character_mentions.to_csv(DATA_DIR / 'character-mentions.csv', index=False)
logger.critical("Saved {} character mentions to {}".format(len(character_mentions.index), DATA_DIR / 'character-mentions.csv'))
comment_sentiments.to_csv(DATA_DIR / 'comment-sentiments.csv', index=False)
logger.critical("Saved {} comment sentiments to {}".format(len(comment_sentiments.index), DATA_DIR / 'comment-sentiments.csv'))


from torch.utils.data import Dataset
from PIL import Image
import os
import random

def make_dataset(dir):
    images = []
    for f in sorted(os.listdir(dir)):
        images.append(os.path.join(dir, f))
    return images

class Unaligned_dataset(Dataset):
    def __init__(self, rootA, rootB, transformA=None, transformB=None,
                 randomB=True):
        self.data_pathA = rootA
        self.data_pathB = rootB
        self.transformA = transformA
        self.transformB = transformB
        self.imagesA = make_dataset(self.data_pathA)
        self.imagesB = make_dataset(self.data_pathB)

        self.randomB = randomB

    def __getitem__(self, index):
        indexA = index % len(self.imagesA)
        if self.randomB:
            indexB = random.randint(0, len(self.imagesB) - 1)
        else:
            indexB = index % len(self.imagesB)
        imgA = Image.open(self.imagesA[indexA]).convert('RGB')
        imgB = Image.open(self.imagesB[indexB]).convert('RGB')
        if self.transformA is not None:
            imgA = self.transformA(imgA)
        if self.transformB is not None:
            imgB = self.transformB(imgB)
        return {'A': imgA, 'B': imgB}

    def __len__(self):
        return max(len(self.imagesA), len(self.imagesB))

class ImageFolder(Dataset):
    def __init__(self, root, transform=None):
        self.data_path = root
        self.transform = transform
        self.images = make_dataset(self.data_path)

    def __getitem__(self, index):
        img = Image.open(self.images[index]).convert('RGB')
        if self.transform is not None:
            img = self.transform(img)
        return img

    def __len__(self):
        return len(self.images)


"""
given a matrix, find its transpose without using inbuilt python function
i_q
"""

def transpose(matrix):
    transpose_mat = [[], [], []]
    for i in matrix:
        for j, k in enumerate(i):
            transpose_mat[j].append(k)
    return transpose_mat

matrix_input = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(transpose(matrix_input))


# %%
import os

import numpy as np
import pandas as pd

random_seed = 1
np.random.seed(random_seed)

# %%
stations_df = pd.read_csv('./data/stations.csv')
stations_dict = stations_df.groupby(['common_id']).first().to_dict('index')
stations_dict['2386-ch']['lower_limit'] = 38_000.0
stations_dict['2386-ch']['upper_limit'] = 40_000.0
stations_dict['2720050000-de']['lower_limit'] = 20.0
stations_dict['2720050000-de']['upper_limit'] = 500.0
stations_dict['36022-ie']['lower_limit'] = 20.0
stations_dict['36022-ie']['upper_limit'] = 175.0
stations_dict['39003-ie']['lower_limit'] = 10.0
stations_dict['39003-ie']['upper_limit'] = 225.0
stations_dict['42960105-de']['lower_limit'] = -10.0
stations_dict['42960105-de']['upper_limit'] = 275.0
stations_dict['auto-1003803']['lower_limit'] = -10.0
stations_dict['auto-1003803']['upper_limit'] = 275.0

# %%
for common_id, station_dict in stations_dict.items():
    fp = f'./data/classified_raw/{common_id}_outliers_classified.parquet'
    if not os.path.exists(fp):
        continue
    raw_classified_df = pd.read_parquet(fp)
    print(f'Removing for {common_id}, lower limit '
          f'{station_dict["lower_limit"]}, upper limit '
          f'{station_dict["upper_limit"]}')
    print('Removing following rows:')
    mask = (raw_classified_df['water_level'] < station_dict['lower_limit']) | (
            raw_classified_df['water_level'] > station_dict['upper_limit'])
    print(raw_classified_df[mask])
    raw_classified_df[~mask].to_parquet(
        f'././data/classified/{common_id}_outliers_classified.parquet')
    print()


# 타임머신
# https://www.acmicpc.net/problem/11657
# https://github.com/yhs3434/Algorithms

from collections import deque

N, M = map(int, input().split(' '))

weight = {}
for i in range(1, N+1):
    weight[i] = {}

for _ in range(M):
    u, v, w = map(int, input().split(' '))

    if v not in weight[u]:
        weight[u][v] = w
    else:
        weight[u][v] = min(weight[u][v], w)

dist = [float('inf')] * (N+1)
dist[1] = 0

loopCnt = 0
loopFlag = False

n = 0
while n <= N:
    for i in range(1, N+1):
        here = i
        for there in weight[here]:
            if dist[here] + weight[here][there] < dist[there]:
                if n == N:
                    loopFlag = True
                dist[there] = dist[here] + weight[here][there]
    n+=1

if loopFlag:
    print(-1)
else:
    for i in range(2, N+1):
        if dist[i] == float('inf'):
            print(-1)
        else:
            print(dist[i])


def factorial(n: int):
    if n < 0:
        return 0
    else:
        result = 1
        for i in range(n):
            result *= (i + 1)

        return result

def permutation(n: int, m: int):
    if n < 0 or m < 0 or n - m < 0:
        return 0

    result = 1
    for i in range(n - m, n):
        result *= (i + 1)

    return result

def combination(n: int, m: int) -> int:
    if n < 0 or m < 0 or n - m < 0:
        return 0

    m = min(m, n - m)

    if m < 1:
        return 1
    else:
        return permutation(n, m) // factorial(m)


import pyodbc
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

#Connecting to SQL Server
server_name = "DESKTOP-5F0PH62\SQLEXPRESS"
db_name = "Youtube_Trend_DW_ALL"

server = "Server="+str(server_name)
db = "Database="+str(db_name)
key = "Driver={SQL Server Native Client 11.0};"+server+";"+db+";"+"Trusted_Connection=yes;"

cnxn = pyodbc.connect(key)

sql= """

    select Top 50 Title as Videos,Likes,Most_Watched,Country,Category,Year_ as Year  from Trends_Fact as T
    inner join Video_DIM as V
    on v.VideoKey=T.VideoKey
    inner join Channel_DIM as C
    on C.ChannelKey=T.ChannelKey
    inner join Date_DIM as D
    on D.DateKey = T.DateKey
    Where  Top_Rated>80000
    order by Top_Rated desc
"""
df=pd.read_sql(sql, cnxn)
df.head(50)

x1=np.array(df['Likes'])
x2=np.array(df['Most_Watched'])

x3=np.array(df['Country'])
x4=np.array(df['Category'])

#Scattered Plot
plt.plot()
plt.title('Youtube Dataset')
plt.scatter(x1,x2)
plt.show()

plt.plot()
plt.title('Most Watched videos by category')
plt.scatter(x3,x4)
plt.xlabel('Country')
plt.ylabel('Category')
plt.show()

#Numpy Zip method
x=np.array(list(zip(x1,x2)))
colors = ['b','g','r']
markers = ['o','v','s']

plt.xlabel('Likes')
plt.ylabel('Most_Watched')

kmeans= KMeans(n_clusters=3).fit(x)

plt.scatter(kmeans.cluster_centers_[:,0], kmeans.cluster_centers_[:,1], s=200, c='Black', label='Trends')

for i,j in enumerate(kmeans.labels_):
    plt.plot(x1[i], x2[i], color=colors[j], marker= markers[j])

plt.legend()
plt.show()


import pygame
from button import Button
from colors import *
from font import *
from network import Network

pygame.font.init()

WIDTH = 700
HEIGHT = 500

window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Client")

def half_width(text):
    return WIDTH / 2 - text.get_width() / 2

def redraw_window(window, game, p):
    window.fill(GREY_DR)

    if not (game.connected()):
        font = create_font(25, bold=True)
        text = font.render("Waiting for Player...", 1, YELLOW_DR, True)
        window.blit(text, (half_width(text), HEIGHT / 2 - text.get_height() / 2))
    else:
        font = create_font(20)
        text = yellow_font(font, "Your Move: ")
        window.blit(text, (20, 50))

        text = yellow_font(font, "Opponent's Move: ")
        window.blit(text, (330, 50))

        move1 = game.get_player_move(0)
        move2 = game.get_player_move(1)

        if game.both_went():
            text1 = white_font(font, move1)
            text2 = white_font(font, move2)
        else:
            if game.p1Went and p == 0:
                text1 = white_font(font, move1)
            elif game.p1Went:
                text1 = white_font(font, "Locked In")
            else:
                text1 = white_font(font, "Waiting...")
            if game.p2Went and p == 1:
                text2 = white_font(font, move2)
            elif game.p2Went:
                text2 = white_font(font, "Locked In")
            else:
                text2 = white_font(font, "Waiting...")

        if p == 1:
            window.blit(text2, (100, 50))
            window.blit(text1, (450, 50))
        else:
            window.blit(text1, (100, 50))
            window.blit(text2, (450, 50))

        for btn in buttons:
            btn.draw(window)

    pygame.display.update()

buttons = [
    Button("Rock", 50, 300, GREY),
    Button("Paper", 250, 300, PURPLE_DR),
    Button("Scissor", 450, 300, BLUE_DR),
    Button("Lizard", 350, 200, ORANGE_DR),
    Button("Spock", 150, 200, GREEN_DR),
]

def main():
    run = True
    clock = pygame.time.Clock()
    n = Network()
    player = int(n.get_p())
    print("You are player", player)

    while run:
        clock.tick(60)
        try:
            game = n.send("get")
        except:
            run = False
            print("Couldn't get game")
            break

        if game.both_went():
            redraw_window(window, game, player)
            pygame.time.delay(500)
            try:
                game = n.send("reset")
            except:
                run = False
                print("Couldn't get game")
                break

            font = create_font(25, bold=True)
            if (game.winner() == 1 and player == 1) or (game.winner() == 0 and player == 0):
                text = green_font(font, "You Won!")
            elif game.winner() == -1:
                text = green_font(font, "Tie Game!")
            else:
                text = green_font(font, "You Lost...")

            window.blit(text, (half_width(text), HEIGHT - 40))
            pygame.display.update()
            pygame.time.delay(2000)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
                pygame.quit()

            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
                for btn in buttons:
                    if btn.click(pos) and game.connected():
                        if player == 0:
                            if not game.p1Went:
                                n.send(btn.text)
                        else:
                            if not game.p2Went:
                                n.send(btn.text)

        redraw_window(window, game, player)

def menu_screen():
    run = True
    clock = pygame.time.Clock()

    while run:
        clock.tick(60)
        window.fill(GREY_DR)
        font = create_font(25, bold=True)
        text = yellow_font(font, "Click to Play!")
        window.blit(text, (half_width(text), HEIGHT / 2 - text.get_height() / 2))
        pygame.display.update()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                run = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                run = False

    main()

while True:
    menu_screen()