__version__ = "1.0.0"

# --- package ---
# --- color ---
import webcolors

# --- image ---
from pathlib import Path
from PIL import Image, ImageSequence
import cv2
import numpy as np
from io import BytesIO

# --- base64 ---
import base64

# --- common ---
import os
from datetime import datetime
import shutil
import time
import inspect

def hex_to_rgb(hex_code):
    """Convert a hex code to RGB values."""
    hex_code = hex_code.lstrip('#')
    return tuple(int(hex_code[i:i+2], 16) for i in (0, 2, 4))

def rgb_to_hex(rgb_values):
    """Convert RGB values to a hex code."""
    r, g, b = rgb_values
    hex_code = "{:02x}{:02x}{:02x}".format(r, g, b)
    return hex_code

def get_color_name(rgb_values):
    """Return the name of the closest matching color."""
    min_colors = {}
    for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
        r_c, g_c, b_c = hex_to_rgb(key)
        rd = (r_c - rgb_values[0]) ** 2
        gd = (g_c - rgb_values[1]) ** 2
        bd = (b_c - rgb_values[2]) ** 2
        min_colors[(rd + gd + bd)] = name

    return min_colors[min(min_colors.keys())]

def image_resize(import_path, import_raw_path, export_path, img, resize_target, resize_resolution):
    image_ext = os.path.splitext(img)[1].split('.')[1]

    move_date = datetime.now().strftime('%y%m%d%H%M%S')

    img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

    shutil.move(img, f"{import_path}{img_new_nm}")

    image = cv2.imread(f'{import_path}{img_new_nm}')

    img_width = image.shape[1]
    img_height = image.shape[0]

    if resize_target == 'W':
        resize_height = int(img_height * (resize_resolution / img_width))
        dim = (resize_resolution, resize_height)

    elif resize_target == 'H':
        resize_width = int(img_width * (resize_resolution / img_height))
        dim = (resize_width, resize_resolution)

    resized_img = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)

    cv2.imwrite(f"{export_path}export_{move_date}.{image_ext}", resized_img)

    move_date = datetime.now().strftime('%y%m%d%H%M%S')

    img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

    shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")

    time.sleep(1)

    return 'Success Image Resize'

def distribute_serveral_png(import_path, import_raw_path, export_path, img):
    image_ext = os.path.splitext(img)[1].split('.')[1]

    if image_ext == 'png':
        move_date = datetime.now().strftime('%y%m%d%H%M%S')

        img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

        shutil.move(img, f"{import_path}{img_new_nm}")

        pc_sizes = [16, 24, 32, 48, 64,
                    72, 80, 96, 128, 256]
        and_sizes = [36, 48, 72, 96, 144]
        ios_sizes = [20, 29, 40, 40, 48,
                     50, 55, 57, 58, 60,
                     60, 72, 76, 80, 87,
                     88, 100, 114, 120, 144,
                     152, 167, 172, 180, 196,
                     1024]

        android_names = ['launcher_ldpi',
                         'launcher_mdpi',
                         'launcher_hdpi',
                         'launcher_xhdpi',
                         'launcher_xxhdpi']
        ios_names = ['icon-20',
                     'icon-29',
                     'icon-20@2x',
                     'icon-40',
                     'icon-24@2x',
                     'icon-50',
                     'icon-27.5@2x',
                     'icon',
                     'icon-29@2x',
                     'icon-20@3x',
                     'icon-60',
                     'icon-72',
                     'icon-76',
                     'icon-40@2x',
                     'icon-83.5@2x',
                     'icon-44@2x',
                     'icon-50@2x',
                     'icon@2x',
                     'icon-60@2x',
                     'icon-72@2x',
                     'icon-76@2x',
                     'icon-29@3x',
                     'icon-86@2x',
                     'icon-60@3x',
                     'icon-98@2x',
                     'icon-1024']

        pc_icon_sizes = [(x, x) for x in pc_sizes]
        and_icon_sizes = [(x, x) for x in and_sizes]
        ios_icon_sizes = [(x, x) for x in ios_sizes]

        image = Image.open(f'{import_path}{img_new_nm}')

        for size in pc_icon_sizes:
            exp_img_new_nm = f"{export_path}export_{move_date}_{str(size[0])}.{image_ext}"
            new_image = image.resize(size)
            new_image.save(exp_img_new_nm)

        new_logo_ico_filename = f"{export_path}export_{move_date}_main.ico"
        new_logo_ico = image.resize(size)
        new_logo_ico.save(new_logo_ico_filename, format="ICO", quality=100)

        for num, label in zip(and_icon_sizes, android_names):
            exp_img_new_nm = f"{export_path}export_{move_date}_{label}.{image_ext}"
            new_image = image.resize(num)
            new_image.save(exp_img_new_nm)

        for num, label in zip(ios_icon_sizes, ios_names):
            exp_img_new_nm = f"{export_path}export_{move_date}_{label}.{image_ext}"
            new_image = image.resize(num)
            new_image.save(exp_img_new_nm)

        time.sleep(1)

        shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")

        return 'Success Image to Icon'

    else:
        return 'Only Convert .PNG'

def bake_one_big_png_to_ico(import_path, import_raw_path, export_path, img):
    """Converts one big PNG into one ICO file.

    args:
        sourcefile (str): Pathname of a PNG file.
        targetfile (str): Pathname of the resulting ICO file.
        sizes (list of int): Requested sizes of the resulting
            icon file, defaults to [16, 32, 48].

    Use this function if you have one big, square PNG file
    and don’t care about fine-tuning individual icon sizes.

    Example::

        sourcefile = "Path/to/high_resolution_logo_512x512.png"
        targetfile = "Path/to/logo.ico"
        sizes = [16, 24, 32, 48, 256]
        bake_one_big_png_to_ico(sourcefile, targetfile, sizes)
    """
    # if sizes is None:
    #     sizes = [16, 24, 32, 48, 256, 512]
    # icon_sizes = [(x, x) for x in sizes]
    # Image.open(sourcefile).save(targetfile, icon_sizes=icon_sizes)

    image_ext = os.path.splitext(img)[1].split('.')[1]

    if image_ext == 'png':
        move_date = datetime.now().strftime('%y%m%d%H%M%S')

        img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

        shutil.move(img, f"{import_path}{img_new_nm}")

        sizes = [16, 24, 32, 48, 64, 72, 80, 96, 128, 256]

        icon_sizes = [(x, x) for x in sizes]

        print(icon_sizes)

        # img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

        Image.open(f'{import_path}{img_new_nm}').save(f"{export_path}export_{move_date}.ico", icon_sizes=icon_sizes)

        # shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")

        time.sleep(1)

        return 'Success Image to Icon'

    else:
        return 'Only Convert .PNG'

def bake_several_pngs_to_ico(sourcefiles, targetfile):
    """Converts several PNG files into one ICO file.

    args:
        sourcefiles (list of str): A list of pathnames of PNG files.
        targetfile (str): Pathname of the resulting ICO file.

    Use this function if you want to have fine-grained control over
    the resulting icon file, providing each possible icon resolution
    individually.

    Example::

        sourcefiles = [
            "Path/to/logo_16x16.png",
            "Path/to/logo_32x32.png",
            "Path/to/logo_48x48.png"
        ]
        targetfile = "Path/to/logo.ico"
        bake_several_pngs_to_ico(sourcefiles, targetfile)
    """

    # Write the global header
    number_of_sources = len(sourcefiles)
    data = bytes((0, 0, 1, 0, number_of_sources, 0))
    offset = 6 + number_of_sources * 16

    # Write the header entries for each individual image
    for sourcefile in sourcefiles:
        img = Image.open(sourcefile)
        data += bytes((img.width, img.height, 0, 0, 1, 0, 32, 0, ))
        bytesize = Path(sourcefile).stat().st_size
        data += bytesize.to_bytes(4, byteorder="little")
        data += offset.to_bytes(4, byteorder="little")
        offset += bytesize

    # Write the individual image data
    for sourcefile in sourcefiles:
        data += Path(sourcefile).read_bytes()

    # Save the icon file
    Path(targetfile).write_bytes(data)

# def image_to_icon(import_path, import_raw_path, export_path, img, resize_resolution):
#     image_ext = os.path.splitext(img)[1].split('.')[1]
#
#     if image_ext == 'png':
#         move_date = datetime.now().strftime('%y%m%d%H%M%S')
#
#         img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
#
#         shutil.move(img, f"{import_path}{img_new_nm}")
#
#         image = cv2.imread(f'{import_path}{img_new_nm}')
#
#         resized_img = cv2.resize(image, (resize_resolution, resize_resolution))
#
#         cv2.imwrite(f"{export_path}export_{image_ext}_{move_date}.ico", resized_img, [cv2.IMWRITE_PNG_COMPRESSION, 9])
#
#         move_date = datetime.now().strftime('%y%m%d%H%M%S')
#
#         img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')
#
#         shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")
#
#         time.sleep(1)
#
#         return 'Success Image to Icon'
#
#     else:
#         return 'Only Convert .PNG'

def image_to_extension(import_path, import_raw_path, export_path, img, extension):
    image_ext = os.path.splitext(img)[1].split('.')[1]

    move_date = datetime.now().strftime('%y%m%d%H%M%S')

    img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

    shutil.move(img, f"{import_path}{img_new_nm}")

    if image_ext == 'gif':
        # Load the GIF into a PIL Image object
        image = Image.open(f"{import_path}{img_new_nm}")

        if extension == 'png':
            # Split the GIF into individual frames
            frames = []
            for frame in ImageSequence.Iterator(image):
                frames.append(frame.copy())

            # Save each frame as an individual image file
            for i, frame in enumerate(frames):
                move_date = datetime.now().strftime('%y%m%d%H%M%S')
                frame.save(f"{export_path}export_{move_date}_{i}.{extension}", format=extension)

        else:
            image = image.convert("RGB")

            image.save(f"{export_path}export_{move_date}.{extension}")

    elif image_ext == 'png':
        # Load the GIF into a PIL Image object
        image = Image.open(f"{import_path}{img_new_nm}")

        background = Image.new('RGB', image.size, (255, 255, 255))

        background.paste(image, mask=image)

        background.save(f"{export_path}export_{move_date}.{extension}")

    else:
        image = cv2.imread(f'{import_path}{img_new_nm}')

        cv2.imwrite(f"{export_path}export_{move_date}.{extension}", image)

    shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")

    time.sleep(1)

    return 'Success Image Extension'

def base64_to_image(import_raw_path, export_path, base64_str, txt_file, file):
    try:
        # ImageFile.LOAD_TRUNCATED_IMAGES = True
        # Remove the "data:image/png;base64," prefix from the string
        base64_string = base64_str.split(',')[1]

        # Determine the length of the input string
        length = len(base64_string)

        # Add padding characters if necessary
        if length % 4 != 0:
            base64_string += '=' * (4 - length % 4)

        # Decode the base64-encoded string into bytes
        image_bytes = base64.b64decode(base64_string)

        image = Image.open(BytesIO(image_bytes))

        image_ext = base64_str.split('/')[1].split(';')[0]

        move_date = datetime.now().strftime('%y%m%d%H%M%S')

        if image_ext == 'gif':
            if image.mode == "RGBA":
                # If the image has an alpha channel, convert it to RGB mode
                image = image.convert("RGB")

            # Get the duration of each frame in the animation
            frame_durations = []
            frames = []
            for frame in ImageSequence.Iterator(image):
                frame_durations.append(frame.info.get("duration", 100))  # default duration is 100ms
                frames.append(frame.copy())

            # Calculate the speed of the animation
            average_duration = sum(frame_durations) / len(frame_durations)
            # if not average_duration == 0.0:
            if average_duration != 0:
                speed = 1 / (average_duration / 1000)  # speed in frames per second
            else:
                speed = 0

            frames[0].save(f"{export_path}export_{move_date}.{image_ext}", format="GIF", save_all=True, append_images=frames[1:], duration=speed, loop=0)

        # Check if the image is a PNG with transparency
        elif image_ext == "png":
            # Convert the image to RGB mode to remove the alpha channel
            image = image.convert("RGBA")

            # Save the image to file
            image.save(f"{export_path}export_{move_date}.{image_ext}")

        else:
            # Convert the bytes to a numpy array
            image_array = np.frombuffer(image_bytes, dtype=np.uint8)

            # Decode the numpy array into an OpenCV image
            image = cv2.imdecode(image_array, cv2.IMREAD_UNCHANGED)

            # Save the image to a file
            cv2.imwrite(f"{export_path}export_{move_date}.{image_ext}", image)

        file.close()

        image_ext = os.path.splitext(txt_file)[1].split('.')[1]

        img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

        shutil.move(txt_file, f"{import_raw_path}{img_new_nm}")

        time.sleep(1)

        return 'Success Image Convert'

    except Exception as e:
        return e

def image_to_base64(import_path, import_raw_path, export_path, img):
    image_ext = os.path.splitext(img)[1].split('.')[1]

    move_date = datetime.now().strftime('%y%m%d%H%M%S')

    img_new_nm = '{}_{}{}'.format('import', move_date, f'.{image_ext}')

    shutil.move(img, f"{import_path}{img_new_nm}")

    if image_ext == 'gif':
        image_pil = Image.open(f"{import_path}{img_new_nm}")

        # Convert the image to a sequence of frames
        frames = []
        try:
            while True:
                frames.append(image_pil.copy())
                image_pil.seek(len(frames))  # skip to the next frame

        except EOFError:
            pass  # end of sequence

        # Encode the frames as GIF bytes
        with BytesIO() as output:
            frames[0].save(output, format='GIF', save_all=True, append_images=frames[1:])
            image_bytes = output.getvalue()

    elif image_ext == 'png':
        image = Image.open(f'{import_path}{img_new_nm}')

        if image.mode != 'RGBA':
            image = image.convert('RGBA')

        buffer = BytesIO()
        image.save(buffer, format='PNG')

        # Encode the image data as a base64 string
        image_bytes = buffer.getvalue()

    else:
        # Load the image file
        image = cv2.imread(f'{import_path}{img_new_nm}')

        # Convert the image to bytes
        success, image_bytes = cv2.imencode(f'.{image_ext}', image)
        image_bytes = image_bytes.tobytes()

    # Encode the bytes as a base64 string
    base64_str = base64.b64encode(image_bytes).decode()

    # Create the base64-encoded image string
    base64_str_dec = f"data:image/{image_ext};base64," + base64_str

    shutil.move(f'{import_path}{img_new_nm}', f"{import_raw_path}{img_new_nm}")

    time.sleep(1)

    # Print the base64-encoded image string
    return base64_str_dec, 'Success Base64 Convert', f"{export_path}export_{move_date}"