import logging
import os
import random
import sys
import tkinter as tkinter_module
from pathlib import Path
from threading import Thread
import argparse

from PIL import Image, ImageTk
from PIL import ImageDraw, ImageFont
from PIL import ImageOps
from flask import Flask, send_from_directory

# Try to import geolocation related packages
try:
    import piexif
    from geopy.geocoders import Nominatim
    from geopy.exc import GeocoderTimedOut
    GEOLOCATION_AVAILABLE = True
except ImportError:
    GEOLOCATION_AVAILABLE = False
    print("Geolocation features not available. Install required packages: pip install piexif geopy")

logger: logging.Logger = logging.getLogger("slideshow")

# Global reference to the App instance, set after creation
_app_instance = None


def startflask():
    print('started app')
    flask_app.run(host="0.0.0.0", port=8090)


flask_app = Flask(__name__, static_url_path="", static_folder="web", )
thread = Thread(target=startflask, daemon=True)


@flask_app.route('/', methods=['GET'])
def upload_file():
    return send_from_directory("web", "index.html")


@flask_app.route('/filename', methods=['GET'])
def filename():
    if _app_instance is not None:
        return _app_instance.curr
    return ""


class App(tkinter_module.Tk):
    fontname = "arial.ttf" if os.name == "nt" else "FreeMono.ttf"

    def __init__(self, image_files, delay, show_names=False, show_location=False):
        tkinter_module.Tk.__init__(self)
        self.curr = ""
        self.ret = None
        self.w = self.winfo_screenwidth()
        self.h = self.winfo_screenheight()
        self.attributes('-fullscreen', True)
        self.geometry("%dx%d+0+0" % (self.w, self.h))
        self.bind("<Escape>", self.escape)
        self.bind("<space>", self.space)
        self.bind('<KeyPress>', self.onKeyPress)
        self.focus_set()
        self.delay = delay
        self.pictures = []
        self.track_img_ndex = 0
        self.show_names = show_names
        self.show_location = show_location

        random.shuffle(image_files)
        for img in image_files:
            self.pictures.append(img)
        self.picture_display = tkinter_module.Label(self)
        self.configure(background='black')
        self.picture_display.configure(background='black')
        self.picture_display.pack(expand=True, fill="both")
        
        # Hide cursor
        self.config(cursor="none")

    def escape(self, e):
        exit()

    def onKeyPress(self, event):
        if event.keysym == "Left":
            self.left()
        if event.keysym == "Right":
            self.right()
        if event.keysym == "5":
            self.space(event)
        if event.keysym == "l":
            self.toggle_location(event)

    def toggle_location(self, e):
        self.show_location = not self.show_location
        self.track_img_ndex = self.track_img_ndex - 1
        self.show_slides(True)

    def left(self):
        self.track_img_ndex = self.track_img_ndex - 2
        self.show_slides(True)

    def right(self):
        self.show_slides(True)

    def space(self, e):
        self.show_names = not self.show_names
        self.track_img_ndex = self.track_img_ndex - 1
        self.show_slides(True)

    def show_slides(self, now=False):
        try:
            if now and self.ret is not None:
                self.after_cancel(self.ret)

            self.track_img_ndex = self.track_img_ndex % len(self.pictures)
            x = self.pictures[self.track_img_ndex]
            self.track_img_ndex += 1
            original_image = Image.open(x)

            original_image = ImageOps.exif_transpose(original_image)
            original_image.thumbnail((self.w, self.h), Image.LANCZOS)
            
            # Create a drawing context
            image_editable = ImageDraw.Draw(original_image)
            
            # Draw filename at the bottom center if enabled
            if self.show_names:
                filename = os.path.basename(x)
                font_size = int(self.w / 80)  # Made font size smaller
                font = ImageFont.truetype(self.fontname, size=font_size)
                
                # Get text size to calculate position
                text_bbox = image_editable.textbbox((0, 0), filename, font=font)
                text_width = text_bbox[2] - text_bbox[0]
                text_height = text_bbox[3] - text_bbox[1]
                
                # Calculate position (centered at bottom with padding)
                x_pos = (original_image.width - text_width) // 2
                y_pos = original_image.height - text_height - 30  # 25 pixels padding from bottom (was 20)
                
                # Draw text with a slight shadow for better visibility
                image_editable.text((x_pos+2, y_pos+2), filename, (0, 0, 0), font=font)  # Shadow
                image_editable.text((x_pos, y_pos), filename, (255, 255, 0), font=font)  # Yellow text

            # Try to get location information if enabled and available
            if self.show_location and GEOLOCATION_AVAILABLE:
                self.add_location(original_image, image_editable)
            
            new_img = ImageTk.PhotoImage(original_image)

            self.picture_display.config(image=new_img)
            self.picture_display.image = new_img
            self.title(os.path.basename(x))
            self.curr = x
            logger.info(str(self.track_img_ndex) + " " + x)
            self.ret = self.after(self.delay, self.show_slides)

        except Exception as ex:
            logger.error(ex)
            self.after(500, self.show_slides)

    def add_location(self, original_image, image_editable):
        try:
                # Extract GPS data from EXIF
            exif_data = original_image.info.get("exif")
            if not exif_data:
                return
            exif_dict = piexif.load(exif_data)
            if "GPS" in exif_dict and exif_dict["GPS"]:
                gps = exif_dict["GPS"]
                    
                    # Helper function to safely get GPS value
                def get_gps_value(gps_data, key):
                    if key in gps_data:
                        value = gps_data[key]
                        if isinstance(value, tuple):
                            if len(value) == 2:  # Rational format e.g. (34199, 2373)
                                return float(value[0]) / float(value[1])
                            elif len(value) == 3:  # Standard DMS (degrees, minutes, seconds)
                                d = float(value[0][0]) / float(value[0][1])
                                m = float(value[1][0]) / float(value[1][1])
                                s = float(value[2][0]) / float(value[2][1])
                                return d + (m / 60.0) + (s / 3600.0)
                            elif len(value) == 4:  # Special format (d_num, d_den, m_num, m_den)
                                d = float(value[0]) / float(value[1])
                                m = float(value[2]) / float(value[3])
                                return d + (m / 60.0)
                        elif isinstance(value, int):
                            return float(value)
                        return float(value)
                    return None

                    # Helper function to get GPS reference
                def get_gps_ref(gps_data, key, default):
                    if key in gps_data:
                        value = gps_data[key]
                        if isinstance(value, bytes):
                            return value.decode('utf-8')
                        return str(value)
                    return default

                # Helper function to decode specific GPS format
               

                # Try different GPS tag combinations
                lat = None
                lon = None
                
                # If that didn't work, try standard GPS tags
                if lat is None and lon is None and 1 in gps and 2 in gps:  # GPSLatitude and GPSLongitude
                    try:
                        lat_ref = get_gps_ref(gps, 1, 'N')  # GPSLatitudeRef
                        lon_ref = get_gps_ref(gps, 3, 'E')  # GPSLongitudeRef
                            
                        lat = get_gps_value(gps, 2)  # GPSLatitude
                        lon = get_gps_value(gps, 4)  # GPSLongitude
                            
                        logger.debug(f"Extracted coordinates: lat={lat}, lon={lon}, lat_ref={lat_ref}, lon_ref={lon_ref}")
                            
                        if lat_ref == 'S':
                            lat = -lat
                        if lon_ref == 'W':
                            lon = -lon
                            
                            # If we have valid coordinates, try to get the country
                        if lat is not None and lon is not None:
                                # Convert coordinates to country name
                            geolocator = Nominatim(user_agent="my_slideshow")
                            try:
                                location = geolocator.reverse(f"{lat}, {lon}", language="he")
                                if location and location.raw.get('address'):
                                    address = location.raw['address']
                                    country = address.get('country', '')
                                        # Try to get area name in order of specificity
                                    area = (address.get('city', '') or 
                                                address.get('town', '') or 
                                                address.get('village', '') or 
                                                address.get('county', '') or 
                                                address.get('state', '') or 
                                                '')
                                        
                                    if country or area:
                                            # Draw location text below filename
                                        font_size = int(self.w / 80)  # Same size as filename
                                        font = ImageFont.truetype(self.fontname, size=font_size)
                                            
                                            # Prepare location text
                                        location_text = []
                                        if area:
                                            location_text.append(area)
                                        if country:
                                            location_text.append(country)
                                        location_text = ', '.join(location_text)
                                            
                                            # Reverse each word individually for Hebrew RTL display,
                                            # preserving word order and punctuation
                                        parts = location_text.split(', ')
                                        reversed_parts = [''.join(reversed(p)) for p in parts]
                                        location_text = ' ,'.join(reversed_parts)
                                            
                                            # Calculate position for location text
                                        text_bbox = image_editable.textbbox((0, 0), location_text, font=font)
                                        text_width = text_bbox[2] - text_bbox[0]
                                        text_height = text_bbox[3] - text_bbox[1]
                                        x_pos = original_image.width - text_width
                                        y_pos = 0#original_image.height - text_height - 10  # Position at bottom with small padding
                                            
                                            # Draw location text with shadow
                                        image_editable.text((x_pos+2, y_pos+2), location_text, (0, 0, 0), font=font)
                                        image_editable.text((x_pos, y_pos), location_text, (255, 255, 0), font=font)
                            except GeocoderTimedOut:
                                pass
                    except Exception as e:
                        logger.error(f"Error processing standard GPS tags: {e}")
                        logger.error(f"GPS data structure: {gps}")
        except Exception as e:
            logger.error(f"Error processing GPS data: {e}")


if __name__ == '__main__':
    # Parse command line arguments
    parser = argparse.ArgumentParser(description='Image Slideshow')
    parser.add_argument('--names', action='store_true', help='Show image names')
    parser.add_argument('--no-location', action='store_true', help='Hide location information')
    parser.add_argument('--root', type=str, help='Override the root location for images')
    args = parser.parse_args()

    delay = 6000
    extensions = (".jpg", ".jpeg", ".heic")
    root = args.root if args.root else "C:\\temp\\photos"
    # if folder root not found try to use current directory
    if not os.path.isdir(root):
        root = "Z:\\temp\\photos"
    if not os.path.isdir(root):
        root = os.getcwd()

    image_files = []
    for path in Path(root).rglob("*.*"):
        for e in extensions:
            if path.__str__().lower().endswith(e):
                image_files.append(path.__str__())

    if not image_files:
        print(f"No image files found in '{root}'. Supported formats: {', '.join(extensions)}")
        sys.exit(1)

    fmt = '%(asctime)s %(name)s  %(levelname)s:  %(message)s'
    dfmt = '%d-%b-%y %H:%M:%S'
    log_file = 'c:/temp/slideshow.log' if os.name == 'nt' else '/tmp/slideshow.log'
    logging.basicConfig(filename=log_file, filemode='a', level=logging.INFO,
                        format=fmt, datefmt=dfmt)
    handler = logging.StreamHandler(sys.stdout)
    handler.setFormatter(logging.Formatter(fmt=fmt, datefmt=dfmt))
    handler.setLevel(logging.DEBUG)
    logging.getLogger().addHandler(handler)
    logging.info("started")

    thread.start()

    print(image_files)
    print(len(image_files))
    _app_instance = App(image_files, delay, show_names=args.names, show_location=not args.no_location)
    _app_instance.show_slides()
    _app_instance.mainloop()
