class App_Progressbar(object):
    """docstring for App_Progressbar"""
    def __init__(self, master, root, below, styles):
        super(App_Progressbar, self).__init__()
        self.master = master
        self.root = root
        self.styles = styles
        self.below = below

        self.putProgressbar()

    def putProgressbar(self):
        self.bar = LabelFrame(self.master,
                              width=0,
                              bg=self.styles['fg'],
                              height=10,
                              borderwidth=0)

    def set_pct(self, pct):
        window_width = self.root.winfo_width()
        desired_px = int(window_width * pct * 0.01)
        self.bar.config(width=desired_px)

    def show(self, *args, **kwargs):
        self.bar.pack()
        self.bar.place(x=0, y=self.below.winfo_height())

    def hide(self, *args, **kwargs):
        self.bar.place_forget(*args, **kwargs)
class App_Button(object):
    """docstring for App_Button"""
    def __init__(self,
                 master,
                 text=None,
                 styles=None,
                 command=None,
                 image=None,
                 title=None,
                 disabled=False):
        super(App_Button, self).__init__()

        self.master = master
        self.text = text
        self.styles = styles
        self.command = command
        self.image = image
        self.title = title
        self.disabled = disabled

        self.putButton()

    def putButton(self):
        self.btn_wrapper = LabelFrame(self.master, relief=FLAT, borderwidth=0)

        self.btn = Button(self.btn_wrapper,
                          text=self.text,
                          relief=FLAT,
                          bg=self.styles['btn_bg'],
                          padx=self.styles['padx'],
                          pady=self.styles['pady'],
                          fg=self.styles['btn_fg'],
                          borderwidth=0,
                          font=self.styles['big_font'],
                          command=self.command,
                          image=self.image,
                          activeforeground=self.styles['a_fg'],
                          activebackground=self.styles['a_bg'],
                          cursor="hand2")
        self.btn.image = self.image

        if self.disabled:
            self.btn.bind("<Button-1>", lambda x: "break")
        else:
            self.btn.bind("<Enter>", self.mouseover)
            self.btn.bind("<Leave>", self.mouseout)

        self.btn.pack()

        if self.title is not None:
            self.tooltip = App_Tooltip(self.btn, text=self.title)

    def mouseover(self, event):
        self.btn.config(fg=self.styles['h_fg'])
        self.btn.config(bg=self.styles['h_bg'])

    def mouseout(self, event):
        self.btn.config(fg=self.styles['btn_fg'])
        self.btn.config(bg=self.styles['btn_bg'])

    def bind(self, *args, **kwargs):
        self.btn.bind(*args, **kwargs)

    def bind_wrapper(self, *args, **kwargs):
        self.btn_wrapper.bind(*args, **kwargs)

    def pack(self, *args, **kwargs):
        self.btn_wrapper.pack(*args, **kwargs)

    def place(self, *args, **kwargs):
        self.btn_wrapper.place(*args, **kwargs)

    def config(self, *args, **kwargs):
        self.btn.config(*args, **kwargs)

    def set_tooltip(self, text):
        self.tooltip.configure(text=text)

    def pack_forget(self):
        self.btn_wrapper.pack_forget()

    def place_forget(self):
        self.btn_wrapper.place_forget()

    def winfo_rootx(self):
        return self.btn_wrapper.winfo_rootx()

    def winfo_rooty(self):
        return self.btn_wrapper.winfo_rooty()

    def winfo_height(self):
        return self.btn_wrapper.winfo_height()

    def winfo_width(self):
        return self.btn_wrapper.winfo_width()
class App_Confirm(object):
  """docstring for App_Confirm"""
  def __init__(self, master, text, styles, root, size_determiner):
    super(App_Confirm, self).__init__()

    self.master = master
    self.text = text
    self.styles = styles.copy()
    self.size_determiner = size_determiner
    self.font_px_size = self.styles['big_font'].cget("size")

    self.root = root

    self.root_width = self.root.winfo_width()
    self.root_height = self.root.winfo_height()

    self.putConfirm()

  def putConfirm(self):
    self.overlay = LabelFrame(self.master, 
      bg=self.styles['overlay'], 
      fg=self.styles['overlay'], 
      width=self.root_width, 
      height=self.root_height, 
      borderwidth=0)
    
    self.wrapper = LabelFrame(self.master, 
      bg=self.styles['bg'], 
      borderwidth=0,
      pady=19,
      padx=10)

    wraplength = int(len(self.size_determiner) * self.font_px_size)

    self.label = Label(self.wrapper, 
      bg=self.styles['bg'], 
      fg=self.styles['fg'],
      text=self.text,
      wraplength=wraplength,
      font=self.styles['big_font'],
      padx=18,
      pady=10)
    self.label.pack()

    choice_wrapper = LabelFrame(self.wrapper, 
      bg=self.styles['bg'], 
      borderwidth=0)
    choice_wrapper.pack()

    no_btn = App_Button(choice_wrapper, 
      text="No", 
      styles=self.styles, 
      command=self.hide)
    no_btn.pack(side=LEFT, padx=10)

    self.yes_btn = App_Button(choice_wrapper, text="Yes", styles=self.styles)
    self.yes_btn.pack(side=LEFT)


  def hide(self):
    self.wrapper.place_forget()
    self.overlay.place_forget()

  def show(self, confirmed_command=None):
    self.wrapper.pack()
    self.root.update_idletasks()

    wrapper_width = self.wrapper.winfo_width()
    wrapper_height = self.wrapper.winfo_height()

    x = (self.root_width / 2) - (wrapper_width / 2)
    y = (self.root_height / 2) - (wrapper_height / 2)

    self.wrapper.place(x=x, y=y)
    self.yes_btn.config(command=confirmed_command)

    self.overlay.place(x=0, y=0)

  def update(self, text):
    self.label.config(text=text)
class App_Prompt(object):
    """docstring for App_Prompt"""
    def __init__(self, master, structure, styles, root, command):
        super(App_Prompt, self).__init__()

        self.master = master
        self.structure = structure
        self.command = command
        self.styles = styles.copy()
        self.font_px_size = self.styles['big_font'].cget("size")

        self.root = root

        self.root_width = self.root.winfo_width()
        self.root_height = self.root.winfo_height()

        self.putPrompt()

    def putPrompt(self):
        self.overlay = LabelFrame(self.master,
                                  bg=self.styles['overlay'],
                                  fg=self.styles['overlay'],
                                  width=self.root_width,
                                  height=self.root_height,
                                  borderwidth=0)

        self.wrapper = LabelFrame(self.master,
                                  bg=self.styles['bg'],
                                  borderwidth=0,
                                  pady=25,
                                  padx=19)

        self.widget_arr = []
        for section in self.structure:
            row = LabelFrame(self.wrapper, bg=self.styles['bg'], borderwidth=0)
            row.pack(padx=10, pady=(0, 15))

            label = Label(row,
                          bg=self.styles['bg'],
                          fg=self.styles['fg'],
                          text=section['label'],
                          font=self.styles['big_font'])
            label.pack(side=LEFT)

            widget_name = section['widget_class']
            widget_args = section['widget_args']
            if widget_name == "App_Num_Select":
                widget = App_Num_Select(row, **widget_args)
                widget.pack(side=LEFT)
            self.widget_arr.append(widget)

        choice_wrapper = LabelFrame(self.wrapper,
                                    bg=self.styles['bg'],
                                    borderwidth=0)
        choice_wrapper.pack()

        no_btn = App_Button(choice_wrapper,
                            text="Cancel",
                            styles=self.styles,
                            command=self.hide)
        no_btn.pack(side=LEFT, padx=10)

        self.yes_btn = App_Button(choice_wrapper,
                                  text="Submit",
                                  styles=self.styles,
                                  command=self.exec_command)
        self.yes_btn.pack(side=LEFT)

    def hide(self):
        self.wrapper.place_forget()
        self.overlay.place_forget()

    def show(self, confirmed_command=None):
        self.wrapper.pack()
        self.root.update_idletasks()

        wrapper_width = self.wrapper.winfo_width()
        wrapper_height = self.wrapper.winfo_height()

        x = (self.root_width / 2) - (wrapper_width / 2)
        y = (self.root_height / 2) - (wrapper_height / 2)

        self.wrapper.place(x=x, y=y)
        self.yes_btn.config(command=confirmed_command)

        self.overlay.place(x=0, y=0)

    def exec_command(self):
        results = []

        for widget in self.widget_arr:
            results.append(widget.get())

        self.command(results)
        self.hide()
class App_Popover(object):
    """docstring for App_Popover"""
    def __init__(self, master, styles):
        super(App_Popover, self).__init__()

        self.master = master
        self.styles = styles

        self.putPopover()

    def putPopover(self):
        self.wrapper = LabelFrame(self.master,
                                  bg=self.styles['bg'],
                                  borderwidth=0)

        self.arrow = Label(self.wrapper,
                           text="→",
                           font=self.styles['big_font'],
                           bg=self.styles['bg'],
                           fg=self.styles['fg_error'])
        self.arrow.pack(side=RIGHT, padx=4)

        self.text = Label(self.wrapper,
                          text="",
                          font=self.styles['big_font'],
                          bg=self.styles['bg'],
                          fg=self.styles['fg_error'])
        self.text.pack(side=RIGHT)

    def set_text(self, text):
        self.text.config(text=text)

    def move(self, target, anchor="left"):
        self.wrapper.pack()

        self.wrapper.update_idletasks()

        target_rootx = target.winfo_rootx()
        target_rooty = target.winfo_rooty()
        target_width = target.winfo_width()
        target_height = target.winfo_height()

        wrapper_width = self.wrapper.winfo_width()
        wrapper_height = self.wrapper.winfo_height()

        y_pos = target_rooty - (wrapper_height / 2)

        if anchor == "left":
            self.arrow.config(text="→")
            x_pos = target_rootx - wrapper_width
            self.arrow.pack(side=RIGHT, padx=4)
            self.text.pack(side=RIGHT)

        elif anchor == "right":
            self.arrow.config(text="←")
            x_pos = target_rootx + target_width

            self.arrow.pack(side=LEFT)
            self.text.pack(side=LEFT, padx=4)

        self.wrapper.place(x=x_pos, y=y_pos)

    def hide(self):
        self.wrapper.place_forget()
class App_Photo(object):
  """docstring for App_Photo"""
  def __init__(self, master, pil_photo, styles, root, fullsize_path, hide_cmd=None, del_cmd=None):
    super(App_Photo, self).__init__()

    self.master = master
    self.pil_photo = pil_photo
    self.fullsize_path = fullsize_path

    self.photo = PIL.ImageTk.PhotoImage(pil_photo)

    self.styles = styles
    self.tool_styles = styles.copy()
    self.tool_styles['big_font'] = styles['font']
    self.tool_styles['padx'] = 5
    self.tool_styles['pady'] = 3

    self.root = root

    self.hide_cmd = hide_cmd
    self.del_cmd = del_cmd

    self.putPhoto()

  def putPhoto(self):
    self.wrapper = LabelFrame(self.master, bg=self.styles['bg'], borderwidth=0)
    self.photo_label = Label(self.wrapper, 
      image=self.photo, 
      borderwidth=0, 
      cursor="hand2")

    self.photo_label.bind("<Button-1>", self.zoom)
    self.photo_label.bind("<Enter>", self.show_tools)
    self.photo_label.bind("<Leave>", self.hide_tools)
    self.photo_label.image = self.photo
    self.photo_label.pack()


    self.minimize_tool = App_Button(self.wrapper, 
      text="➖", 
      styles=self.tool_styles,
      command=self.hide_cmd)
    self.minimize_tool.bind_wrapper("<Enter>", self.show_tools)
    self.minimize_tool.place(x=0, y=0)
    self.minimize_tool.place_forget()


    self.delete_tool = App_Button(self.wrapper, 
      text="✕", 
      styles=self.tool_styles,
      command=self.del_cmd)
    self.delete_tool.bind_wrapper("<Enter>", self.show_tools)
    self.delete_tool.place(x=0, y=0)
    self.delete_tool.place_forget()

    self.overlay = LabelFrame(self.root, 
      bg=self.styles['overlay'], 
      fg=self.styles['overlay'],
      borderwidth=0)

    self.overlay_close = App_Button(self.overlay, 
      text="⩁", 
      title="Close the zoomed view",
      styles=self.styles,
      command=self.un_zoom)
    self.overlay_close.place(x=0, y=0)

  def zoom(self, event):
    window_width = self.root.winfo_width()
    window_height = self.root.winfo_height()

    self.overlay.config(width=window_width)
    self.overlay.config(height=window_height)
    self.overlay.place(x=0, y=0)

    zoomed_width = int(window_width * 0.8)
    zoomed_height = int(window_height * 0.8)

    pil_photo = PIL.Image.open(self.fullsize_path)

    wpercent = (zoomed_width/float(pil_photo.size[0]))
    hsize = int((float(pil_photo.size[1])*float(wpercent)))
    zoomed_pil_photo = pil_photo.resize((zoomed_width,hsize), PIL.Image.ANTIALIAS)

    hpercent = (zoomed_height/float(pil_photo.size[1]))
    wsize = int((float(pil_photo.size[0])*float(hpercent)))
    zoomed_pil_photo = pil_photo.resize((wsize,zoomed_height), PIL.Image.ANTIALIAS)

    zoomed_photo = PIL.ImageTk.PhotoImage(zoomed_pil_photo)

    self.zoomed_photo_label = Label(self.root, 
      image=zoomed_photo, 
      borderwidth=0)
    self.zoomed_photo_label.image = zoomed_photo
    self.zoomed_photo_label.pack()

    photo_x_pos = (window_width / 2) - (wsize / 2)
    photo_y_pos = (window_height / 2) - (zoomed_height / 2)

    overlay_close_width = self.overlay_close.winfo_width()

    self.overlay_close.place(x=window_width-overlay_close_width-25, y=25)
    self.zoomed_photo_label.place(x=photo_x_pos, y=photo_y_pos)
    
  def un_zoom(self):
    self.overlay.place_forget()
    self.zoomed_photo_label.place_forget()

  def show_tools(self, event):
    photo_x = self.photo_label.winfo_x()
    photo_y = self.photo_label.winfo_y()
    photo_width = self.photo_label.winfo_width()

    minimize_tool_width = self.minimize_tool.winfo_width()
    delete_tool_width = self.delete_tool.winfo_width()

    minimize_tool_x_pos = photo_x + photo_width - minimize_tool_width - delete_tool_width - 5
    delete_tool_x_pos = photo_x + photo_width - delete_tool_width
    tool_y_pos = photo_y + 5

    self.minimize_tool.place(x=minimize_tool_x_pos, y=tool_y_pos)
    self.delete_tool.place(x=delete_tool_x_pos, y=tool_y_pos)

  def hide_tools(self, event):
    self.minimize_tool.place_forget()
    self.delete_tool.place_forget()

  def bind(self, *args, **kwargs):
    self.wrapper.bind(*args, **kwargs)

  def pack(self, *args, **kwargs):
    self.wrapper.pack(*args, **kwargs)

  def destroy(self, *args, **kwargs):
    self.wrapper.destroy(*args, **kwargs)