import io
import logging
import time
import uuid
from PIL import Image

logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s', level=logging.INFO)


def scaleImage(img, scale):
    if img is None:
        return
    return img.resize((img.width * scale, img.height * scale))


def readCUR(f, width=-1.0, height=-1.0):
    frameImage = Image.open(f, formats=['cur', 'ico'])
    if (frameImage.mode == 'P'):
        palette = list(frameImage.palette.getdata()[1])
        for i in range(4, len(palette), 4):
            if sum(palette[i:i + 3]) == 0:
                break
            palette[i + 3] = 255
        frameImage.putpalette(palette, 'BGRA')
        frameImage = frameImage.convert('RGBA')
    if (width, height) == (-1.0, -1.0):
        return frameImage, (float(frameImage.width), float(frameImage.height))
    if -1 in (width, height):
        width = height / frameImage.height * frameImage.width if width == -1 else width
        height = width / frameImage.width * frameImage.height if height == -1 else height
    return frameImage.resize((int(width), int(height))), (width, height)


def analyzeANI(f):
    if f.read(4) != b'RIFF':
        return {'code': -1, 'msg': 'File is not a ANI File!'}
    logging.debug('文件头检查完成！')
    fileSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
    # if os.path.getsize(filePath) != fileSize:
    #     return {'code':-2,'msg':'File is damaged!'}
    logging.debug('文件长度检查完成！')
    if f.read(4) != b'ACON':
        return {'code': -1, 'msg': 'File is not a ANI File!'}
    logging.debug('魔数检查完成！')
    frameRate = (1/60)*1000
    while (True):
        chunkName = f.read(4)
        if chunkName == b'LIST':
            break
        chunkSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
        if chunkName.lower() == b'rate':
            logging.debug('发现自定义速率！')
            frameRate = frameRate * int.from_bytes(f.read(4), byteorder='little', signed=False)
            logging.warning('发现自定义速率！由于GIF限制，将取第一帧与第二帧的速率作为整体速率！')
            f.read(chunkSize - 4)
        else:
            logging.debug('发现自定义Chunk！')
            f.read(chunkSize)
    listChunkSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
    if f.read(4) != b'fram':
        return {'code': -3, 'msg': 'File not a ANI File!(No Frames)'}
    logging.debug('frame头检查完成！')
    frameList = []
    nowSize = 4
    while (nowSize < listChunkSize):
        if f.read(4) != b'icon':
            return {'code': -4, 'msg': 'File not a ANI File!(Other Kind Frames)'}
        nowSize += 4
        subChunkSize = int.from_bytes(f.read(4), byteorder='little', signed=False)
        nowSize += 4
        frameList.append(f.read(subChunkSize))
        nowSize += subChunkSize
    return {'code': 0, 'msg': frameList, 'frameRate': frameRate}


def main():
    from config import capeConfig
    uniqueId = (f'local.{capeConfig['Author'] or 'unknown'}'
                f'.{capeConfig['CapeName'] or 'untitled'}'
                f'.{time.time()}.{str(uuid.uuid4()).upper()}')
    capeData = {
        'Author': capeConfig['Author'],
        'CapeName': capeConfig['CapeName'],
        'CapeVersion': capeConfig['CapeVersion'],
        'Cloud': False,
        'Cursors': {},
        'HiDPI': capeConfig['HiDPI'],
        'Identifier': capeConfig['Identifier'] or uniqueId,
        'MinimumVersion': 2.0,
        'Version': 2.0
    }
    for cursorType, cursorConfig in capeConfig['Cursors'].items():
        cursorSetting = {
            'FrameCount': 1,
            'FrameDuration': cursorConfig['FrameDuration'],
            'HotSpotX': cursorConfig['HotSpot'][0] + 2.0,
            'HotSpotY': cursorConfig['HotSpot'][1] + 2.0,
            'Representations': []
        }
        hidpiRatio = 2 if capeConfig['HiDPI'] else 1
        width, height = cursorConfig.get('Size', (-1.0, -1.0))

        with open(cursorConfig['Path'], 'rb') as f:
            spriteSheet = None
            if (res := analyzeANI(f))['code'] == 0:
                logging.info('ANI文件分析完成，帧提取完成！')
                cursorSetting['FrameCount'] = len(res['msg'])
                for frameIndex in range(len(res['msg'])):
                    b = io.BytesIO(res['msg'][frameIndex])
                    frame, (width, height) = readCUR(b, width, height)
                    position = (2, 2 + int((height + 4) * frameIndex))
                    if frameIndex == 0:
                        spriteSheet = Image.new('RGBA', (int(width + 4), int(height + 4) * len(res['msg'])))
                    spriteSheet.paste(frame, position)
            else:
                logging.info('尝试作为CUR读入')
                frame, (width, height) = readCUR(f, width, height)
                spriteSheet = Image.new('RGBA', (int(width + 4), int(height + 4)))
                spriteSheet.paste(frame, (2, 2))
            logging.info(f'目标尺寸：{width}x{height}@{hidpiRatio}x')
            cursorSetting['PointsHigh'], cursorSetting['PointsWide'] = width + 4, height + 4
            for scale in (1, 2) if capeConfig['HiDPI'] else (1,):
                byteBuffer = io.BytesIO()
                scaleImage(spriteSheet, scale).save(byteBuffer, format='tiff', compression='tiff_lzw')
                cursorSetting['Representations'].append(byteBuffer.getvalue())
            capeData['Cursors'][cursorType] = cursorSetting

    from plistlib import dump, FMT_XML
    with open(f'{capeData['Identifier']}.cape', 'wb') as f:
        dump(capeData, f, fmt=FMT_XML, sort_keys=True, skipkeys=False)


if __name__ == '__main__':
    main()
