text: #!/usr/bin/env python
  
  # NOTICE... this file is generated by tk_happy.
  # Any code or comments added by the user must be in designated areas ONLY.
  # User additions will be lost if they are placed in code-generated areas.
  
  # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "imports"
  import sys
  import os
  from tkinter import *
  from tkinter import ttk
  from tkinter import messagebox
  from tkinter import filedialog
  import tkinter.font as tkfont
  import pickle
  from meal import Meal
  from input_popup import Input_Popup
  from ics import Calendar, Event
  import random
  from datetime import datetime, timedelta
  import time
  from concurrent.futures import ThreadPoolExecutor
  import threading
  
  # >>>>>>insert any user code below this comment for section "imports"
  # Place any user import statements here
  
  # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "top_of_init"
  
  
  class Main_UI:
      def __init__(self, master):
          self.initComplete = 0
          frame = Frame(master, width=1196, height=588)
          frame.pack()
          self.master = master
          self.x, self.y, self.w, self.h = -1,-1,-1,-1
  
          self.calendar = Calendar()
  
          self.run = True
  
          # bind master to <Configure> in order to handle any resizing, etc.
          # postpone self.master.bind("<Configure>", self.Master_Configure)
          self.master.bind('<Enter>', self.bindConfigure)
  
          def exitClick():
              result = messagebox.askquestion("Exit", "Are You Sure You Want To Exit?", icon='warning',
                                              parent=self.master)
              if result == 'yes':
                  self.run = False
                  self.store_data()
                  for child_element in self.master.winfo_children():
                      child_element.destroy()
                  exit()
  
          self.background_checkpointer_executor = ThreadPoolExecutor(max_workers=1)
          self.checkpoint_timer_duration = 60 #seconds
          self.background_checkpointer_executor.submit(self.background_checkpointer)
  
          self.master.protocol('WM_DELETE_WINDOW', exitClick)
  
  
          self.master.title("Meal Helper")
  
          self.week_strings = ["1 Week", "2 Weeks"]
  
          self.Weeks_Combo = ttk.Combobox(self.master, state="readonly", values=self.week_strings)
          self.Weeks_Combo.current(0)
          self.Weeks_Combo.place(x=562, y=550, width=85, height=30)
          self.Weeks_Combo.bind("<<ComboboxSelected>>", self.update_selected_weeks)
  
          self.Meals_Per_Week_Combo = ttk.Combobox(self.master, state="readonly", values=["Meals Per Week"]+list(range(1,8)))
          self.Meals_Per_Week_Combo.current(0)
          self.Meals_Per_Week_Combo.place(x=662, y=550, width=130, height=30)
          self.Meals_Per_Week_Combo.bind("<<ComboboxSelected>>", self.update_selected_mpw)
  
          self.today = datetime.today()
  
          self.date_strings = ['Start Date']
  
          for i in range(7):
              date = self.today + timedelta(days=i)
              self.date_strings.append(f"{date.strftime('%m/%d/%y')} -  {date.strftime('%A')}")
  
          self.Start_Date_Combo = ttk.Combobox(self.master, state="readonly", values=self.date_strings)
          self.Start_Date_Combo.current(0)
          self.Start_Date_Combo.place(x=446, y=550, width=100, height=30)
          self.Start_Date_Combo.bind('<<ComboboxSelected>>', self.update_selected_start_date)
  
          self.Add_Ingredient_Button = Button(self.master,text="+", width="15")
          self.Add_Ingredient_Button.place(x=444, y=468, width=35, height=29)
          self.Add_Ingredient_Button.bind("<ButtonRelease-1>", self.Add_Ingredient_Button_Click)
  
          self.Add_Instruction_Button = Button(self.master,text="+", justify="center", width="15")
          self.Add_Instruction_Button.place(x=840, y=468, width=36, height=29)
          self.Add_Instruction_Button.bind("<ButtonRelease-1>", self.Add_Instruction_Button_Click)
  
          self.Add_Meal_Button = Button(self.master,text="+", width="15")
          self.Add_Meal_Button.place(x=48, y=468, width=36, height=29)
          self.Add_Meal_Button.bind("<ButtonRelease-1>", self.Add_Meal_Button_Click)
  
          self.Export_Meal_Button = Button(self.master,text="Export", width="15")
          self.Export_Meal_Button.place(x=192, y=468, width=72, height=29)
          self.Export_Meal_Button.bind("<ButtonRelease-1>", self.Export_Meal_Button_Click)
  
          self.Import_Meal_Button = Button(self.master, text="Import", width="15")
          self.Import_Meal_Button.place(x=274, y=468, width=72, height=29)
          self.Import_Meal_Button.bind("<ButtonRelease-1>", self.Import_Meal_Button_Click)
  
          self.Generate_Meal_Plan_Button = Button(self.master,text="Generate Meal Plan", anchor="center", background="White", width="15")
          self.Generate_Meal_Plan_Button.place(x=504, y=516, width=202, height=29)
          self.Generate_Meal_Plan_Button.bind("<ButtonRelease-1>", self.Generate_Meal_Plan_Button_Click)
  
          self.Remove_Ingredient_Button = Button(self.master,text="-", width="15")
          self.Remove_Ingredient_Button.place(x=480, y=468, width=36, height=29)
          self.Remove_Ingredient_Button.bind("<ButtonRelease-1>", self.Remove_Ingredient_Button_Click)
  
          self.Remove_Instruction_Button = Button(self.master,text="-", width="15")
          self.Remove_Instruction_Button.place(x=876, y=468, width=36, height=29)
          self.Remove_Instruction_Button.bind("<ButtonRelease-1>", self.Remove_Instruction_Button_Click)
  
          self.Remove_Meal_Button = Button(self.master,text="-", width="15")
          self.Remove_Meal_Button.place(x=84, y=468, width=37, height=29)
          self.Remove_Meal_Button.bind("<ButtonRelease-1>", self.Remove_Meal_Button_Click)
  
          self.Move_Instruction_Up_Button = Button(self.master,text="+", justify="center", width="15")
          self.Move_Instruction_Up_Button.place(x=800, y=100, width=36, height=29)
          self.Move_Instruction_Up_Button.bind("<ButtonRelease-1>", self.Move_Instruction_Up_Button_Click)
  
          self.Move_Instruction_Down_Button = Button(self.master, text="-", justify="center", width="15")
          self.Move_Instruction_Down_Button.place(x=800, y=130, width=36, height=29)
          self.Move_Instruction_Down_Button.bind("<ButtonRelease-1>", self.Move_Instruction_Down_Button_Click)
  
          self.Ingredient_Label = Label(self.master,text="Ingredients", image="", relief="flat", width="15")
          self.Ingredient_Label.place(x=528, y=24, width=142, height=23)
  
          self.Instructions_Label = Label(self.master,text="Instructions", width="15")
          self.Instructions_Label.place(x=936, y=24, width=142, height=23)
  
          self.Meal_Label = Label(self.master,text="Meals", relief="flat", width="15")
          self.Meal_Label.place(x=132, y=24, width=143, height=24)
  
          lbframe = Frame( self.master )
          self.Listbox_1_frame = lbframe
          scrollbar = Scrollbar(lbframe, orient=VERTICAL)
          self.Listbox_1 = Listbox(lbframe, width="15", selectmode="extended", yscrollcommand=scrollbar.set)
          scrollbar.config(command=self.Listbox_1.yview)
          scrollbar.pack(side=RIGHT, fill=Y)
          self.Listbox_1.pack(side=LEFT, fill=BOTH, expand=1)
  
          self.Listbox_1_frame.place(x=48, y=48, width=312, height=413)
          self.Listbox_1.bind("<ButtonRelease-1>", self.Listbox_1_Click)
          self.Listbox_1.bind("<<ListboxSelect>>", self.Listbox_1_Click)
          self.Listbox_1.bind("<Double-1>", self.Listbox_1_Double_Click)
  
          lbframe = Frame(self.master)
          self.Listbox_2_frame = lbframe
          scrollbar = Scrollbar(lbframe, orient=VERTICAL)
          self.Listbox_2 = Listbox(lbframe, width="15", selectmode="extended", yscrollcommand=scrollbar.set)
          scrollbar.config(command=self.Listbox_2.yview)
          scrollbar.pack(side=RIGHT, fill=Y)
          self.Listbox_2.pack(side=LEFT, fill=BOTH, expand=1)
  
          self.Listbox_2_frame.place(x=444, y=48, width=316, height=415)
          self.Listbox_2.bind("<ButtonRelease-1>", self.Listbox_2_Click)
          self.Listbox_2.bind("<<ListboxSelect>>", self.Listbox_2_Click)
          self.Listbox_2.bind("<Double-1>", self.Listbox_2_Double_Click)
  
          lbframe = Frame( self.master )
          self.Listbox_3_frame = lbframe
          scrollbar = Scrollbar(lbframe, orient=VERTICAL)
          self.Listbox_3 = Listbox(lbframe, width="15", selectmode="extended", yscrollcommand=scrollbar.set)
          scrollbar.config(command=self.Listbox_3.yview)
          scrollbar.pack(side=RIGHT, fill=Y)
          self.Listbox_3.pack(side=LEFT, fill=BOTH, expand=1)
  
          self.Listbox_3_frame.place(x=840, y=48, width=317, height=416)
          self.Listbox_3.bind("<ButtonRelease-1>", self.Listbox_3_Click)
          self.Listbox_3.bind("<<ListboxSelect>>", self.Listbox_3_Click)
          self.Listbox_3.bind("<Double-1>", self.Listbox_3_Double_Click)
  
          self.custom_chk_value = BooleanVar()
          self.custom_chk_value.set(True)
          self.Meals_Per_Week_Combo.configure(state="disabled")
          self.Custom_Chk_Btn_Checkbutton = Checkbutton(self.master, text="Custom", width="15", var=self.custom_chk_value, command=self.update_chkbtn)
          self.Custom_Chk_Btn_Checkbutton.place(x=715, y=517, width=80, height=26)
  
          self.master.resizable(0,0) # Linux may not respect this
          # >>>>>>insert any user code below this comment for section "top_of_init"
  
          if not self.load_data():
              self.all_ingredients = []
              self.all_unit_types = []
              self.quantities = [str(x) for x in list(range(1,11))]
              self.meals = []
  
          self.selected_meal = None
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "Master_Configure"
      def bindConfigure(self, event):
          if not self.initComplete:
              self.master.bind("<Configure>", self.Master_Configure)
              self.initComplete = 1
  
      def Master_Configure(self, event):
          pass
          # >>>>>>insert any user code below this comment for section "Master_Configure"
          # replace, delete, or comment-out the following
          if event.widget != self.master:
              if self.w != -1:
                  return
          x = int(self.master.winfo_x())
          y = int(self.master.winfo_y())
          w = int(self.master.winfo_width())
          h = int(self.master.winfo_height())
          if (self.x, self.y, self.w, self.h) == (-1,-1,-1,-1):
              self.x, self.y, self.w, self.h = x,y,w,h
  
          if self.w!=w or self.h!=h:
              print("Master reconfigured... make resize adjustments")
              self.w=w
              self.h=h
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=7"
      def Add_Ingredient_Button_Click(self, event): #click method for component ID=7
          pass
          # >>>>>>insert any user code below this comment for section "compID=7"
          # replace, delete, or comment-out the following
          print("executed method Add_Ingredient_Button_Click")
          done = False
  
          position = None
          while not done:
              if not self.selected_meal:
                  messagebox.showwarning("Oops", "Please select a meal before adding an ingredient.",
                                         parent=self.master)
                  return
  
              self.quantities = [x for x in self.quantities if x.isnumeric()]
              self.quantities.sort(key=float)
  
              popup_data = {
                  "ingredients": self.all_ingredients,
                  "unit_types": self.all_unit_types,
                  "quantities": self.quantities
              }
  
              if position:
                  popup_data.update({"position": position})
  
              add_ingredient_popup = Input_Popup("new_ingredient", parent=self.master, data=popup_data)
  
              self.master.wait_window(add_ingredient_popup.popup_window)
  
              ingredient_data = add_ingredient_popup.get_rv_data()
  
              if "position" in ingredient_data:
                  position = ingredient_data["position"]
  
              if "done" in ingredient_data and ingredient_data["done"]:
                  break
  
              if "ingredient" in ingredient_data and ingredient_data["ingredient"] and "unit_type" in ingredient_data and ingredient_data["unit_type"] and "quantity" in ingredient_data and ingredient_data["quantity"]:
                  if ingredient_data["ingredient"] not in self.all_ingredients:
                      self.all_ingredients.append(ingredient_data["ingredient"])
                      self.all_ingredients.sort()
  
                  if ingredient_data["unit_type"] not in self.all_unit_types:
                      self.all_unit_types.append(ingredient_data["unit_type"])
                      self.all_unit_types.sort()
  
                  if ingredient_data["quantity"] not in self.quantities:
                      self.quantities.append(ingredient_data["quantity"])
                      try:
                          self.quantities.sort(key=float)
                      except:
                          messagebox.showwarning("Oops", "You must enter a number for quantity", parent=self.master)
                          continue
  
                  if ingredient_data["ingredient"] in self.selected_meal.ingredient_names:
                      existing_ingredient = None
                      for x in self.selected_meal.ingredients:
                          if x["name"] == ingredient_data["ingredient"]:
                              existing_ingredient = x
                              break
  
                      if ingredient_data["unit_type"] == existing_ingredient["unit"]:
                          quantity = str(float(ingredient_data["quantity"]) + float(existing_ingredient["quantity"]))
                          if float(quantity).is_integer():
                              quantity = str(int(float(quantity)))
  
                          update = messagebox.askyesno("Oops", f"Found existing ingredient '{ingredient_data['ingredient']}' with the same unit type.\n\nCurrent quantity: {existing_ingredient['quantity']}\nUpdated quantity: {quantity}\n\nDo you want to update the existing quantity?", parent=self.master)
  
                          if update:
                              self.selected_meal.update_ingredient(ingredient_data["ingredient"], quantity,
                                                                   ingredient_data["unit_type"])
                          else:
                              try:
                                  quantity = str(float(ingredient_data["quantity"]))
                                  if float(quantity).is_integer():
                                      quantity = str(int(float(quantity)))
                              except:
                                  messagebox.showwarning("Oops", "You must enter a number for quantity", parent=self.master)
                                  continue
  
                              replace = messagebox.askyesno("Oops", f"Found existing ingredient '{ingredient_data['ingredient']}' with the same unit type.\n\nCurrent quantity: {existing_ingredient['quantity']}\nNew quantity: {quantity}\n\nDo you want to replace the existing quantity?",
                                                     parent=self.master)
  
                              if replace:
                                  self.selected_meal.update_ingredient(ingredient_data["ingredient"], quantity,
                                                                       ingredient_data["unit_type"])
  
                      else:
                          messagebox.showwarning("Oops", f"Found existing ingredient '{ingredient_data['ingredient']}' but with different unit type.\n\nPrevious Unit Type: {existing_ingredient['unit']}\nNew Unit Type: {ingredient_data['unit_type']}\n\nThe ingredient will NOT be added",
                                                 parent=self.master)
                          return
                  else:
                      try:
                          quantity = str(float(ingredient_data["quantity"]))
                          if float(quantity).is_integer():
                              quantity = str(int(float(quantity)))
                      except:
                          messagebox.showwarning("Oops", "You must enter a number for quantity", parent=self.master)
                          continue
                      self.selected_meal.add_ingredient(ingredient_data["ingredient"], quantity, unit=ingredient_data["unit_type"])
  
                  self.populate_ingredients()
                  done = add_ingredient_popup.done
              else:
                  print("no input")
                  done = True
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=13"
      def Add_Instruction_Button_Click(self, event): #click method for component ID=13
          pass
          # >>>>>>insert any user code below this comment for section "compID=13"
          # replace, delete, or comment-out the following
          print("executed method Add_Instruction_Button_Click")
          done = False
  
          position = None
          while not done:
              if not self.selected_meal:
                  messagebox.showwarning("Oops", "Please select a meal before adding an instruction.",
                                         parent=self.master)
                  return
  
              popup_data = {}
  
              if position:
                  popup_data.update({"position": position})
  
              add_instruction_popup = Input_Popup("new_instruction", parent=self.master)
  
              self.Add_Instruction_Button.configure(state=DISABLED)
              self.master.wait_window(add_instruction_popup.popup_window)
              self.Add_Instruction_Button.configure(state=NORMAL)
  
              instruction_data = add_instruction_popup.get_rv_data()
  
              if "position" in instruction_data:
                  position = instruction_data["position"]
  
              if "done" in instruction_data and instruction_data["done"]:
                  break
  
              if "instruction" in instruction_data and instruction_data["instruction"]:
                  self.selected_meal.add_instruction(instruction_data["instruction"])
                  self.populate_instructions()
              else:
                  print("no input")
                  done = True
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=5"
      def Add_Meal_Button_Click(self, event): #click method for component ID=5
          pass
          # >>>>>>insert any user code below this comment for section "compID=5"
          # replace, delete, or comment-out the following
          print("executed method Add_Meal_Button_Click")
  
          add_meal_popup = Input_Popup("new_meal", parent=self.master)
  
          self.Add_Meal_Button.configure(state=DISABLED)
          self.master.wait_window(add_meal_popup.popup_window)
          self.Add_Meal_Button.configure(state=NORMAL)
  
          meal_data = add_meal_popup.get_rv_data()
  
          if "name" in meal_data and meal_data["name"]:
              new_meal = Meal(meal_data["name"])
              self.meals.append(new_meal)
  
              self.Listbox_1.delete(0, 'end')
  
              new_meal_idx = self.populate_meals(meal_data["name"])
  
              self.Listbox_1.select_set(new_meal_idx)
              self.selected_meal = new_meal
              self.populate_ingredients()
          else:
              print("no input")
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=9"
      def Export_Meal_Button_Click(self, event): #click method for component ID=9
          pass
          # >>>>>>insert any user code below this comment for section "compID=9"
          # replace, delete, or comment-out the following
          print("executed method Export_Meal_Button_Click")
  
          if not self.selected_meal:
              messagebox.showwarning("Oops", "Please select a meal before exporting.",
                                     parent=self.master)
              return
  
          filename = filedialog.asksaveasfilename(parent=self.master, initialdir =f"{os.path.expanduser('~')}/Desktop/", initialfile=f"{self.selected_meal.meal_name.replace(' ', '_')}.mhe")
  
          if not filename:
              return
  
          with open(filename, "wb") as export_file:
              pickle.dump(self.selected_meal, export_file)
  
          # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=9"
      def Import_Meal_Button_Click(self, event):  # click method for component ID=9
          pass
  
          print("executed method Import_Meal_Button_Click")
  
          filename = filedialog.askopenfilename(parent=self.master, initialdir =f"{os.path.expanduser('~')}/Desktop/")
  
          if not filename:
              return
  
          with open(filename, "rb") as import_file:
              try:
                  new_meal = pickle.load(import_file)
  
                  if new_meal:
                      self.meals.append(new_meal)
                      self.populate_meals()
  
                      messagebox.showinfo("Import Success", f"New Meal Imported: {new_meal.meal_name}", parent=self.master)
              except:
                  messagebox.showwarning("Oops", f"Unable to import {os.path.basename(filename)}",
                                             parent=self.master)
  
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=10"
      def Generate_Meal_Plan_Button_Click(self, event): #click method for component ID=10
          pass
          # >>>>>>insert any user code below this comment for section "compID=10"
          # replace, delete, or comment-out the following
          print("executed method Generate_Meal_Plan_Button_Click")
  
          custom = self.custom_chk_value.get()
          print(f"Custom: {custom}")
  
          start_date = self.Start_Date_Combo.get()
  
          if start_date == "Start Date":
              messagebox.showwarning("Oops", "Please select a start date before generating this meal plan",
                                     parent=self.master)
              return
  
          days_needed = self.Meals_Per_Week_Combo.get()
          weeks = self.Weeks_Combo.current() + 1
  
          if days_needed == "Meals Per Week" and not self.custom_chk_value.get():
              messagebox.showwarning("Oops", "Please select the number of meals per week before generating this meal plan",
                                     parent=self.master)
              return
          elif not self.custom_chk_value.get():
              days_needed = int(days_needed)
  
              if weeks == 1 and len(self.meals) < days_needed:
                  messagebox.showwarning("Oops", f"Please add at least {days_needed} meals before generating this meal plan",
                                         parent=self.master)
                  return
              elif weeks == 2 and len(self.meals) < (days_needed*2):
                  messagebox.showwarning("Oops", f"Please add at least {days_needed*2} meals before generating this meal plan",
                                         parent=self.master)
                  return
  
          gen_type = "random"
          if custom:
              gen_type = "custom"
  
          days_needed_mod = ""
  
          if not custom:
              plural_mod = ""
  
              if days_needed > 1:
                  plural_mod = "s"
  
              days_needed_mod = f" with {days_needed} meal{plural_mod} per week"
  
          confirm_generate = messagebox.askyesno("Generate Meal Plan", f"Confirm you would like to generate a {gen_type} meal plan for {self.Weeks_Combo.get()}{days_needed_mod}, starting on {start_date}",
                                       parent=self.master)
  
          if not confirm_generate:
              return
  
          start_date = start_date.split("-")[0].strip()
  
          print(f"Generating meal plan for {weeks} weeks")
  
          if gen_type == "random":
              meal_days = self.get_meal_random_days(start_date, weeks, days_needed)
              export_meals = self.get_random_meals(weeks, days_needed)
  
              meal_dates = []
              for i in range(len(meal_days)):
                  meal_dates.append((meal_days[i], export_meals[i]))
          else:
              meal_dates = self.build_custom_meal_plan(start_date, weeks, days_needed)
              export_meals = []
  
              if not meal_dates or len(meal_dates) == 0:
                  return
  
          for meal_date in meal_dates:
              self.generate_calendar_event(meal_date[1], meal_date[0])
              if self.custom_chk_value.get():
                  export_meals.append(meal_date[1])
  
          self.export_calendar()
  
          self.generate_shopping_list(export_meals)
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=8"
      def Remove_Ingredient_Button_Click(self, event): #click method for component ID=8
          pass
          # >>>>>>insert any user code below this comment for section "compID=8"
          # replace, delete, or comment-out the following
          print("executed method Remove_Ingredient_Button_Click")
  
  
          cur_selection = self.Listbox_2.curselection()
  
          try:
              remove_ingredient = self.Listbox_2.get(cur_selection[0]).split(" - ")[0]
          except:
              return
  
          remove = messagebox.askyesno("Remove", f"Are you sure you want to remove the following ingredient:\n\n{remove_ingredient}",
                                       parent=self.master)
  
          if not remove:
              return
  
          if remove_ingredient:
              self.selected_meal.remove_ingredient(remove_ingredient)
              self.populate_ingredients()
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=14"
      def Remove_Instruction_Button_Click(self, event): #click method for component ID=14
          pass
          # >>>>>>insert any user code below this comment for section "compID=14"
          # replace, delete, or comment-out the following
          print("executed method Remove_Instruction_Button_Click")
  
          try:
              remove_instruction = self.Listbox_3.get(self.Listbox_3.curselection()[0])
          except:
              return
  
          remove = messagebox.askyesno("Remove",
                                       f"Are you sure you want to remove the following ingredient:\n\n{remove_instruction}",
                                       parent=self.master)
  
          if not remove:
              return
  
          if remove_instruction:
              self.selected_meal.remove_instruction(remove_instruction)
              self.populate_instructions()
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=6"
      def Remove_Meal_Button_Click(self, event): #click method for component ID=6
          pass
          # >>>>>>insert any user code below this comment for section "compID=6"
          # replace, delete, or comment-out the following
          print("executed method Remove_Meal_Button_Click")
  
          if self.selected_meal:
              remove = messagebox.askyesno("Remove",
                                           f"Are you sure you want to remove the following meal:\n\n{self.selected_meal.meal_name}",
                                           parent=self.master)
  
              if not remove:
                  return
  
              self.meals.remove(self.selected_meal)
              self.selected_meal = None
              self.populate_meals()
              self.Listbox_2.delete(0, 'end')
              self.Listbox_3.delete(0, 'end')
  
      def Move_Instruction_Up_Button_Click(self, event):
          pass
  
          print("executed method Move_Instruction_Up_Button_Click")
  
          if len(self.Listbox_3.curselection()) == 0:
              return
  
          listbox3_selection = self.Listbox_3.curselection()[0]
  
          cur_selection = self.Listbox_3.get(listbox3_selection)
  
          print(f"Moving '{cur_selection}' up")
  
          self.selected_meal.reorder_instruction(cur_selection, "up")
          self.populate_instructions()
  
          self.Listbox_3.selection_set(listbox3_selection - 1)
  
      def Move_Instruction_Down_Button_Click(self, event):
          pass
  
          print("executed method Move_Instruction_Down_Button_Click")
  
          if len(self.Listbox_3.curselection()) == 0:
              return
  
          listbox3_selection = self.Listbox_3.curselection()[0]
  
          cur_selection = self.Listbox_3.get(listbox3_selection)
  
          print(f"Moving '{cur_selection}' down")
  
          self.selected_meal.reorder_instruction(cur_selection, "down")
          self.populate_instructions()
  
          self.Listbox_3.selection_set(listbox3_selection + 1)
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=1"
      def Listbox_1_Click(self, event): #click method for component ID=1
          pass
          # >>>>>>insert any user code below this comment for section "compID=1"
          # replace, delete, or comment-out the following
          print("executed method Listbox_1_Click")
          print("current selection(s) =",self.Listbox_1.curselection())
          labelL = []
          for i in self.Listbox_1.curselection():
              labelL.append( self.Listbox_1.get(i))
          print("current label(s) =",labelL)
          # use self.Listbox_1.insert(0, "item zero")
          #     self.Listbox_1.insert(index, "item i")
          #            OR
          #     self.Listbox_1.insert(END, "item end")
          #   to insert items into the list box
  
          print("Fetching meal..")
  
          if len(labelL) == 0:
              return
  
          selected_meal_name = labelL[0]
  
          for meal in self.meals:
              if selected_meal_name == meal.meal_name:
                  self.selected_meal = meal
                  print("Found selected meal..")
  
                  self.populate_ingredients()
                  self.populate_instructions()
  
                  break
  
      def Listbox_1_Double_Click(self, event):  # click method for component ID=1
          pass
  
          print("executed method Listbox_1_Double_Click")
  
          edit_meal_data = {
              "meal_name": self.selected_meal.meal_name
          }
  
          edit_meal_popup = Input_Popup("edit_meal", parent=self.master, data=edit_meal_data)
  
          self.master.wait_window(edit_meal_popup.popup_window)
  
          meal_data = edit_meal_popup.get_rv_data()
  
          if "name" in meal_data and meal_data["name"]:
              self.selected_meal.meal_name = meal_data["name"]
              self.Listbox_1.delete(0, 'end')
              self.populate_meals()
          else:
              print("no input")
  
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=2"
      def Listbox_2_Click(self, event): #click method for component ID=2
          pass
          # >>>>>>insert any user code below this comment for section "compID=2"
          # replace, delete, or comment-out the following
          print("executed method Listbox_2_Click")
          print("current selection(s) =",self.Listbox_2.curselection())
          labelL = []
          for i in self.Listbox_2.curselection():
              labelL.append( self.Listbox_2.get(i))
          print("current label(s) =",labelL)
          # use self.Listbox_2.insert(0, "item zero")
          #     self.Listbox_2.insert(index, "item i")
          #            OR
          #     self.Listbox_2.insert(END, "item end")
          #   to insert items into the list box
  
      def Listbox_2_Double_Click(self, event):  # click method for component ID=1
          pass
  
          print("executed method Listbox_2_Double_Click")
  
          ingredient_parts = self.Listbox_2.get(self.Listbox_2.curselection()[0]).split(" - ")
          self.quantities.sort(key=float)
  
          edit_ingredient_data ={
              "ingredient": ingredient_parts[0],
              "quantity": ingredient_parts[1],
              "unit": ingredient_parts[2],
              "ingredients": self.all_ingredients,
              "unit_types": self.all_unit_types,
              "quantities": self.quantities
          }
  
          edit_ingredient_popup = Input_Popup("edit_ingredient", parent=self.master, data=edit_ingredient_data)
  
          self.master.wait_window(edit_ingredient_popup.popup_window)
  
          ingredient_data = edit_ingredient_popup.get_rv_data()
  
          if "unit_type" in ingredient_data and ingredient_data["unit_type"] and "quantity" in ingredient_data and ingredient_data["quantity"]:
              if ingredient_data["unit_type"] not in self.all_unit_types:
                  self.all_unit_types.append(ingredient_data["unit_type"])
                  self.all_unit_types.sort()
  
              if ingredient_data["quantity"] not in self.quantities:
                  self.quantities.append(ingredient_data["quantity"])
                  self.quantities.sort(key=float)
  
              self.selected_meal.update_ingredient(ingredient_parts[0], quantity=ingredient_data["quantity"], unit=ingredient_data["unit_type"])
  
              self.populate_ingredients()
          else:
              print("no input")
  
      def Listbox_3_Double_Click(self, event):  # click method for component ID=1
          pass
  
          current_instruction = self.Listbox_3.get(self.Listbox_3.curselection()[0])
  
          edit_instruction_data = {
              'current_instruction': current_instruction
          }
          edit_instruction_popup = Input_Popup("edit_instruction", parent=self.master, data=edit_instruction_data)
          self.master.wait_window(edit_instruction_popup.popup_window)
          instruction_data = edit_instruction_popup.get_rv_data()
  
          if "instruction" not in instruction_data:
              return
  
          self.selected_meal.update_instruction(current_instruction, instruction_data["instruction"])
          self.populate_instructions()
  
      # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "compID=11"
      def Listbox_3_Click(self, event): #click method for component ID=11
          pass
          # >>>>>>insert any user code below this comment for section "compID=11"
          # replace, delete, or comment-out the following
          ("executed method Listbox_3_Click")
          print("current selection(s) =",self.Listbox_3.curselection())
          labelL = []
          for i in self.Listbox_3.curselection():
              labelL.append( self.Listbox_3.get(i))
          print("current label(s) =",labelL)
          # use self.Listbox_3.insert(0, "item zero")
          #     self.Listbox_3.insert(index, "item i")
          #            OR
          #     self.Listbox_3.insert(END, "item end")
          #   to insert items into the list box
  
  # tk_happy generated code. DO NOT EDIT THE FOLLOWING. section "end"
      def update_chkbtn(self, event=None):
          print(f"Custom: {self.custom_chk_value.get()}")
  
          if self.custom_chk_value.get():
              self.Meals_Per_Week_Combo.current(0)
              self.Meals_Per_Week_Combo.configure(state="disabled")
          else:
              self.Meals_Per_Week_Combo.configure(state="readonly")
  
      def update_selected_weeks(self, event):
          print("executed method update_selected_weeks")
          print(f"current selection: {self.Weeks_Combo.get()}")
  
      def update_selected_start_date(self, event):
          print("executed method update_selected_start_date")
          print(f"current selection: {self.Start_Date_Combo.get()}")
  
      def update_selected_mpw(self, event):
          print("executed method update_selected_mpw")
          print(f"current selection: {self.Meals_Per_Week_Combo.get()}")
  
      def populate_instructions(self):
          self.Listbox_3.delete(0, 'end')
  
          for instruction in self.selected_meal.instructions:
              self.Listbox_3.insert(END, instruction)
  
      def populate_ingredients(self):
          self.Listbox_2.delete(0, 'end')
  
          for ingredient_data in self.selected_meal.ingredients:
              self.Listbox_2.insert(END, f"{ingredient_data['name']} - {ingredient_data['quantity']} - {ingredient_data['unit']}")
  
      def populate_meals(self, match_name=None):
          self.sort_meals()
          self.Listbox_1.delete(0, 'end')
  
          new_meal_idx = 0
  
          i = 0
          for meal in self.meals:
              self.Listbox_1.insert(END, meal.meal_name)
  
              if meal.meal_name == match_name:
                  new_meal_idx = i
  
              i += 1
  
          return new_meal_idx
  
      def background_checkpointer(self):
          last_save = int(time.time())
          while self.run:
              curTime = int(time.time())
              curDuration = curTime - last_save
  
              if curDuration >= self.checkpoint_timer_duration:
                  print("Checkpointer: Triggering -> store_data")
                  self.store_data()
                  last_save = int(time.time())
              else:
                  print("Checkpointer: Waiting for timer duration to elapse, sleeping..")
                  time.sleep(3)
          print("Checkpointer: Releasing")
  
      def store_data(self):
          print("Meal Helper: Starting Data Store Process")
          with open("meal_helper.dat", "wb") as dat_file:
              data = {
                  "meals": self.meals,
                  "all_ingredients": self.all_ingredients,
                  "quantities": self.quantities,
                  "all_unit_types": self.all_unit_types
              }
  
              pickle.dump(data, dat_file)
          print("Meal Helper: Data Store Process Successful")
  
      def load_data(self):
          try:
              with open("meal_helper.dat", "rb") as dat_file:
                  data = pickle.load(dat_file)
  
                  self.all_ingredients =  list(set([x.title() for x in data["all_ingredients"]]))
                  self.all_ingredients.sort()
                  self.all_unit_types = list(set([x.title() for x in data["all_unit_types"]]))
                  self.all_unit_types.sort()
                  self.quantities = data["quantities"]
                  self.quantities.sort(key=float)
                  self.meals = data["meals"]
  
                  self.populate_meals()
              return True
          except:
              return False
  
      def sort_meals(self):
          local_meals = self.meals.copy()
          meal_names = [x.meal_name for x in local_meals]
  
          self.meals = []
  
          meal_names.sort()
  
          for meal_name in meal_names:
              for meal in local_meals:
                  if meal.meal_name == meal_name:
                      self.meals.append(meal)
                      break
  
      def generate_calendar_event(self, meal, timestamp):
          event = Event()
  
          event.name = meal.meal_name
          event.begin = timestamp
          desc = ""
  
          for instruction in meal.instructions:
              desc += "• " + instruction + "\n"
  
          event.description = desc
  
          self.calendar.events.add(event)
  
          print(f"Added event to calendar: {event}")
  
      def export_calendar(self):
          path = f"{os.path.expanduser('~')}/Meal Plan.ics"
          print(f"Writing to {path}")
  
          with open(path, 'w') as calendar_event:
              calendar_event.writelines(self.calendar)
  
          os.system(f"open '{path}'")
  
          messagebox.showinfo("Export Success", "Please click ok after you have imported your meal plan.", parent=self.master)
  
          os.system(f"rm '{path}'")
          self.calendar = Calendar()
  
      def get_random_meals(self, weeks, days_needed):
          meal_ct = weeks * int(days_needed)
  
          local_meals = self.meals.copy()
          export_meals = []
  
          random.shuffle(local_meals)
          random.shuffle(local_meals)
  
          for i in range(meal_ct):
              export_meals.append(local_meals[i])
  
          return export_meals
  
      def generate_shopping_list(self, export_meals):
          export_list_items = {}
  
          for meal in export_meals:
              cur_ingredients = meal.ingredients
              for ingredient in cur_ingredients:
                  if ingredient["name"] not in export_list_items:
                      cur_dict = {ingredient["name"]: (ingredient["quantity"], ingredient["unit"])}
                  else:
                      cur_unit_type = ingredient["unit"]
                      existing_unit_type = export_list_items[ingredient["name"]][1]
  
                      if cur_unit_type != existing_unit_type:
                          cur_dict = {ingredient["name"]: (ingredient["quantity"], ingredient["unit"])}
                      else:
                          cur_quantity = ingredient["quantity"]
                          existing_quantity = export_list_items[ingredient["name"]][0]
  
                          quantity = str(float(cur_quantity) + float(existing_quantity))
                          if float(quantity).is_integer():
                              quantity = str(int(float(quantity)))
  
                          cur_dict = {ingredient["name"]: (quantity, ingredient["unit"])}
  
                  export_list_items.update(cur_dict)
  
          sorted_keys = sorted(export_list_items.keys())
  
          path = f"{os.path.expanduser('~')}/Desktop/shopping_list.txt"
          with open(path, "w") as shoping_list_file:
              for item in sorted_keys:
                  line = f"{item} - {export_list_items[item][0]} - {export_list_items[item][1]}\n"
                  shoping_list_file.write(line.replace("  ", " "))
  
          os.system(f"open {path}")
  
      def get_meal_random_days(self, start_date_str, weeks, days_needed):
          days_needed = int(days_needed)
          all_days_ct = 7
          all_days = []
          meal_days = []
  
          start_date = datetime.strptime(start_date_str, "%m/%d/%y")
  
          for i in range(all_days_ct):
              date = start_date + timedelta(days=i+1)
              new_date_str = f"{date.strftime('%Y-%m-%d')} 23:00:00"
              all_days.append(new_date_str)
  
          random.shuffle(all_days)
          random.shuffle(all_days)
  
          for i in range(days_needed):
              meal_days.append(all_days[i])
  
          if weeks == 2:
              week_two_start_date_str = (start_date + timedelta(days=7)).strftime("%m/%d/%y")
              meal_days = meal_days + self.get_meal_random_days(week_two_start_date_str, 1, days_needed)
  
          return meal_days
  
      def build_custom_meal_plan(self, start_date, weeks, days_needed):
          custom_meal_data = {
              'start_date': start_date,
              'weeks': weeks,
              'days_needed': days_needed,
              'meals': self.meals
          }
  
          custom_meal_popup = Input_Popup("custom_meal_plan", parent=self.master, data=custom_meal_data)
          self.master.wait_window(custom_meal_popup.popup_window)
  
          meal_data = custom_meal_popup.get_rv_data()
  
          meal_dates = None
  
          if "meal_dates" in meal_data and meal_data["meal_dates"]:
              meal_dates = meal_data["meal_dates"]
  
          return meal_dates
  
  
  def main():
      root = Tk()
      app = Main_UI(root)
      root.mainloop()
  
  
  if __name__ == '__main__':
      main()
  