# importing the required modules  
import fileinput
from tkinter import *
from msilib.schema import Font
import tkinter as tk                    # importing the tkinter module as tk  
from tkinter import ttk                 # importing the ttk module from the tkinter library  
from tkinter import messagebox          # importing the messagebox module from the tkinter library  
import sqlite3 as sql                   # importing the sqlite3 module as sql  
from collections import defaultdict
from typing import Iterable, List, TypeVar
 
# defining the function to add tasks to the list  
def add_task():  
    # getting the string from the entry field  
    task_string = task_field.get()  
    # checking whether the string is empty or not  
    if len(task_string) == 0:  
        # displaying a message box with 'Empty Field' message  
        messagebox.showinfo('Error', 'Field is Empty.')  
    else:  
        # adding the string to the tasks list  
        tasks.append(task_string)  
        # using the execute() method to execute a SQL statement  
        the_cursor.execute('insert into tasks values (?)', (task_string ,))  
        # calling the function to update the list  
        list_update()  
        # deleting the entry in the entry field  
        task_field.delete(0, 'end')  

def sort_tasks():
    tasks.sort()
    temp_list = list(task_listbox.get(0, tk.END))
    temp_list.sort(key=str.lower)
    # delete contents of present listbox
    task_listbox.delete(0, tk.END)
    # load listbox with sorted data
    for item in temp_list:
        task_listbox.insert(tk.END, item)
    
# defining the function to update the list  
def list_update():  
    # calling the function to clear the list  
    clear_list()  
    # iterating through the strings in the list  
    for task in tasks:  
        # using the insert() method to insert the tasks in the list box  
        task_listbox.insert('end', task)  
  
# defining the function to delete a task from the list  
def delete_task():  
    # using the try-except method  
    try:  
        # getting the selected entry from the list box  
        the_value = task_listbox.get(task_listbox.curselection())  
        # checking if the stored value is present in the tasks list  
        if the_value in tasks:  
            # removing the task from the list  
            tasks.remove(the_value)  
            # calling the function to update the list  
            list_update()  
            # using the execute() method to execute a SQL statement  
            the_cursor.execute('delete from tasks where title = ?', (the_value,))  
    except:  
        # displaying the message box with 'No Item Selected' message for an exception  
        messagebox.showinfo('Error', 'No Task Selected. Cannot Delete.')  
def markcompleted():
    
    marked=task_listbox.curselection()
    temp=marked[0]
    #store the text of selected item in a string
    temp_marked=task_listbox.get(marked)
    
    #update it 
    temp_marked=temp_marked+" ✔"
    task_listbox.delete(temp)
    task_listbox.insert(temp,temp_marked) 
     
           
# function to delete all tasks from the list  
def delete_all_tasks():  
    # displaying a message box to ask user for confirmation  
    message_box = messagebox.askyesno('Delete All', 'Are you sure?')  
    # if the value turns to be True  
    if message_box == True:  
        # using while loop to iterate through the tasks list until it's empty   
        while(len(tasks) != 0):  
            # using the pop() method to pop out the elements from the list  
            tasks.pop()  
        # using the execute() method to execute a SQL statement  
        the_cursor.execute('delete from tasks')  
        # calling the function to update the list  
        list_update()  
  
# function to clear the list  
def clear_list():  
    # using the delete method to delete all entries from the list box  
    task_listbox.delete(0, 'end')  
  
# function to close the application  
def close():  
    # printing the elements from the tasks list  
    print(tasks)  
    # using the destroy() method to close the application  
    guiWindow.destroy()  
  
# function to retrieve data from the database  
def retrieve_database():  
    # using the while loop to iterate through the elements in the tasks list  
    while(len(tasks) != 0):  
        # using the pop() method to pop out the elements from the list  
        tasks.pop()  
    # iterating through the rows in the database table  
    for row in the_cursor.execute('select title from tasks'):  
        # using the append() method to insert the titles from the table in the list  
        tasks.append(row[0])  
def save():
    file = open('To Do.txt','w')
    for  task in tasks:
        file.write(task + "\n")
def open_file():
    file1 = open("To Do.txt") 
    for task in file1:
        tasks.append(task.strip())
        task_listbox.insert(tk.END, task.strip())
   
    pass
    # main function  
if __name__ == "__main__":  
    # creating an object of the Tk() class  
    guiWindow = tk.Tk()  
    # setting the title of the window  
    guiWindow.title("To-Do List Manager")  
    # setting the geometry of the window  
    guiWindow.geometry("500x450+750+250")  
    # disabling the resizable option  
    guiWindow.resizable(0, 0)  
    # setting the background color to #FAEBD7  
    guiWindow.configure(bg = "#dfc5fe")  
  
    # using the connect() method to connect to the database  
    the_connection = sql.connect('listOfTasks.db')  
    # creating the cursor object of the cursor class  
    the_cursor = the_connection.cursor()  
    # using the execute() method to execute a SQL statement  
    the_cursor.execute('create table if not exists tasks (title text)')  
  
    # defining an empty list  
    tasks = []  
      
    # defining frames using the tk.Frame() widget  
    header_frame = tk.Frame(guiWindow, bg = "#dfc5fe")  
    functions_frame = tk.Frame(guiWindow, bg = "#dfc5fe")  
    listbox_frame = tk.Frame(guiWindow, bg = "#dfc5fe")  
  
    # using the pack() method to place the frames in the application  
    header_frame.pack(fill = "both")  
    functions_frame.pack(side = "left", expand = True, fill = "both")  
    listbox_frame.pack(side = "right", expand = True, fill = "both")  
      
    # defining a label using the ttk.Label() widget  
    header_label = ttk.Label(  
        header_frame,  
        text = "Your To-Do List",  
        font = ("Microsoft Himalaya", "36", "bold"),  
        background = "#dfc5fe",  
        foreground = "#4b0082"  
    )  
    # using the pack() method to place the label in the application  
    header_label.pack(padx = 20, pady = 20)  
  
    # defining another label using the ttk.Label() widget  
    task_label = ttk.Label(  
        functions_frame,  
        text = "Enter the Task:",  
        font = ("Microsoft Himalaya", "20", "bold"),  
        background = "#dfc5fe",  
        foreground = "#4b0082"  
    )  
    # using the place() method to place the label in the application  
    task_label.place(x = 30, y = 0)  
      
    # defining an entry field using the ttk.Entry() widget  
    task_field = ttk.Entry(  
        functions_frame,  
        font = ("Consolas", "11"),  
        width = 19 ,  
        background = "#FFF8DC",  
        foreground = "#4b0082"  
    )  
    # using the place() method to place the entry field in the application  
    task_field.place(x = 30, y = 40)  
  
    # adding buttons to the application using the ttk.Button() widget  
    add_button = ttk.Button(  
        functions_frame,  
        text = "Add Task",  
        width = 24,  
        command = add_task  
    )  

    sort_button = ttk.Button(  
        functions_frame,  
        text = "Sort Tasks",  
        width = 24, 
        command = sort_tasks 
       
    )  
    del_button = ttk.Button(  
        functions_frame,  
        text = "Delete Task",  
        width = 24,  
        command = delete_task  
    )  
    mark_button=ttk.Button(functions_frame, text="Mark as completed ",width=24,command=markcompleted)
    mark_button.pack(pady=3)
    del_all_button = ttk.Button(  
        functions_frame,  
        text = "Delete All Tasks",  
        width = 24,  
        command = delete_all_tasks  
    )  
    save_button = ttk.Button(  
        functions_frame,  
        text = "Save",  
        width = 24,  
        command = save  
    )  
    open_file_button = ttk.Button(  
        functions_frame,  
        text = "Open",  
        width = 24,  
        command = open_file  
    ) 
    exit_button = ttk.Button(  
        functions_frame,  
        text = "Exit",  
        width = 24,  
        command = close  
    )  
    # using the place() method to set the position of the buttons in the application  
    add_button.place(x = 30, y = 75)
    mark_button.place(x = 30, y = 110)
    del_button.place(x = 30, y = 145) 
    sort_button.place(x = 30, y = 180)
    del_all_button.place(x = 30, y = 215)
    save_button.place(x = 30, y = 250)
    open_file_button.place(x = 30, y = 285)
    exit_button.place(x = 30, y = 320)  
  
    # defining a list box using the tk.Listbox() widget  
    task_listbox = tk.Listbox(  
        listbox_frame,  
        width = 20,  
        height = 10,
        font = ("Microsoft Himalaya","20"), 
        selectmode = 'SINGLE',  
        background = "#FFFFFF",  
        foreground = "#8e4585",  
        selectbackground = "#4b0082",  
        selectforeground = "#FFFFFF"  
    )  
    # using the place() method to place the list box in the application  
    task_listbox.place(x = 10, y = 20)  
  
    # calling some functions  
    retrieve_database()  
    list_update()  
    # using the mainloop() method to run the application  
    guiWindow.mainloop()  
    # establishing the connection with database  
    the_connection.commit()  
    the_cursor.close()  