# -*- coding: utf-8 -*-
import io
from PIL import Image
import icnsutil
import os

from ddans.native.system import NSystem
from ddans.native.hook import NHook
from ddans.utils import Utils

SUPPORT_TYPE = ["png", "jpg"]
ICON_SIZES = [32, 64, 128, 256, 512, 1024]
IMAGE_SIZES = [16, 24, 32, 48, 64, 128, 256, 512, 1024]
HEADER_SIZE = 8

ICNS_SIZES = [128, 256, 512, 32, 64, 256, 512, 1024]
ICNS_SIZE_TYPES = ['ic07', 'ic08', 'ic09',
                   'ic11', 'ic12', 'ic13', 'ic14', 'ic10']


def toInt(s):
    b = s.encode('ascii')
    return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]


class Icon:
    name = ""
    ext = ""
    file = ""
    dirname = ""
    is_valid = False
    image = None

    def __init__(self, file):
        self.file = file
        self.dirname = os.path.dirname(file)
        name, ext = NSystem.split_name_ext(file)
        self.name = name
        self.ext = ext
        if self.ext in SUPPORT_TYPE and os.path.exists(file):
            self.image = Image.open(self.file)
            self.is_valid = True

    def convert(self,
                format: str = None,
                width: int = 0,
                height: int = 0,
                name: str = "",
                dest: str = ""):
        if not self.is_valid or self.image is None:
            return None
        if format is None or len(format) <= 0:
            return None

        _format = format.lower()
        _name = name if len(name) > 0 else self.name
        if _format == self.ext and _name == self.name:
            return None

        _io = self.to_bytes(format=format, width=width, height=height)
        if _io is None:
            return None

        dirname = dest if NHook.isvalid_str(dest) else self.dirname
        outName = NSystem.get_full_path(dirname, f"{_name}.{_format}")
        return Utils.catch(
            lambda: NSystem.write_file(_io.getvalue(), outName))

    def to_bytes(self, format: str = None, width: int = 0, height: int = 0):
        if not self.is_valid or self.image is None:
            return None
        if format is None or len(format) <= 0:
            return None

        _format = format.lower()
        _io = io.BytesIO()

        w = width if width > 0 else self.image.width
        h = height if height > 0 else self.image.height
        im = self.image.resize(size=(w, h))

        if _format == "icns":
            ret = Utils.catch(
                lambda: im.save(_io, format=self.fix_format(_format)))
        elif _format == "ico" and (w >= 512 or h >= 512):
            return None
        else:
            ret = Utils.catch(
                lambda: im.save(_io, format=self.fix_format(_format)))
        if ret is not None:
            return _io
        else:
            return None

    def fix_format(self, fmt: str):
        format = fmt
        if fmt in ["jp2", "jpx", "j2k"]:
            format = "jpeg2000"
        return format

    def to_ico(self, width=0, height=0, name="", filename=""):
        if not self.is_valid:
            return None
        dest = ""
        if NHook.isvalid_str(filename):
            dest = NSystem.get_dir(filename)
            name, _ = NSystem.split_name_ext(filename)
        return self.convert("ico", width, height, name, dest)

    def to_icns(self, name="", filename=""):
        if not self.is_valid:
            return None
        dest = ""
        if NHook.isvalid_str(filename):
            dest = NSystem.get_dir(filename)
            name = NSystem.split_name_ext(filename)
        result = self.convert("icns", name=name, dest=dest)
        self.to_jp2_icns(name, suffix="512")
        return result

    def to_jp2_icns(self, name: str = "", size=512, suffix="", dest=""):
        if not self.is_valid:
            return

        _size = size if size > 0 else 512
        _idx = ICNS_SIZES.index(_size)
        _idx = _idx if _idx >= 0 else ICON_SIZES.index(512)
        if _idx < 0:
            _size = 512
            _idx = ICON_SIZES.index(_size)

        _tytes_io = self.to_bytes("jp2", width=_size, height=_size)
        if _tytes_io is None:
            return
        img = icnsutil.IcnsFile()
        img.add_media(ICNS_SIZE_TYPES[_idx], data=_tytes_io.getvalue())

        _name = name if len(name) > 0 else self.name
        _name_ext = "%s%s%s.icns" % (
            _name,
            "_" if len(suffix) > 0 else "",
            suffix)

        dirname = dest if NHook.isvalid_str(dest) else self.dirname
        outName = NSystem.get_full_path(dirname, _name_ext)
        NSystem.ensure_dir(outName)
        Utils.catch(lambda: img.write(outName))

    def to_icons(self, dest=""):
        if not self.is_valid:
            return

        dirname = dest if NHook.isvalid_str(dest) else self.dirname
        dir = os.path.join(dirname, "%s_icons" % self.name)

        # png
        for size in IMAGE_SIZES:
            _name = "%s_%sx%s" % (self.name, size, size)
            self.convert("png",
                         width=size, height=size, name=_name, dest=dir)

        # ico
        for size in IMAGE_SIZES:
            _name = os.path.join(
                "ico",
                "%s_%sx%s.ico" % (self.name, size, size))
            filename = NSystem.get_full_path(dir, _name)
            self.to_ico(width=size, height=size, filename=filename)
        # icns
        self.convert("icns", dest=dir)
        Utils.catch(
            lambda: self.to_jp2_icns(suffix="512", dest=dir))

    def icns_extract(self, dest=""):
        if not os.path.exists(self.file):
            return None
        format = self.ext.lower()
        if format != "icns":
            return None
        icns = icnsutil.IcnsFile(self.file)
        if icns is None:
            return None

        dirname = dest if NHook.isvalid_str(dest) else self.dirname
        outDir = NSystem.get_full_path(dirname, f"{self.name}_icns")
        NSystem.ensure_dir(outDir)
        icns.export(outDir, allowed_ext='jp2', recursive=True)
        icns.export(outDir, allowed_ext='png', recursive=True,
                    convert_png=True)
        return outDir
