# Python imports
import os
from os.path import isfile
import hashlib
import threading

# Lib imports
import gi
gi.require_version('GdkPixbuf', '2.0')
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import GdkPixbuf

try:
    from PIL import Image as PImage
except ModuleNotFoundError as e:
    PImage = None

# Application imports
from .mixins.videoiconmixin import VideoIconMixin
from .mixins.meshsiconmixin import MeshsIconMixin
from .mixins.desktopiconmixin import DesktopIconMixin



class IconException(Exception):
    ...



class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
    def create_icon(self, dir, file):
        full_path = f"{dir}/{file}"
        return self.get_icon_image(dir, file, full_path)

    def get_icon_image(self, dir, file, full_path):
        try:
            thumbnl = None

            if file.lower().endswith(self.fmeshs):               # 3D Mesh icon
                ...
            elif file.lower().endswith(self.fvideos):            # Video icon
                thumbnl = self.create_video_thumbnail(full_path)
            elif file.lower().endswith(self.fimages):            # Image Icon
                thumbnl = self.create_scaled_image(full_path)
            elif file.lower().endswith( (".blend",) ):           # Blender icon
                thumbnl = self.create_blender_thumbnail(full_path)
            elif full_path.lower().endswith( ('.desktop',) ):    # .desktop file parsing
                thumbnl = self.find_thumbnail_from_desktop_file(full_path)

            if not thumbnl:
                # TODO: Detect if not in a thread and use directly for speed get_system_thumbnail
                thumbnl = self.get_system_thumbnail(full_path, self.sys_icon_wh[0])
                # thumbnl = self._get_system_thumbnail_gtk_thread(full_path, self.sys_icon_wh[0])
                if not thumbnl:
                    raise IconException("No known icons found.")


            return thumbnl
        except IconException:
            ...

        return self.get_generic_icon()

    def create_blender_thumbnail(self, full_path, returnHashInstead=False):
        try:
            path_exists, img_hash, hash_img_path = self.generate_hash_and_path(full_path)
            if not path_exists:
                self.generate_blender_thumbnail(full_path, hash_img_path)

            if returnHashInstead:
                return img_hash, hash_img_path

            return self.create_scaled_image(hash_img_path, self.video_icon_wh)
        except IconException as e:
            print("Blender thumbnail generation issue:")
            print( repr(e) )

        return None

    def create_video_thumbnail(self, full_path, scrub_percent = "65%", replace=False, returnHashInstead=False):
        try:
            path_exists, img_hash, hash_img_path = self.generate_hash_and_path(full_path)
            if path_exists and replace:
                os.remove(hash_img_path)
                path_exists = False

            if not path_exists:
                self.generate_video_thumbnail(full_path, hash_img_path, scrub_percent)

            if returnHashInstead:
                return img_hash, hash_img_path

            return self.create_scaled_image(hash_img_path, self.video_icon_wh)
        except IconException as e:
            print("Image/Video thumbnail generation issue:")
            print( repr(e) )

        return None


    def create_scaled_image(self, full_path, wxh = None):
        if not wxh:
            wxh = self.video_icon_wh

        if full_path:
            try:
                if full_path.lower().endswith(".gif"):
                    return  GdkPixbuf.PixbufAnimation.new_from_file(full_path) \
                                                        .get_static_image() \
                                                        .scale_simple(wxh[0], wxh[1], GdkPixbuf.InterpType.BILINEAR)
                elif full_path.lower().endswith(".webp") and PImage:
                    return self.image2pixbuf(full_path, wxh)

                pixbuf = None
                try:
                    pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(full_path, wxh[0], wxh[1], True)
                except Exception as e:
                    ...

                return pixbuf
            except IconException as e:
                print("Image Scaling Issue:")
                print( repr(e) )

        return None

    def create_from_file(self, full_path):
        try:
            return GdkPixbuf.Pixbuf.new_from_file(full_path)
        except IconException as e:
            print("Image from file Issue:")
            print( repr(e) )

        return None

    def _get_system_thumbnail_gtk_thread(self, full_path, size):
        def _call_gtk_thread(event, result):
            result.append( self.get_system_thumbnail(full_path, size) )
            event.set()

        result  = []
        event   = threading.Event()
        GLib.idle_add(_call_gtk_thread, event, result)
        event.wait()
        return result[0]


    def get_system_thumbnail(self, full_path, size):
        try:
            gio_file  = Gio.File.new_for_path(full_path)
            info      = gio_file.query_info('standard::icon' , 0, None)
            icon      = info.get_icon().get_names()[0]
            data      = settings_manager.get_icon_theme().lookup_icon(icon , size , 0)

            if data:
                icon_path = data.get_filename()
                return GdkPixbuf.Pixbuf.new_from_file(icon_path)

            raise IconException("No system icon found...")
        except IconException:
            ...

        return None

    def get_generic_icon(self):
        return GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICON)

    def generate_hash_and_path(self, full_path):
        img_hash      = self.fast_hash(full_path)
        hash_img_path = f"{self.ABS_THUMBS_PTH}/{img_hash}.jpg"
        path_exists   = True if isfile(hash_img_path) else False

        return path_exists, img_hash, hash_img_path


    def fast_hash(self, filename: str, hash_factory: callable = hashlib.md5, chunk_num_blocks: int = 128, i: int = 1) -> str:
        h = hash_factory()
        with open(filename,'rb') as f:
            # NOTE: Jump to middle of file
            f.seek(0, 2)
            mid = int(f.tell() / 2)
            f.seek(mid, 0)

            while chunk := f.read(chunk_num_blocks * h.block_size):
                h.update(chunk)
                if (i == 12):
                    break

                i += 1

        return h.hexdigest()

    def image2pixbuf(self, full_path, wxh):
        """Convert Pillow image to GdkPixbuf"""
        im   = PImage.open(full_path)
        data = im.tobytes()
        data = GLib.Bytes.new(data)
        w, h = im.size

        pixbuf = GdkPixbuf.Pixbuf.new_from_bytes(data, GdkPixbuf.Colorspace.RGB,
                                                            False, 8, w, h, w * 3)

        return pixbuf.scale_simple(wxh[0], wxh[1], 2) # BILINEAR = 2
