import logging
import subprocess
import io

ICONS_SUPPORTED_FORMATS = ["png"]


class Image:
    def __init__(self, width: int, height: int, data: bytes):
        self.width = width
        self.height = height
        self.data = data

    def write(self, filename):
        with open(filename, "wb") as file:
            file.write(self.data)

    def data_as_carray(self):
        return (
            "{" + "".join("0x{:02x},".format(img_byte) for img_byte in self.data) + "}"
        )


def is_file_an_icon(filename):
    extension = filename.lower().split(".")[-1]
    return extension in ICONS_SUPPORTED_FORMATS


class ImageTools:
    __pil_unavailable = False
    __hs2_unavailable = False

    @staticmethod
    def is_processing_slow():
        try:
            return False
        except ImportError:
            return True

    def __init__(self):
        self.logger = logging.getLogger()

    def png2xbm(self, file):
        if self.__pil_unavailable:
            return subprocess.check_output(["convert", file, "xbm:-"])

        try:
            from PIL import Image, ImageOps
        except ImportError:
            self.__pil_unavailable = True
            self.logger.info("pillow module is missing, using convert cli util")
            return self.png2xbm(file)

        with Image.open(file) as im:
            with io.BytesIO() as output:
                bw = im.convert("1")
                bw = ImageOps.invert(bw)
                bw.save(output, format="XBM")
                return output.getvalue()

    def xbm2hs(self, data):
        if self.__hs2_unavailable:
            return subprocess.check_output(
                ["heatshrink", "-e", "-w8", "-l4"], input=data
            )

        try:
            import heatshrink2
        except ImportError:
            self.__hs2_unavailable = True
            self.logger.info("heatshrink2 module is missing, using heatshrink cli util")
            return self.xbm2hs(data)

        return heatshrink2.compress(data, window_sz2=8, lookahead_sz2=4)


__tools = ImageTools()


def file2image(file):
    output = __tools.png2xbm(file)
    assert output

    # Extract data from text
    f = io.StringIO(output.decode().strip())
    width = int(f.readline().strip().split(" ")[2])
    height = int(f.readline().strip().split(" ")[2])
    data = f.read().strip().replace("\n", "").replace(" ", "").split("=")[1][:-1]
    data_str = data[1:-1].replace(",", " ").replace("0x", "")

    data_bin = bytearray.fromhex(data_str)

    # Encode icon data with LZSS
    data_encoded_str = __tools.xbm2hs(data_bin)

    assert data_encoded_str

    data_enc = bytearray(data_encoded_str)
    data_enc = bytearray([len(data_enc) & 0xFF, len(data_enc) >> 8]) + data_enc

    # Use encoded data only if its length less than original, including header
    if len(data_enc) + 2 < len(data_bin) + 1:
        data = b"\x01\x00" + data_enc
    else:
        data = b"\x00" + data_bin

    return Image(width, height, data)
