text: import constants
  import tkinter
  import tkinter.ttk
  import tkinter.filedialog
  import tkinter.messagebox
  import tkcalendar
  import datetime
  import pathlib
  import models
  import data
  import csv_parser
  import troops_disposer
  import time_planner
  import message_generator
  import jinja2
  import utils
  
  
  class Widget:
      def add_children_padding(self, root, x, y):
          for child in root.winfo_children():
              child.grid_configure(padx=x, pady=y)
  
      def center_grid_horizontally(self, frame):
          for n in range(frame.grid_size()[0]):
              frame.grid_columnconfigure(n, weight=1)
  
      def set_readonly_spinbox_value(self, spinbox, value):
          spinbox["state"] = "normal"
          spinbox.delete(0, "end")
          spinbox.insert(0, value)
          spinbox["state"] = "readonly"
  
  
  class Window(tkinter.Tk):
      def __init__(self, title):
          super().__init__()
          self.title(f"{title} v{constants.VERSION}")
  
      def run(self):
          self.mainloop()
  
      def close(self):
          self.destroy()
  
      def update_minsize(self):
          self.update()
          self.minsize(self.winfo_reqwidth(), self.winfo_reqheight())
  
  
  class MainWindow(Window):
      __tab_control = None
      __tabs = {}
  
      class PathString:
          obj_list = {}
  
          def __init__(self, tab_class_name, path):
              self.tab_class_name = tab_class_name
              self.path = path
              self.obj_list[tab_class_name] = self
  
          @classmethod
          def get(cls, tab_class_name):
              if tab_class_name in cls.obj_list:
                  return cls.obj_list[tab_class_name]
              return None
  
      def __create_tabs(self):
          # Notebook
          self.__tab_control = tkinter.ttk.Notebook(self)
  
          self.__tabs[OriginsDataTab] = OriginsDataTab(
              self, self.__tab_control, "Dane wejściowe"
          )
          self.__tab_control.add(
              self.__tabs[OriginsDataTab], text=self.__tabs[OriginsDataTab].name
          )
  
          self.__tabs[TargetsDataTab] = TargetsDataTab(self, self.__tab_control, "Cele")
          self.__tab_control.add(
              self.__tabs[TargetsDataTab], text=self.__tabs[TargetsDataTab].name
          )
  
          self.__tabs[DetailsTab] = DetailsTab(self, self.__tab_control, "Szczegóły")
          self.__tab_control.add(
              self.__tabs[DetailsTab], text=self.__tabs[DetailsTab].name
          )
  
          # self.__tabs[SummaryTab] = SummaryTab(self.__tab_control, "Podsumowanie")
          # self.__tab_control.add(
          #     self.__tabs[SummaryTab], text=self.__tabs[SummaryTab].name, state="disabled"
          # )
  
          # self.__tabs[MessagesTab] = MessagesTab(self, self.__tab_control, "Wiadomości")
          # self.__tab_control.add(
          #     self.__tabs[MessagesTab],
          #     text=self.__tabs[MessagesTab].name,
          #     state="disabled",
          # )
  
          # details_tab_frame = create_details_tab(self, details_tab)
          # details_tab_frame.pack(fill=tkinter.BOTH, expand=True)
  
          self.__tab_control.grid(column=0, row=0)
  
      def __create_content(self):
          worlds_data = data.Worlds()
  
          # Lista światów
          world_label = tkinter.Label(self, text="Wybrany świat:\nbrak")
  
          def world_cb_set_values():
              worlds_keys = worlds_data.get_keys()
  
              if worlds_keys:
                  world_combobox["values"] = worlds_keys
              else:
                  tkinter.messagebox.showerror(
                      "Błąd",
                      "Nie udało się pobrać listy aktywnych światów.\n"
                      + "Sprawdź połączenie z internetem i spróbuj ponownie.",
                  )
  
          def world_cb_select_changed(event):
              selected_world = world_combobox.get()
              world_label.configure(text="Wybrany świat:\n" + selected_world)
              self.after(0, self.update_minsize())
  
          world_combobox = tkinter.ttk.Combobox(
              self, state="readonly", takefocus=False, postcommand=world_cb_set_values
          )
  
          world_combobox.bind("<<ComboboxSelected>>", world_cb_select_changed)
  
          world_combobox.grid(column=0, row=1, padx=10, pady=10)
          world_label.grid(column=0, row=2, padx=10, pady=5)
  
          # Separator
          tkinter.ttk.Separator(self, orient="horizontal").grid(
              row=3, padx=10, pady=10, sticky="ew"
          )
  
          # Dodanie przycisków
          def load_files_data_clicked():
              try:
                  data_headers_selected = False
                  target_headers_selected = False
  
                  for header in models.Header.obj_list:
                      if header.selected:
                          if header.tab_class_name == "OriginsDataTab":
                              data_headers_selected = True
                          elif header.tab_class_name == "TargetsDataTab":
                              target_headers_selected = True
  
                      if data_headers_selected and target_headers_selected:
                          break
  
                  if not data_headers_selected or not target_headers_selected:
                      tkinter.messagebox.showerror(
                          "Błąd", "W jednej z zakładek nie wybrałeś żadnego nagłówka!"
                      )
                  else:
                      # Wyłączanie zakładek z wynikami
                      if SummaryTab in self.__tabs:
                          # self.__tab_control.tab(
                          #     self.__tabs[SummaryTab], state="disabled"
                          # )
                          self.__tab_control.forget(self.__tabs[SummaryTab])
  
                      if MessagesTab in self.__tabs:
                          # self.__tab_control.tab(
                          #     self.__tabs[MessagesTab], state="disabled"
                          # )
                          self.__tab_control.forget(self.__tabs[MessagesTab])
  
                      # Wczytywanie plików
                      models.clear_lists()
  
                      path_origins = self.PathString.get(OriginsDataTab.get_classname())
  
                      if path_origins:
                          if not csv_parser.parse(
                              path_origins.path, OriginsDataTab.get_classname()
                          ):
                              tkinter.messagebox.showerror(
                                  "Błąd",
                                  "Nie udało się wczytać zawartości pliku zawierającego dane wejściowe.",
                              )
                              return
                      else:
                          tkinter.messagebox.showerror(
                              "Błąd",
                              "Nie udało się pobrać ścieżki do pliku zawierającego dane wejściowe.",
                          )
                          return
  
                      path_targets = self.PathString.get(TargetsDataTab.get_classname())
                      if path_targets:
  
                          if not csv_parser.parse(
                              path_targets.path, TargetsDataTab.get_classname()
                          ):
                              tkinter.messagebox.showerror(
                                  "Błąd",
                                  "Nie udało się wczytać zawartości pliku zawierającego dane o celach.",
                              )
                              return
                      else:
                          tkinter.messagebox.showerror(
                              "Błąd",
                              "Nie udało się pobrać ścieżki do pliku zawierającego dane o celach.",
                          )
                          return
  
                      message = "Poprawnie wczytano zawartość plików.\n\n"
                      for troops_type in models.TroopsType.obj_list:
                          targets = [
                              target
                              for target in models.Target.obj_list
                              if target.troops_type == troops_type
                          ]
                          origins = [
                              origin
                              for origin in models.Origin.obj_list
                              if origin.troops_type == troops_type
                          ]
  
                          targets_total_amount = sum(target.amount for target in targets)
                          origins_total_amount = len(origins)
  
                          message += (
                              f"{troops_type.name.upper()}\ndostępne: {origins_total_amount}"
                              f", zapotrzebowanie: {targets_total_amount}\n\n"
                          )
  
                      tkinter.messagebox.showinfo("Sukces", message.strip())
                      create_plan_button["state"] = "normal"
              except IndexError:
                  tkinter.messagebox.showerror(
                      "Błąd",
                      "Nie wybrałeś pliku z danymi o wojsku lub z danymi o celach!",
                  )
  
          load_files_button = tkinter.Button(
              self, text="Wczytaj dane z plików", command=load_files_data_clicked
          )
          load_files_button.grid(column=0, row=4, padx=10, pady=5)
  
          def create_plan_clicked():
              selected_world_key = world_combobox.get()
  
              if selected_world_key:
                  snob_max_distance = worlds_data.get_snob_max_distance(
                      selected_world_key
                  )
  
                  if snob_max_distance:
                      not_set_timeframes = []
  
                      for header in models.Header.obj_list:
                          if header.selected and header.header_type is not None:
                              if not header.header_type in models.Timeframe.obj_list:
                                  not_set_timeframes.append(header.name.lower())
  
                      if not_set_timeframes:
                          # Usuwanie duplikatów z listy
                          not_set_timeframes = list(dict.fromkeys(not_set_timeframes))
  
                          tkinter.messagebox.showerror(
                              "Błąd",
                              "Nie ustawiłeś ram czasowych dla poniższych typów wojsk:\n- {}".format(
                                  "\n- ".join(not_set_timeframes)
                              ),
                          )
                          return
  
                      troops_disposer.distribute(snob_max_distance)
                      time_planner.calculate(selected_world_key)
                      message_generator.generate()
  
                      min_start_datetime = min(
                          [timeplan.start for timeplan in models.Timeplan.obj_list]
                      )
                      tzinfo = min_start_datetime.astimezone().tzinfo
                      now = datetime.datetime.now(tzinfo)
  
                      if min_start_datetime > now:
                          tkinter.messagebox.showinfo(
                              "Sukces",
                              (
                                  "Poprawnie wygenerowano rozpiskę.\n\n"
                                  f"Rozpoczęcie wysyłki (pierwszy rozkaz):\n{utils.format_datetime(min_start_datetime)}"
                              ),
                          )
                      else:
                          tkinter.messagebox.showwarning(
                              "Ostrzeżenie",
                              (
                                  "Poprawnie wygenerowano rozpiskę.\n\n"
                                  f"Czas rozpoczęcia wysyłki jest wcześniejszy niż aktualny:\n{utils.format_datetime(min_start_datetime)}"
                              ),
                          )
  
                      # Ponowne utworzenie zawartości kart - aktualne dane
                      # if SummaryTab in self.__tabs:
                      #     self.__tab_control.forget(self.__tabs[SummaryTab])
  
                      self.__tabs[SummaryTab] = SummaryTab(
                          self.__tab_control, "Podsumowanie"
                      )
                      self.__tab_control.add(
                          self.__tabs[SummaryTab],
                          text=self.__tabs[SummaryTab].name,
                          # state="disabled",
                      )
  
                      # if MessagesTab in self.__tabs:
                      #     self.__tab_control.forget(self.__tabs[MessagesTab])
  
                      self.__tabs[MessagesTab] = MessagesTab(
                          self, self.__tab_control, "Wiadomości"
                      )
                      self.__tab_control.add(
                          self.__tabs[MessagesTab],
                          text=self.__tabs[MessagesTab].name,
                          # state="disabled",
                      )
  
                      # Włączenie zakładek z wynikami
                      # self.__tab_control.tab(self.__tabs[SummaryTab], state="normal")
                      # self.__tab_control.tab(self.__tabs[MessagesTab], state="normal")
  
                      # Wyłączenie przycisku
                      create_plan_button["state"] = "disabled"
              else:
                  tkinter.messagebox.showerror("Błąd", "Nie wybrałeś świata!")
  
          create_plan_button = tkinter.Button(
              self, text="Stwórz rozpiskę", command=create_plan_clicked
          )
          create_plan_button["state"] = "disabled"
          create_plan_button.grid(column=0, row=5, padx=10, pady=10)
  
      def __create_menu(self):
          # Dodanie menu
          menu = tkinter.Menu(self)
  
          def info_button_clicked():
              info = f"Automatyczny planer ataków\nWersja {constants.VERSION}\n\nszelbi (szelbi.ovh)\n\u00a9 2021"
              tkinter.messagebox.showinfo("Informacje", info)
  
          options_item = tkinter.Menu(menu, tearoff=0)
          options_item.add_command(label="Informacje", command=info_button_clicked)
          options_item.add_command(label="Wyjdź", command=self.close)
  
          menu.add_cascade(label="Opcje", menu=options_item)
  
          self.config(menu=menu)
  
      def __configure(self):
          # Ustawienie funkcji do zamykania okna poprzez "X"
          self.protocol("WM_DELETE_WINDOW", self.close)
  
          # Ustawienie minimalnego rozmiaru okna
          self.update_minsize()
  
          # Zablokowanie możliwości zmieniania rozmiaru okna
          self.resizable(False, False)
  
      def run(self):
          self.__create_tabs()
          self.__create_content()
          self.__create_menu()
          self.__configure()
          super().run()
  
  
  class ChoiceListboxes(tkinter.Frame, Widget):
      def __init__(self, parent, headers, first_header, second_header):
          tkinter.Frame.__init__(self, parent)
  
          self.parent = parent
          self.headers = headers
          self.first_header = first_header
          self.second_header = second_header
  
          self.__create_content()
  
      def __create_content(self):
          # Nagłówki do list
          avaliable_headers_label = tkinter.Label(self, text=self.first_header)
          avaliable_headers_label.grid(column=0, row=0)
  
          selected_headers_label = tkinter.Label(self, text=self.second_header)
          selected_headers_label.grid(column=2, row=0)
  
          # Lista dostępnych opcji
          avaliable_listbox = tkinter.Listbox(self, selectmode=tkinter.SINGLE)
          for header in self.headers:
              avaliable_listbox.insert(tkinter.END, header.name)
  
          avaliable_listbox.grid(column=0, row=1)
  
          # Lista wybranych opcji
          selected_listbox = tkinter.Listbox(self, selectmode=tkinter.MULTIPLE)
          selected_listbox.grid(column=2, row=1)
  
          # Przyciski do przenoszenia
          button_frame = tkinter.Frame(self)
  
          def add_button_clicked():
              selected_options = avaliable_listbox.curselection()
  
              for i in selected_options[::-1]:
                  name = avaliable_listbox.get(i)
                  selected_listbox.insert(tkinter.END, name)
  
                  index = list(selected_listbox.get(0, tkinter.END)).index(name)
  
                  for header in self.headers:
                      if (
                          header.name == name
                          and header.tab_class_name == self.parent.get_classname()
                      ):
                          header.selected = True
                          header.index = index
  
                  avaliable_listbox.delete(i)
  
          add_button = tkinter.Button(
              button_frame, text=chr(187), command=add_button_clicked
          )
          add_button.grid(column=0, row=0)
  
          def remove_button_clicked():
              selected_options = selected_listbox.curselection()
  
              for i in selected_options[::-1]:
                  name = selected_listbox.get(i)
                  avaliable_listbox.insert(tkinter.END, name)
  
                  for header in self.headers:
                      if (
                          header.name == name
                          and header.tab_class_name == self.parent.get_classname()
                      ):
                          header.selected = False
                          header.index = None
  
                  selected_listbox.delete(i)
  
          remove_button = tkinter.Button(
              button_frame, text=chr(171), command=remove_button_clicked
          )
          remove_button.grid(column=0, row=1)
  
          button_frame.grid(column=1, row=1)
  
          # Dwuklik do przenoszenia pozycji
          avaliable_listbox.bind("<Double-Button-1>", lambda event: add_button.invoke())
          selected_listbox.bind("<Double-Button-1>", lambda event: remove_button.invoke())
  
          self.add_children_padding(self, 10, 10)
          self.add_children_padding(button_frame, 0, 10)
  
  
  class Tab(tkinter.Frame, Widget):
      def __init__(self, parent, name):
          tkinter.Frame.__init__(self, parent)
          self.name = name
  
      @classmethod
      def get_classname(cls):
          return cls.__name__
  
  
  class ScrollableFrame(tkinter.Frame):
      def __init__(self, container, *args, **kwargs):
          super().__init__(container, *args, **kwargs)
          canvas = tkinter.Canvas(self, highlightthickness=0)
          scrollbar = tkinter.Scrollbar(self, orient="vertical", command=canvas.yview)
          self.scrollable_frame = tkinter.Frame(canvas)
  
          self.scrollable_frame.bind(
              "<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
          )
  
          canvas.create_window(
              (canvas.winfo_reqwidth() / 2, 0), window=self.scrollable_frame, anchor="n"
          )
  
          canvas.configure(yscrollcommand=scrollbar.set)
  
          canvas.pack(side="left", fill="both", expand=True)
          scrollbar.pack(side="right", fill="y")
  
  
  class ScrollableTab(ScrollableFrame, Widget):
      def __init__(self, parent, name):
          ScrollableFrame.__init__(self, parent)
          self.name = name
  
  
  class OriginsDataTab(Tab):
      def __init__(self, window, parent, name):
          super().__init__(parent, name)
          self.window = window
          self.__create_content()
  
      def __create_content(self):
          # Wczytywanie pliku z danymi
          file_label = tkinter.Label(self, text="Wczytany plik: brak")
  
          def data_button_clicked():
              selected_file = tkinter.filedialog.askopenfilename(
                  filetypes=(("Plik CSV", ".csv"),)
              )
  
              if selected_file:
                  path_string = self.window.PathString(
                      self.get_classname(), selected_file
                  )
                  path = pathlib.Path(path_string.path)
  
                  filename = path.name
                  if len(filename) > 30:
                      filename = f"{filename[:20]}...{filename[-7:]}"
  
                  file_label.configure(text=f"Wczytany plik: {filename}")
                  self.window.after(0, lambda: self.window.update_minsize())
  
          load_data_button = tkinter.Button(
              self, text="Wczytaj plik CSV (UTF-8)", command=data_button_clicked
          )
  
          load_data_button.grid(column=0, row=0)
          file_label.grid(column=0, row=1)
  
          # Separator
          tkinter.ttk.Separator(self, orient="horizontal").grid(row=2, sticky="ew")
  
          # Listy
          options = [
              header
              for header in models.Header.obj_list
              if header.tab_class_name == self.get_classname()
          ]
          listboxes = ChoiceListboxes(
              self,
              options,
              "Dostępne nagłówki",
              "Wybrane nagłówki\n(kolejność jak w arkuszu)",
          )
          listboxes.grid(column=0, row=3)
  
          # Wyśrodkowanie wszystkich kolumn
          self.center_grid_horizontally(self)
  
          self.add_children_padding(self, 10, 10)
  
  
  class TargetsDataTab(Tab):
      def __init__(self, window, parent, name):
          super().__init__(parent, name)
          self.window = window
          self.__create_content()
  
      def __create_content(self):
          # Wczytywanie pliku z danymi
          file_label = tkinter.Label(self, text="Wczytany plik: brak")
  
          def data_button_clicked():
              selected_file = tkinter.filedialog.askopenfilename(
                  filetypes=(("Plik CSV", ".csv"),)
              )
  
              if selected_file:
                  path_string = self.window.PathString(
                      self.get_classname(), selected_file
                  )
                  path = pathlib.Path(path_string.path)
  
                  filename = path.name
                  if len(filename) > 30:
                      filename = f"{filename[:20]}...{filename[-7:]}"
  
                  file_label.configure(text=f"Wczytany plik: {filename}")
                  self.window.after(0, lambda: self.window.update_minsize())
  
          load_data_button = tkinter.Button(
              self, text="Wczytaj plik CSV (UTF-8)", command=data_button_clicked
          )
  
          load_data_button.grid(column=0, row=0)
          file_label.grid(column=0, row=1)
  
          # Separator
          tkinter.ttk.Separator(self, orient="horizontal").grid(row=2, sticky="ew")
  
          # Listy
          options = [
              header
              for header in models.Header.obj_list
              if header.tab_class_name == self.get_classname()
          ]
          listboxes = ChoiceListboxes(
              self,
              options,
              "Dostępne nagłówki",
              "Wybrane nagłówki\n(kolejność jak w arkuszu)",
          )
          listboxes.grid(column=0, row=3)
  
          # Wyśrodkowanie wszystkich kolumn
          self.center_grid_horizontally(self)
  
          self.add_children_padding(self, 10, 10)
  
  
  class SummaryTab(ScrollableTab):
      def __init__(self, parent, name):
          super().__init__(parent, name)
          self.__create_content()
  
      def __create_content(self):
          # Rozpiska zbiorcza, wszystkie cele
          consolidated_plan_label = tkinter.Label(
              self.scrollable_frame, text="Rozpiska zbiorcza"
          )
  
          timeframes_headers = models.Timeframe.get_headers()
  
          file_loader = jinja2.FileSystemLoader("templates")
          env = jinja2.Environment(
              loader=file_loader, trim_blocks=True, lstrip_blocks=True
          )
          template = env.get_template("consolidated_plan.jinja2")
  
          headers = [
              header
              for header in models.Header.obj_list
              if header.tab_class_name == "OriginsDataTab"
              and header.usable == True
              and header.selected == True
              and header.header_type in models.TroopsType.obj_list
          ]
  
          target_villages = []
  
          for target in models.Target.obj_list:
              if target.village not in target_villages:
                  target_villages.append(target.village)
  
          output = template.render(
              date=timeframes_headers.dates,
              weekday=timeframes_headers.weekdays,
              headers=headers,
              target_villages=target_villages,
              timeplans=models.Timeplan.obj_list,
          )
  
          consolidated_plan_text = tkinter.Text(
              self.scrollable_frame, width=40, height=15
          )
          consolidated_plan_text.insert(1.0, output.strip())
          consolidated_plan_text["state"] = "disabled"
  
          consolidated_plan_label.grid(column=0, row=1, columnspan=2)
          consolidated_plan_text.grid(column=0, row=2, columnspan=2)
  
          # Szukanie braków
          not_planned = [
              target
              for target in models.Target.obj_list
              if not models.Timeplan.is_target_filled(target)
          ]
          unused_origins = [
              origin
              for origin in models.Origin.obj_list
              if models.Timeplan.is_origin_available(origin)
          ]
  
          # Nierozpisane ataki
          not_planned_targets_amount = len(not_planned)
          not_planned_attacks_amount = 0
  
          for target in not_planned:
              not_planned_attacks_amount += target.amount
              for timeplan in models.Timeplan.obj_list:
                  if timeplan.target == target:
                      not_planned_attacks_amount -= 1
  
          not_planned_label = tkinter.Label(
              self.scrollable_frame,
              text=(
                  f"Nierozpisane cele ({not_planned_targets_amount if not_planned_targets_amount else 'brak'} wiosek, "
                  f"{not_planned_attacks_amount if not_planned_attacks_amount else 'brak'} ataków)"
              ),
          )
          not_planned_text = tkinter.Text(self.scrollable_frame, width=40, height=15)
  
          message = ""
          for target in not_planned:
              attacks_left = target.amount
  
              for timeplan in models.Timeplan.obj_list:
                  if timeplan.target == target:
                      attacks_left -= 1
  
              message += f"{target.village.coords} - {target.troops_type.name.upper()} : {attacks_left}\n"
  
          not_planned_text.insert(1.0, message.strip())
          not_planned_text["state"] = "disabled"
  
          not_planned_label.grid(column=0, row=3)
          not_planned_text.grid(column=0, row=4)
  
          # Niewykorzystanie wioski atakujące
          unused_origins_amount = len(unused_origins)
          unused_origins_label = tkinter.Label(
              self.scrollable_frame,
              text=f"Niewykorzystane wioski atakujące ({unused_origins_amount if unused_origins_amount else 'brak'})",
          )
          unused_origins_text = tkinter.Text(self.scrollable_frame, width=40, height=15)
  
          message = ""
          for origin in unused_origins:
              message += f"{origin.village.coords} ({origin.player.nickname}) - {origin.troops_type.name.upper()}\n"
  
          unused_origins_text.insert(1.0, message.strip())
          unused_origins_text["state"] = "disabled"
  
          unused_origins_label.grid(column=0, row=5)
          unused_origins_text.grid(column=0, row=6)
  
          self.add_children_padding(self.scrollable_frame, 5, 5)
  
  
  class MessagesTab(Tab):
      def __init__(self, window, parent, name):
          super().__init__(parent, name)
          self.window = window
          self.__create_content()
  
      def __create_content(self):
          # Lista graczy, dla których wygenerowano rozpiskę
          player_label = tkinter.Label(self, text="Wybrany gracz: brak")
  
          def player_cb_set_values():
              if models.Message.obj_list:
                  player_keys = []
                  for message in models.Message.obj_list:
                      player_keys.append(message.player.nickname)
  
                  player_combobox["values"] = player_keys
              else:
                  tkinter.messagebox.showerror("Błąd", "Brak wiadomości do wyświetlenia.")
  
          def player_cb_select_changed(event):
              selected_player = player_combobox.get()
  
              if len(selected_player) > 30:
                  selected_player = f"{selected_player[:20]}...{selected_player[-7:]}"
  
              player_label.configure(text=f"Wybrany gracz: {selected_player}")
  
              message_text["state"] = "normal"
              message_text.delete(1.0, tkinter.END)
  
              message = models.Message.get_msg_by_nickname(selected_player)
              if message:
                  message_text.insert(1.0, message)
              message_text["state"] = "disabled"
  
              self.window.after(0, lambda: self.window.update_minsize())
  
          # player_cb_value = tkinter.StringVar()
          player_combobox = tkinter.ttk.Combobox(
              self, state="readonly", takefocus=False, postcommand=player_cb_set_values
          )
  
          player_combobox.bind("<<ComboboxSelected>>", player_cb_select_changed)
  
          player_combobox.grid(column=0, row=0)
          player_label.grid(column=0, row=1)
  
          # Pole tekstowe
          message_text = tkinter.Text(self, width=40, height=15)
          message_text["state"] = "disabled"
  
          message_text.grid(column=0, row=2)
  
          # Wyśrodkowanie wszystkich kolumn
          self.center_grid_horizontally(self)
  
          self.add_children_padding(self, 10, 10)
  
  
  class DetailsTab(Tab):
      def __init__(self, window, parent, name):
          super().__init__(parent, name)
          self.window = window
          self.__create_content()
  
      def __create_content(self):
          # Wybór typu jednostek do ustawienia przedziału
          troops_type_label = tkinter.Label(self, text="Zmiana czasu dla:\nbrak")
          troops_type_cb_values = [
              troops_type for troops_type in models.TroopsType.obj_list
          ]
  
          def troops_type_cb_select_changed(event):
              start_date_entry["state"] = "normal"
              end_date_entry["state"] = "normal"
  
              selected_header_name = troops_type_combobox.get()
              troops_type_label.configure(
                  text=f'Zmiana czasu dla:\n"{selected_header_name.upper()}"'
              )
  
              selected_troops_type = None
              for troops_type in troops_type_cb_values:
                  if troops_type.name == selected_header_name:
                      selected_troops_type = troops_type
                      break
  
              if selected_troops_type:
                  if selected_troops_type in models.Timeframe.obj_list:
                      timeframe = models.Timeframe.obj_list[selected_troops_type]
  
                      start_date_entry.set_date(timeframe.start)
                      end_date_entry.set_date(timeframe.end)
  
                      self.set_readonly_spinbox_value(
                          spinbox_start_hours, timeframe.start.hour
                      )
                      self.set_readonly_spinbox_value(
                          spinbox_start_minutes, timeframe.start.minute
                      )
                      self.set_readonly_spinbox_value(
                          spinbox_start_seconds, timeframe.start.second
                      )
  
                      self.set_readonly_spinbox_value(
                          spinbox_end_hours, timeframe.end.hour
                      )
                      self.set_readonly_spinbox_value(
                          spinbox_end_minutes, timeframe.end.minute
                      )
                      self.set_readonly_spinbox_value(
                          spinbox_end_seconds, timeframe.end.second
                      )
                  else:
                      now = datetime.datetime.now()
                      start_date_entry.set_date(now)
                      end_date_entry.set_date(now)
  
                      self.set_readonly_spinbox_value(spinbox_start_hours, 7)
                      self.set_readonly_spinbox_value(spinbox_start_minutes, 0)
                      self.set_readonly_spinbox_value(spinbox_start_seconds, 0)
  
                      self.set_readonly_spinbox_value(spinbox_end_hours, 7)
                      self.set_readonly_spinbox_value(spinbox_end_minutes, 0)
                      self.set_readonly_spinbox_value(spinbox_end_seconds, 0)
              else:
                  tkinter.messagebox.showerror(
                      "Błąd", "Nie wybrałeś typu wojsk do zmiany czasu."
                  )
  
              self.window.after(0, lambda: self.window.update_minsize())
  
          troops_type_combobox = tkinter.ttk.Combobox(
              self, state="readonly", takefocus=False, values=troops_type_cb_values
          )
  
          troops_type_combobox.bind("<<ComboboxSelected>>", troops_type_cb_select_changed)
  
          troops_type_combobox.grid(column=0, row=0, columnspan=4)
          troops_type_label.grid(column=0, row=1, columnspan=4)
  
          # Separator
          tkinter.ttk.Separator(self, orient="horizontal").grid(
              row=2, sticky="ew", columnspan=4
          )
  
          # Przedziały czasowe
          start_date_label = tkinter.Label(self, text="Data początkowa:")
          start_date_entry = tkcalendar.DateEntry(
              self,
              width=12,
              background="darkblue",
              foreground="white",
              datepattern="dd.mm.y",
              state="disabled",
              takefocus=False,
              locale=constants.LOCALE,
          )
  
          start_time_label = tkinter.Label(self, text="Czas początkowy:")
          spinbox_start_hours = tkinter.Spinbox(
              self, from_=0, to=23, width=3, state="disabled", wrap=True, takefocus=False
          )
          spinbox_start_minutes = tkinter.Spinbox(
              self, from_=0, to=59, width=3, state="disabled", wrap=True, takefocus=False
          )
          spinbox_start_seconds = tkinter.Spinbox(
              self, from_=0, to=59, width=3, state="disabled", wrap=True, takefocus=False
          )
  
          end_date_label = tkinter.Label(self, text="Data końcowa:")
          end_date_entry = tkcalendar.DateEntry(
              self,
              width=12,
              background="darkblue",
              foreground="white",
              datepattern="dd.mm.y",
              state="disabled",
              takefocus=False,
              locale=constants.LOCALE,
          )
  
          end_time_label = tkinter.Label(self, text="Czas końcowy:")
          spinbox_end_hours = tkinter.Spinbox(
              self, from_=0, to=23, width=3, state="disabled", wrap=True, takefocus=False
          )
          spinbox_end_minutes = tkinter.Spinbox(
              self, from_=0, to=59, width=3, state="disabled", wrap=True, takefocus=False
          )
          spinbox_end_seconds = tkinter.Spinbox(
              self, from_=0, to=59, width=3, state="disabled", wrap=True, takefocus=False
          )
  
          def set_datetime_button_clicked():
              selected_troops_type_name = troops_type_combobox.get()
              selected_troops_type = None
  
              for troops_type in troops_type_cb_values:
                  if troops_type.name == selected_troops_type_name:
                      selected_troops_type = troops_type
                      break
  
              if selected_troops_type:
                  # Pobieranie czasów
                  tzinfo = datetime.datetime.now().astimezone().tzinfo
  
                  start_date = start_date_entry.get_date()
  
                  start_hours = int(spinbox_start_hours.get())
                  start_minutes = int(spinbox_start_minutes.get())
                  start_seconds = int(spinbox_start_seconds.get())
                  start_time = datetime.time(
                      hour=start_hours, minute=start_minutes, second=start_seconds, fold=1
                  )
  
                  start_datetime = datetime.datetime.combine(
                      start_date, start_time, tzinfo
                  )
  
                  end_date = end_date_entry.get_date()
  
                  end_hours = int(spinbox_end_hours.get())
                  end_minutes = int(spinbox_end_minutes.get())
                  end_seconds = int(spinbox_end_seconds.get())
                  end_time = datetime.time(
                      hour=end_hours, minute=end_minutes, second=end_seconds, fold=1
                  )
  
                  end_datetime = datetime.datetime.combine(end_date, end_time, tzinfo)
  
                  if start_datetime > end_datetime:
                      tkinter.messagebox.showerror(
                          "Błąd", "Data początkowa nie może być późniejsza od końcowej."
                      )
                      return
                  elif start_datetime == end_datetime:
                      tkinter.messagebox.showwarning(
                          "Ostrzeżenie",
                          (
                              "Data początkowa jest równa końcowej.\nAtaki zostaną rozpisane na dokładny czas wejścia.\n\n"
                              f'Poprawnie ustawiono czasy dla: "{selected_troops_type.name.upper()}"'
                          ),
                      )
                  else:
                      tkinter.messagebox.showinfo(
                          "Sukces",
                          f'Poprawnie ustawiono czasy dla: "{selected_troops_type.name.upper()}"',
                      )
  
                  # Nadpisanie czasów
                  models.Timeframe(start_datetime, end_datetime, selected_troops_type)
  
              else:
                  tkinter.messagebox.showerror("Błąd", "Nie wybrałeś typu wojsk!")
  
          set_datetime_btn = tkinter.Button(
              self,
              text="Ustaw czas dla wybranego typu wojsk",
              command=set_datetime_button_clicked,
          )
  
          start_date_label.grid(column=0, row=3)
          start_date_entry.grid(column=1, row=3, columnspan=3)
  
          start_time_label.grid(column=0, row=4)
          spinbox_start_hours.grid(column=1, row=4)
          spinbox_start_minutes.grid(column=2, row=4)
          spinbox_start_seconds.grid(column=3, row=4)
  
          end_date_label.grid(column=0, row=5)
          end_date_entry.grid(column=1, row=5, columnspan=3)
  
          end_time_label.grid(column=0, row=6)
          spinbox_end_hours.grid(column=1, row=6)
          spinbox_end_minutes.grid(column=2, row=6)
          spinbox_end_seconds.grid(column=3, row=6)
  
          set_datetime_btn.grid(column=0, row=7, columnspan=4)
  
          # Wyśrodkowanie wszystkich kolumn
          self.center_grid_horizontally(self)
  
          self.add_children_padding(self, 10, 10)
  