from .EmbThread import EmbThread
from .ReadHelper import read_int_8

CsdSubMaskSize = 479
CsdXorMaskSize = 501

_subMask = [0] * CsdSubMaskSize
_xorMask = [0] * CsdXorMaskSize

_decryptArray = [
    0x43, 0x6E, 0x72, 0x7A, 0x76, 0x6C, 0x61, 0x6F, 0x7C, 0x29, 0x5D, 0x62, 0x60, 0x6E, 0x61, 0x62, 0x20,
    0x41, 0x66, 0x6A, 0x3A, 0x35, 0x5A, 0x63, 0x7C, 0x37, 0x3A, 0x2A, 0x25, 0x24, 0x2A, 0x33, 0x00, 0x10,
    0x14, 0x03, 0x72, 0x4C, 0x48, 0x42, 0x08, 0x7A, 0x5E, 0x0B, 0x6F, 0x45, 0x47, 0x5F, 0x40, 0x54, 0x5C,
    0x57, 0x55, 0x59, 0x53, 0x3A, 0x32, 0x6F, 0x53, 0x54, 0x50, 0x5C, 0x4A, 0x56, 0x2F, 0x2F, 0x62, 0x2C,
    0x22, 0x65, 0x25, 0x28, 0x38, 0x30, 0x38, 0x22, 0x2B, 0x25, 0x3A, 0x6F, 0x27, 0x38, 0x3E, 0x3F, 0x74,
    0x37, 0x33, 0x77, 0x2E, 0x30, 0x3D, 0x34, 0x2E, 0x32, 0x2B, 0x2C, 0x0C, 0x18, 0x42, 0x13, 0x16, 0x0A,
    0x15, 0x02, 0x0B, 0x1C, 0x1E, 0x0E, 0x08, 0x60, 0x64, 0x0D, 0x09, 0x51, 0x25, 0x1A, 0x18, 0x16, 0x19,
    0x1A, 0x58, 0x10, 0x14, 0x5B, 0x08, 0x15, 0x1B, 0x5F, 0xD5, 0xD2, 0xAE, 0xA3, 0xC1, 0xF0, 0xF4, 0xE8,
    0xF8, 0xEC, 0xA6, 0xAB, 0xCD, 0xF8, 0xFD, 0xFB, 0xE2, 0xF0, 0xFE, 0xFA, 0xF5, 0xB5, 0xF7, 0xF9, 0xFC,
    0xB9, 0xF5, 0xEF, 0xF4, 0xF8, 0xEC, 0xBF, 0xC3, 0xCE, 0xD7, 0xCD, 0xD0, 0xD7, 0xCF, 0xC2, 0xDB, 0xA4,
    0xA0, 0xB0, 0xAF, 0xBE, 0x98, 0xE2, 0xC2, 0x91, 0xE5, 0xDC, 0xDA, 0xD2, 0x96, 0xC4, 0x98, 0xF8, 0xC9,
    0xD2, 0xDD, 0xD3, 0x9E, 0xDE, 0xAE, 0xA5, 0xE2, 0x8C, 0xB6, 0xAC, 0xA3, 0xA9, 0xBC, 0xA8, 0xA6, 0xEB,
    0x8B, 0xBF, 0xA1, 0xAC, 0xB5, 0xA3, 0xBB, 0xB6, 0xA7, 0xD8, 0xDC, 0x9A, 0xAA, 0xF9, 0x82, 0xFB, 0x9D,
    0xB9, 0xAB, 0xB3, 0x94, 0xC1, 0xA0, 0x8C, 0x8B, 0x8E, 0x95, 0x8F, 0x87, 0x99, 0xE7, 0xE1, 0xA3, 0x83,
    0x8B, 0xCF, 0xA3, 0x85, 0x9D, 0x83, 0xD4, 0xB7, 0x83, 0x84, 0x91, 0x97, 0x9F, 0x88, 0x8F, 0xDD, 0xAD,
    0x90]


def BuildDecryptionTable(seed):
    mul1 = 0x41C64E6D
    add1 = 0x3039
    for i in range(0, CsdSubMaskSize):
        seed *= mul1
        seed += add1
        seed &= 0xFFFFFFFF
        _subMask[i] = (seed >> 16) & 0xFF
    for i in range(0, CsdXorMaskSize):
        seed *= mul1
        seed += add1
        seed &= 0xFFFFFFFF
        _xorMask[i] = (seed >> 16) & 0xFF


def DecodeCsdByte(fileOffset, val, type):
    if type != 0:
        fileOffsetHigh = fileOffset & 0xFFFFFF00
        fileOffsetLow = fileOffset & 0xFF
        newOffset = fileOffsetLow
        fileOffsetLow = fileOffsetHigh
        final = fileOffsetLow % 0x300
        if final != 0x100 and final != 0x200:
            newOffset = _decryptArray[newOffset] | fileOffsetHigh
        elif final != 0x100 and final == 0x200:
            if newOffset == 0:
                fileOffsetHigh = fileOffsetHigh - 0x100
            newOffset = _decryptArray[newOffset] | fileOffsetHigh
        elif newOffset != 1 and newOffset != 0:
            newOffset = _decryptArray[newOffset] | fileOffsetHigh
        else:
            fileOffsetHigh = fileOffsetHigh - 0x100
            newOffset = _decryptArray[newOffset] | fileOffsetHigh
    else:
        newOffset = fileOffset
    result = ((val ^ _xorMask[newOffset % CsdXorMaskSize]) - _subMask[newOffset % CsdSubMaskSize]) & 0xFF
    print("%d %02x" % (fileOffset, result))
    return result


def read_csd_stitches(f, out):
    type = 0
    identifier = [0] * 8
    colorChange = -1
    colorOrder = [0] * 14
    for i in range(0, 8):
        identifier[i] = read_int_8(f)

    print(identifier)
    if identifier[0] != 0x7C and identifier[2] != 0xC3:
        type = 1
    if type == 0:
        BuildDecryptionTable(0xC)
    else:
        BuildDecryptionTable(identifier[0])
    f.seek(8, 0)  # Seek Set
    for i in range(0, 16):
        thread = EmbThread()
        r = DecodeCsdByte(f.tell(), read_int_8(f), type)
        g = DecodeCsdByte(f.tell(), read_int_8(f), type)
        b = DecodeCsdByte(f.tell(), read_int_8(f), type)
        thread.set_color(r, g, b)
        thread.catalogNumber = ""
        thread.description = ""
        out.add_thread(thread)
    unknown1 = DecodeCsdByte(f.tell(), read_int_8(f), type)
    unknown2 = DecodeCsdByte(f.tell(), read_int_8(f), type)

    for i in range(0, 14):
        colorOrder[i] = DecodeCsdByte(f.tell(), read_int_8(f), type)

    while True:
        b0 = DecodeCsdByte(f.tell(), read_int_8(f), type)
        b1 = DecodeCsdByte(f.tell(), read_int_8(f), type)
        b2 = DecodeCsdByte(f.tell(), read_int_8(f), type)

        dx = int(b2)
        dy = int(b1)

        if b0 == 0xF8 or b0 == 0x87 or b0 == 0x91:
            break

        negativeX = ((b0 & 0x20) > 0)
        negativeY = ((b0 & 0x40) > 0)

        if negativeX:
            dx = -dx
        if not negativeY:
            dy = -dy

        b0 &= 0xFF ^ 0xE0

        if (b0 & 0x1F) == 0:
            out.stitch(dx, dy)
        elif (b0 & 0x0C) > 0:
            out.color_change()
            if colorChange >= 14:
                break  # Invalid color change.
            colorChange += 1
        elif (b0 & 0x1F) > 0:
            out.trim()
            out.move(dx, dy)
        else:
            out.stitch(dx, dy)


def read(f, out, settings=None):
    read_csd_stitches(f, out)
