#madebyr

import tkinter as tk
from tkinter import filedialog, simpledialog, messagebox
from PIL import Image, ImageTk
import piexif
import os
import random
import threading
import time
from queue import Queue
import threading

class ImageViewer:
    def __init__(self, root):
        self.SUPPORTED_EXTENSIONS = (".png", ".jpg", ".jpeg", ".gif", ".bmp", ".JPG", ".JPEG", ".BMP", ".GIF", ".webp", ".WEBP")
        self.button_style = {"font": ("consolas", 10), "fg": "white", "bg": "#3c3c3c", "relief": "flat"}
		
        self.image_loading_queue = Queue(maxsize=3)
        self.switch_timestamps = []
        self.is_button_press_in_progress = False  # Variable to track if button is pressed
		
        self.root = root
        self.root.geometry("800x600")  # Initial window size
        self.root.title("Jestur Viewer")
        self.root.configure(bg="#808080")  # Set window background color
        self.root.attributes("-topmost", True)  # Make the window stay on top

        self.image_folder = ""
        self.image_files = []
        self.current_image_index = 0

        self.main_frame = tk.Frame(self.root, bg="#808080")
        self.main_frame.pack()

        self.select_folder_button = tk.Button(self.main_frame, text="Select Folder", command=self.select_folder)
        self.select_folder_button.configure(**self.button_style)
        self.select_folder_button.pack(side=tk.LEFT, padx=5, pady=10)

        self.set_timer_button = tk.Button(self.main_frame, text="Timer", command=self.set_timer_interval)
        self.set_timer_button.configure(**self.button_style)
        self.set_timer_button.pack(side=tk.LEFT, padx=5, pady=10)

        self.start_button = tk.Button(self.main_frame, text="Start", command=self.start_pause_slideshow)
        self.start_button.configure(font=self.button_style["font"], bg="#909090", fg="#707070", relief=tk.FLAT)
        self.start_button.pack(side=tk.LEFT, padx=5, pady=10)

        self.mirror_button = tk.Button(self.main_frame, text=">|<", command=self.toggle_mirror_image)
        self.mirror_button.configure(**self.button_style)
        self.mirror_button.pack(side=tk.LEFT, padx=5, pady=10)
        
        self.greyscale_button = tk.Button(self.main_frame, text="B/W", command=self.toggle_greyscale)
        self.greyscale_button.configure(**self.button_style)
        self.greyscale_button.pack(side=tk.LEFT, padx=5, pady=10)
        
		
        self.random_next_image_var = tk.BooleanVar(value=False) # Checkbox variable
        self.random_next_image_checkbox = tk.Checkbutton(self.main_frame, text="Random", variable=self.random_next_image_var, command=self.toggle_colors)
        self.random_next_image_checkbox.configure(**self.button_style, bd=2)
        self.random_next_image_checkbox.pack(side=tk.LEFT, padx=5, pady=10)
		
		#image label
        self.canvas = tk.Canvas(self.root)
        self.is_mirrored = False
        self.is_greyscale = False
        self.is_paused = False
		
        self.timer_label = tk.Label(self.root, text="5", **self.button_style, anchor="ne") #fg="white", bg="black", font=("arial", 12), bd=1, relief=tk.RIDGE)
        self.timer_label.configure(font=("consolas",17))
        self.timer_label.place(relx=1, anchor="ne", x=-0, y=0)

        self.timer_interval = 5000  # Default time interval in milliseconds (5 seconds)
        self.set_timer_interval = 5000  # Store the set timer interval
        self.timer = None  # Timer ID
		
        self.window_size = (self.root.winfo_width(), self.root.winfo_height())
        self.resize_timer = None

        self.root.bind("<Right>", self.next_image)
        self.root.bind("<Left>", self.previous_image)
        self.root.bind("<space>", self.start_pause_slideshow)
        self.root.bind("<Configure>", self.update_image_size)
        self.root.bind("<KeyRelease-Left>", self.release_button_press)
        self.root.bind("<KeyRelease-Right>", self.release_button_press)
		
    def manage_button_press(self):
        self.is_button_press_in_progress = True
        if hasattr(self, "delayed_display_image_timer"):
            self.root.after_cancel(self.delayed_display_image_timer)
			
    def release_button_press(self, event=None):
        self.is_button_press_in_progress = False
        # Schedule the delayed display of the last intended image
        self.delayed_display_image_timer = self.root.after(100, self.display_current_image)
		
    def toggle_colors(self):
        if self.random_next_image_var.get():
            self.random_next_image_checkbox.configure(fg="#2c2c2c", bg="#d8d8d8")
        else:
            self.random_next_image_checkbox.configure(**self.button_style)

    def select_folder(self):
        self.image_folder = filedialog.askdirectory()
        if self.image_folder:
            image_files = os.listdir(self.image_folder) # Get all files in the selected folder
            self.image_files = [file for file in image_files if file.endswith(self.SUPPORTED_EXTENSIONS)] # Filter image files
            
            if len(self.image_files) > 0:
                self.current_image_index = 0
                self.canvas.pack(fill=tk.BOTH, expand=True)
                self.select_folder_button.pack(side=tk.LEFT, padx=5, pady=5)
                self.root.title("Jesturing in " + self.image_folder)
                self.canvas.config(bg="#808080", highlightthickness=0)
                self.display_image()   
                self.start_button.config(**self.button_style)
            else:
                messagebox.showinfo("No Image Files", "The selected folder does not contain any image files.")
                self.image_folder = ""
                self.root.title("Jestur")

    def set_timer_interval(self):
        self.root.attributes("-topmost", False)
        interval = simpledialog.askinteger("Set Timer Interval", "How many seconds?")        
        if interval:
            if interval < 1:  # Check if interval is less than 1 second
                interval = 1
            self.timer_interval = interval * 1000  # Convert to milliseconds
            self.set_timer_interval = self.timer_interval  # Store the set timer interval
        self.root.lift()  # Bring the main window to the top
        self.root.focus_force()  # Give focus to the main window
        self.root.attributes("-topmost", True)

    def start_pause_slideshow(self, event=None):
        if self.image_folder == "":
            messagebox.showinfo("You haven't picked a folder", "Please select an image folder first.")
        else:
            if self.timer is None:  # Check if timer is not running
                self.display_image()
                self.start_button.config(text="Pause")
                self.is_paused = False  # Set paused flag to False
                self.start_timer()
            else:
                if self.is_paused:  # Check if the slideshow is paused
                    self.start_button.config(text="Pause")
                    self.is_paused = False  # Set paused flag to False
                    self.start_timer()
                else:
                    self.pause_timer()
                    self.start_button.config(text="Start")
                    self.is_paused = True  # Set paused flag to True
    
    def pause_timer(self):
        if self.timer is not None:
            self.root.after_cancel(self.timer)
            self.timer = None

    def start_timer(self):
        if self.image_folder != "" and not self.is_paused:
            self.update_timer()
            self.timer = self.root.after(1000, self.start_timer)

    def update_timer(self):
        seconds_left = int(self.timer_interval / 1000)
        self.timer_label.config(text=f"{seconds_left}")
        self.timer_interval -= 1000
    
        if self.timer_interval < 0:
            self.timer_interval = self.set_timer_interval  # Use the stored set timer interval
            # Do not call next_image(); instead reset index and display the image if it is the time for a slide change
            if not self.random_next_image_var.get():
                self.current_image_index = (self.current_image_index + 1) % len(self.image_files)
            else:
                self.current_image_index = random.randint(0, len(self.image_files) - 1)
            self.display_image()
			
    def toggle_mirror_image(self):
        self.is_mirrored = not self.is_mirrored
        self.display_image()

    def toggle_greyscale(self):
        self.is_greyscale = not self.is_greyscale
        self.display_image()			
		
    def load_image(self, image_path):
        image = Image.open(image_path)
        
        # Check if the image has EXIF data
        if "exif" in image.info:
            exif_data = piexif.load(image.info["exif"])
            if piexif.ImageIFD.Orientation in exif_data["0th"]:
                orientation = exif_data["0th"][piexif.ImageIFD.Orientation]
                if orientation == 3:
                    image = image.rotate(180, expand=True)
                elif orientation == 6:
                    image = image.rotate(-90, expand=True)
                elif orientation == 8:
                    image = image.rotate(90, expand=True)
    
        if self.is_greyscale:
            image = image.convert("L")
    
        if self.is_mirrored:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)
    
        aspect_ratio = image.width / image.height
        canvas_width = self.canvas.winfo_width()
        canvas_height = self.canvas.winfo_height()
        max_width = min(canvas_width, int(aspect_ratio * canvas_height))
        max_height = min(canvas_height, int(canvas_width / aspect_ratio))
        scale_factor = min(max_width / image.width, max_height / image.height)
        new_width = int(image.width * scale_factor)
        new_height = int(image.height * scale_factor)
    
        if new_width > 0 and new_height > 0:
            resized_image = image.resize((new_width, new_height), Image.BICUBIC)
            self.photo = ImageTk.PhotoImage(resized_image)
            self.canvas.delete("all")
            self.canvas.create_image(canvas_width // 2, canvas_height // 2, image=self.photo)

    def display_image(self):
        if self.image_folder != "" and self.image_files:
            if self.should_load_image():  # We should immediately load the image
                image_path = os.path.join(self.image_folder, self.image_files[self.current_image_index])
                threading.Thread(target=self.load_image, args=(image_path,)).start()
            else:  # Handling rapid browsing or long press by showing only image name
                image_name = self.image_files[self.current_image_index]
                self.show_image_name(image_name)
    
    def show_image_name(self, name):
        self.canvas.delete("all")
        self.canvas.create_text(
            self.canvas.winfo_width() // 2,
            self.canvas.winfo_height() // 2,
            text=name,
            fill="white"
        )
    
    def update_image_size(self, event=None):
        current_width = self.root.winfo_width()
        current_height = self.root.winfo_height()
    
        if (current_width, current_height) != self.window_size:
            self.window_size = (current_width, current_height)
            self.canvas.config(width=current_width, height=current_height)
            self.buffer_forsize()
			
    def buffer_forsize(self, event=None):
        if self.resize_timer:
            self.root.after_cancel(self.resize_timer)
        self.resize_timer = self.root.after(500, self.display_image)
		
    def update_switch_timestamps(self):
        current_time = time.time()
        self.switch_timestamps.append(current_time)
        # Clean up old timestamps
        threshold_time = 1
        self.switch_timestamps = [t for t in self.switch_timestamps if current_time - t <= threshold_time]
        
        rapid_browsing = len(self.switch_timestamps) > 6  # Adjust this threshold as needed
        if rapid_browsing:
            # Schedule the delayed display of the current image
            self.display_current_image_with_delay()
            self.switch_timestamps.clear()  # Clear timestamps to reset rapid browsing detection
        return rapid_browsing
		
    def queue_image_loading(self):
        if self.image_loading_queue.full():
            # Clear the queue if it"s full
            with self.image_loading_queue.mutex:
                self.image_loading_queue.queue.clear()
        self.image_loading_queue.put(self.current_image_index)
        self.process_image_loading_queue()

    def process_image_loading_queue(self):
        if not self.image_loading_queue.empty():
            # Only process the last item in the queue
            while self.image_loading_queue.qsize() > 1:
                self.image_loading_queue.get()
            last_index = self.image_loading_queue.get()
            self.root.after_cancel(self.timer) if self.timer else None
            self.current_image_index = last_index
            threading.Thread(target=self.display_image).start()
            self.timer_interval = self.set_timer_interval	
		
    def next_image(self, event=None):
        self.manage_button_press()
        if self.image_folder != "" and self.image_files:
            if self.should_load_image():
                if self.random_next_image_var.get():
                    # Skip random selection if navigating through images too rapidly or holding the button too long
                    self.current_image_index = random.randint(0, len(self.image_files) - 1)
                else:
                    self.current_image_index = (self.current_image_index + 1) % len(self.image_files)
                self.display_image()

    
    def previous_image(self, event=None):
        self.manage_button_press()  # Indicate start of button press
        if self.image_folder != "" and self.image_files:  # Ensure we have images to handle
            if self.should_load_image():
                if self.random_next_image_var.get():
                    # Skip random selection if navigating through images too rapidly or holding the button too long
                    self.current_image_index = random.randint(0, len(self.image_files) - 1)
                else:
                    self.current_image_index = (self.current_image_index - 1) % len(self.image_files)
                self.display_image()

            
    def display_current_image_with_delay(self):
        # Cancel any previous image display attempts
        if hasattr(self, "delayed_display_image_timer"):
            self.root.after_cancel(self.delayed_display_image_timer)
    
        # Schedule a new display attempt for the current image after the delay
        self.delayed_display_image_timer = self.root.after(300, self.display_current_image)

    def display_current_image(self):
        # Call the original method to load and display the current image
        self.display_image()

    def should_load_image(self):
        threshold_rapid_presses = 6  # after how many rapid presses to display the image name
        threshold_time = 1  # how long to consider presses as "rapid" in seconds
        current_time = time.time()
        self.switch_timestamps.append(current_time)
        self.switch_timestamps = [t for t in self.switch_timestamps if current_time - t <= threshold_time]
    
        # Reset timer interval
        self.timer_interval = self.set_timer_interval
    
        # Check if rapid or long pressing is occurring
        if len(self.switch_timestamps) > threshold_rapid_presses or self.is_button_press_in_progress:
            # Schedule the delayed display of the current image
            self.display_current_image_with_delay()
            return False
        return True

if __name__ == "__main__":
    root = tk.Tk()
    image_viewer = ImageViewer(root)
    root.mainloop()

on the next and previous button presses. it just load image name and stays there instead of loading the next image name or show image. it’s supposed to load and show images immediately on next and previous press, even when rapidly pressed or held, but only if it’s too much, it loads just the image name of the current image it’s supposed to be showing and browse that way until the key press is released, or when there’s not too much in queue. so the behaviour that i’m looking for is it loads images immediately on next and previous button press but if it’s too much on the queue or too rapid, it only show image name as i browse through snappy, then when i stop pressing at the current image, it refreshes and load the image as it’s supposed to. can you just give me the complete fixed code with that behaviour with a more neat arrangement and comment included on each method. i want the new and improved code entirely in full without any parts cut off so i can just replace the entire code from beginning to end instead of replacing parts of it.