import struct
import re
import io


def get_size(data: bytes):
    """
    Returns size of given image fragment, if possible.
    Based on image_size script by Paulo Scardine: https://github.com/scardine/image_size
    """
    size = len(data)
    if size >= 10 and data[:6] in (b'GIF87a', b'GIF89a'):
        # GIFs
        w, h = struct.unpack("<HH", data[6:10])
        return int(w), int(h)

    if size >= 24 and data.startswith(b'\211PNG\r\n\032\n') and data[12:16] == b'IHDR':
        # PNGs
        w, h = struct.unpack(">LL", data[16:24])
        return int(w), int(h)

    if size >= 16 and data.startswith(b'\211PNG\r\n\032\n'):
        # older PNGs
        w, h = struct.unpack(">LL", data[8:16])
        return int(w), int(h)

    if size >= 30 and data[:4] == b'RIFF' and data[8:12] == b'WEBP':
        # WebP
        webp_type = data[12:16]
        if webp_type == b'VP8 ': # Lossy WebP (old)
            w, h = struct.unpack("<HH", data[26:30])
        elif webp_type == b'VP8L': # Lossless WebP
            bits = struct.unpack("<I", data[21:25])[0]
            w = int(bits & 0x3FFF) + 1
            h = int((bits >> 14) & 0x3FFF) + 1
        elif webp_type == b'VP8X': # Extended WebP
            w = int((data[26] << 16) | (data[25] << 8) | data[24]) + 1
            h = int((data[29] << 16) | (data[28] << 8) | data[27]) + 1
        else:
            w = 0
            h = 0
        return w, h

    if b'<svg' in data:
        # SVG
        start = data.index(b'<svg')
        end = data.index(b'>', start)
        svg = str(data[start:end + 1], 'utf8')
        w = re.search(r'width=["\'](\d+)', svg)
        h = re.search(r'height=["\'](\d+)', svg)
        return int(w.group(1) if w else 0), int(h.group(1) if h else 0)

    if size >= 2 and data.startswith(b'\377\330'):
        # JPEG
        with io.BytesIO(data) as inp:
            inp.seek(0)
            inp.read(2)
            b = inp.read(1)
            while (b and ord(b) != 0xDA):
                while ord(b) != 0xFF:
                    b = inp.read(1)
                while ord(b) == 0xFF:
                    b = inp.read(1)
                if 0xC0 <= ord(b) <= 0xC3:
                    inp.read(3)
                    h, w = struct.unpack(">HH", inp.read(4))
                    return int(w), int(h)
                inp.read(int(struct.unpack(">H", inp.read(2))[0]) - 2)
                b = inp.read(1)

        return 0, 0