import os
import random
import timeit
import prettytable


def GUI():
    current_list=[]
    while True:
        print("1 - Generate a List")
        print("2 - Show Current List")
        print("3 - Bubble Sort")
        print("4 - Strand Sort")
        print("5 - Best Case")
        print("6 - Average Case")
        print("7 - Worst Case")
        print("8 - Clear screen")
        print("0 - Exit")
        value = input("> ")
        match value:
            case "1":
                n=int(input("Select lenght of the list: ")) 
                current_list = generate_list(n)
                print("Generated list: {}\n".format(current_list))
            case "2":
                show_list(current_list)
            case "3":
                steps=int(input("Please select the number of steps: "))
                current_list=bubble_sort(current_list,steps)
            case "4":
                steps=int(input("Please select the number of steps: "))
                current_list=strand_sort(current_list,steps)
            case "5":
                print("Please choose the sorting algorithm: ")
                print("1 - Bubble Sort")
                print("2 - Strand Sort")
                s=input(">")
                match s:
                    case "1":
                        best_case(bubble_sort)
                    case "2":
                        best_case(strand_sort)
                    case _:
                        print("Invalid command")
            case "6":
                print("Please choose the sorting algorithm: ")
                print("1 - Bubble Sort")
                print("2 - Strand Sort")
                s=input("> ")
                match s:
                    case "1":
                        average_case(bubble_sort)
                    case "2":
                        average_case(strand_sort)
                    case _:
                        print("Invalid command")
            case "7":
                print("Please choose the sorting algorithm: ")
                print("1 - Bubble Sort")
                print("2 - Strand Sort")
                s=input("> ")
                match s:
                    case "1":
                        worst_case(bubble_sort)
                    case "2":
                        worst_case(strand_sort)
                    case _:
                        print("Invalid command")
            case "8":
                cls = lambda: os.system('cls')
                cls()
            case "0":
                exit()
            case _:
                print("Invalid command")

def bubble_sort(l,step):
    if step!=0:
        print("Starting list: {}".format(l))
    current_step=0
    for i in range(len(l)-1):
        sorted=True
        for j in range(len(l)-1-i):
            if l[j]>l[j+1]:
                l[j],l[j+1]=l[j+1],l[j]
                sorted=False
                current_step+=1
                if step!=0 and current_step % step == 0:
                    print("List after {} steps: {}".format(current_step,l))
        if sorted:
            break
    if step!=0:
        print("Sorted list: {}\n".format(l))
    return l

def strand_sort(l,step):
    if step!=0:
        print("Starting list: {}\n".format(l))
    current_step=0
    aux_list=[]
    final_list=[]
    while l:
        aux_list=[]
        aux_list.append(l.pop(0))
        current_step+=1
        strand_sort_message(current_step,l,aux_list,final_list,step)
        for i in l.copy():
            if aux_list[-1] <= i:
                aux_list.append(l.pop(l.index(i)))
                current_step+=1
                strand_sort_message(current_step,l,aux_list,final_list,step)
        if final_list:
            i=0
            while aux_list:
                if aux_list[-1]>final_list[i]:
                    i+=1
                else:
                    final_list.insert(i,aux_list[-1])
                    aux_list.pop()
                    current_step+=1
                    strand_sort_message(current_step,l,aux_list,final_list,step)
                    i=0
        else:
            final_list=aux_list.copy()
            aux_list.clear()
            current_step+=1
            strand_sort_message(current_step,l,aux_list,final_list,step)
    if step!=0:
        print("Sorted list: {}\n".format(final_list))
    return final_list

def strand_sort_message(current_step,l,aux_list,final_list,step):
    if step!=0 and current_step%step==0:
        print("Lists after {} steps:".format(current_step))
        print("Starting list: {}".format(l))
        print("Working list: {}".format(aux_list))
        print("Final list: {}\n".format(final_list))

def generate_list(n):
    return [random.randint(0,100) for _ in range(n)]

def show_list(l):
    print("Current list: {}\n".format(l))

def best_case(sort):
    n=500
    times=[]
    while n<=8000:
        time=timeit.timeit(lambda: sort(list(range(1,n+1)),0),number=1)
        times.append((n,time))
        n*=2
    display_times(times)    

def worst_case(sort):
    n=500
    times=[]
    while n<=8000:
        time=timeit.timeit(lambda: sort(list(range(n,0,-1)),0),number=1)
        times.append((n,time))
        n*=2
    display_times(times)

def average_case(sort):
    n=500
    times=[]
    while n<=8000:
        time=timeit.timeit(lambda: sort(generate_list(n),0),number=1)
        times.append((n,time))
        n*=2
    display_times(times)

def display_times(times):
    x=prettytable.PrettyTable()
    x.field_names=["List lenght","Time"]
    x.add_rows(times)
    print(x)

if __name__=="__main__":
    GUI()