import config, vector, util

# TODO: use this
class BitCode:
    char *algorithm # None if unknown
    int w, h # in bits, used for row-wise rotation
    unsigned char *bits # the actual bits, 8 per byte
    bool has_mask

int def code_compare_bitwise(char const *c1, char const *c2, int n):
    """
    Compare two binary codes of length n bytes. Returns the number of differing
    bits. n should be divisable by 4.
    """
    int d = 0
    for int i = 0; i < n / 4; i++:
        uint32_t x1 = *(uint32_t *)(c1 + i * 4)
        uint32_t x2 = *(uint32_t *)(c2 + i * 4)
        uint32_t diff = x1 ^ x2
        for int j = 0; j < 32; j++:
            d += diff & 1
            diff >>= 1
    return d

int def code_compare_bitwise_masked(unsigned char const *c1,
    unsigned char const *m1,
    unsigned char const *c2, unsigned char const *m2, int n, int *m):
    """
    Compare two binary codes of length n bytes. Returns the number of differing
    bits. n should be divisable by 4.

    m is set to the number of comparable bits, accounting for masked out bits.
    """
    int d = 0
    int really = 0
    for int i = 0; i < n / 4; i++:
        uint32_t x1 = *(uint32_t *)(c1 + i * 4)
        uint32_t x2 = *(uint32_t *)(c2 + i * 4)
        uint32_t a1 = *(uint32_t *)(m1 + i * 4)
        uint32_t a2 = *(uint32_t *)(m2 + i * 4)
        uint32_t a = ~(a1 | a2)
        uint32_t diff = x1 ^ x2
        diff &= a
        for int j = 0; j < 32; j++:
            d += diff & 1
            really += a & 1
            diff >>= 1
            a >>= 1
            
    *m = really
    return d

def bitrotatel(unsigned char *c, int n):
    uint32_t lsb = (*(uint32_t *)(c + n - 4)) & (1 << 31)
    lsb >>= 31
    for int i = 0; i < n / 4; i++:
        uint32_t x = *(uint32_t *)(c + i * 4)
        uint32_t msb = x & (1 << 31)
        x <<= 1
        x |= lsb
        lsb = msb >> 31
        *(uint32_t *)(c + i * 4) = x

int def code_compare_masked_bitoffset(int w, h,
    unsigned char const *c1, unsigned char const *m1,
    unsigned char const *c2, unsigned char const *m2, int bit_offset, int *m):
    # w * h - length of code in bits
    int n = w / 8
    unsigned char rotcode[n * h]
    unsigned char rotmask[n * h]

    unsigned char const *ip = c2
    unsigned char *op = rotcode
    unsigned char const *im = m2
    unsigned char *om = rotmask
    int offset = (bit_offset + 7) >> 3
    # Shift right, in bytes.
    for int y = 0; y < h; y++:
        memcpy(op, ip + offset, n - offset)
        memcpy(op + n - offset, ip, offset)
        memcpy(om, im + offset, n - offset)
        memcpy(om + n - offset, im, offset)
        ip += n
        op += n
        im += n
        om += n

    # Shift back left for the unused positions.
    for int x = 0; x < offset * 8 - bit_offset; x++:
        for int y = 0; y < h; y++:
            bitrotatel(rotcode + n * y, n)
            bitrotatel(rotmask + n * y, n)

    int really = 0
    int boff = bit_offset
    # E.g. for 16, try 33 different positions, and then use minimum distance.
    int mind = w * h
    for int i = -boff; i <= boff; i++:
        int compared
        int d = code_compare_bitwise_masked(c1, m1, rotcode, rotmask, n * h,
            &compared)
        if d < mind:
            mind = d
            really = compared
        for int y = 0; y < h; y++:
            bitrotatel(rotcode + n * y, n)
            bitrotatel(rotmask + n * y, n)
    *m = really
    return mind

#FIXME: deprecated
int def code_compare_masked(int w, h,
    unsigned char const *c1, unsigned char const *m1,
    unsigned char const *c2, unsigned char const *m2, byte_offset, int *m):
    return code_compare_masked_bitoffset(w, h, c1, m2, c2, m2, byte_offset * 8, m)

int def bitcode_compare(BitCode *code1, BitCode *code2, int bit_offset, int *m):
    """
    Given two bitcodes, compare them. The return value is the number of
    differing bits. m is set to the number of total bits compared.
    The bit_offset parameter specifies how many bits to rotate left/right to
    find the minimum bit difference.

    If the codes use known and different algorithms, both the return value and
    m will be set to 0.
    """
    *m = 0

    # Check for same algorithm.
    if code1->algorithm or code2->algorithm:
        if code1->algorithm and code2->algorithm:
            if strcmp(code1->algorithm, code2->algorithm): return 0
        else:
            return 0
    else:
        print("Warning: Unsupported code format!\n")

    if code1->w != code2->w or code1->h != code2->h: return 0

    int w = code1->w
    int h = code1->h

    int n = code_compare_masked_bitoffset(w, h,
        code1->bits, code1->bits + (w * h) / 8,
        code2->bits, code2->bits + (w * h) / 8,
        bit_offset, m)
    return n

def put_uint32(Vector *v, uint32_t n):
    unsigned char data[4]
    data[0] = (n & 0x000000ff) >> 0
    data[1] = (n & 0x0000ff00) >> 8
    data[2] = (n & 0x00ff0000) >> 16
    data[3] = (n & 0xff000000) >> 24
    vector_cat(v, data, 4)

uint32_t def get_uint32(Vector *v, int bytepos):
    unsigned char *data = v->data
    uint32_t n = data[bytepos + 0]
    n += data[bytepos + 1] << 8
    n += data[bytepos + 2] << 16
    n += data[bytepos + 3] << 24
    return n

Vector *def bitcode_encode(BitCode *code):
    Vector *v = vector_new(1)
    vector_cat(v, "CODE", 4)
    put_uint32(v, 0)
    if code->algorithm:
        vector_cat(v, "ALGO", 4)
        put_uint32(v, strlen(code->algorithm))
        vector_cat(v, code->algorithm, strlen(code->algorithm))
    vector_cat(v, "W   ", 4)
    put_uint32(v, 4)
    put_uint32(v, code->w)
    vector_cat(v, "H   ", 4)
    put_uint32(v, 4)
    put_uint32(v, code->h)

    int mask = 1
    if code->has_mask:
        mask = 2
        vector_cat(v, "MASK", 4)
        put_uint32(v, 0)

    vector_cat(v, "BITS", 4)
    put_uint32(v, mask * code->w * code->h / 8) # second half is mask
    vector_cat(v, code->bits, mask * code->w * code->h / 8)
    return v

BitCode *def bitcode_decode(Vector *v):
    BitCode *bc = ALLOCATE0(sizeof *bc)
    if memcmp(v->data, "CODE\0\0\0\0", 8): # assume old code
        # old code means, we simply have one binary BITS chunk
        #int code_bytes = v->count
        #code_bytes *= v->element_size
        # We assume exactly one half is code, and the second half
        # is masking bits.
        #code_bytes /= 2
        # FIXME: hack hack hack!
        # if code_bytes == 512 * 20 / 8:
        #    bc->w = 512
        #    bc->h = 20
        # elif code_bytes == 256 * 64 / 8:
        #    bc->w = 256
        #    bc->h = 64
        # else:
        fprintf(stderr, "Malformed code detected.")
        abort()
        #bc->bits = ALLOCATE(code_bytes * 2)
        #memcpy(bc->bits, v->data, code_bytes * 2)
        return bc
    int pos = 0
    while pos < v->count * v->element_size:
        unsigned char *data = v->data
        char id[4]
        memcpy(id, data + pos, 4)
        uint32_t size = get_uint32(v, pos + 4)
        pos += 8
        if not memcmp(id, "ALGO", 4):
            bc->algorithm = ALLOCATE0(size + 1)
            memcpy(bc->algorithm, data + pos, size)
        elif not memcmp(id, "W   ", 4):
            if size == 4:  bc->w = get_uint32(v, pos)
        elif not memcmp(id, "H   ", 4):
            if size == 4:  bc->h = get_uint32(v, pos)
        elif not memcmp(id, "BITS", 4):
            bc->bits = ALLOCATE(size)
            memcpy(bc->bits, data + pos, size)
        elif not memcmp(id, "MASK", 4):
            bc->has_mask = True
        else:
            pass # ignore any unknown chunks
        pos += size
    return bc

BitCode *def bitcode_from_file(char const *filename):
    Vector *v = readfile(filename)
    if not v: return None
    BitCode *bc = bitcode_decode(v)
    vector_del(v)
    return bc

def bitcode_to_file(BitCode *bc, char const *filename):
    Vector *v = bitcode_encode(bc)
    writefile(filename, v)
    vector_del(v)