/* This file was generated by scramble.py. */
#include "code.h"
#include "scramble.h"
/* TODO: use this */
int 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 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;
}
void 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 code_compare_masked_bitoffset(int w, int 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 code_compare_masked(int w, int h, unsigned char const *c1, unsigned char const *m1, unsigned char const *c2, unsigned char const *m2, unsigned byte_offset, int *m)
{
    return code_compare_masked_bitoffset(w, h, c1, m2, c2, m2, byte_offset * 8, m);
}
int 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 || code2->algorithm)
    {
        if (code1->algorithm && 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 || 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;
}
void 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 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 * 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);
    /* second half is mask */
    put_uint32(v, mask * code->w * code->h / 8);
    vector_cat(v, code->bits, mask * code->w * code->h / 8);
    return v;
}
BitCode * bitcode_decode(Vector *v)
{
    BitCode *bc = ALLOCATE0(sizeof *bc);
    /* assume old code */
    if (memcmp(v->data, "CODE\0\0\0\0", 8))
    /* 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 (! memcmp(id, "ALGO", 4))
        {
            bc->algorithm = ALLOCATE0(size + 1);
            memcpy(bc->algorithm, data + pos, size);
        }
        else if (! memcmp(id, "W   ", 4))
        {
            if (size == 4)
            {
                bc->w = get_uint32(v, pos);
            }
        }
        else if (! memcmp(id, "H   ", 4))
        {
            if (size == 4)
            {
                bc->h = get_uint32(v, pos);
            }
        }
        else if (! memcmp(id, "BITS", 4))
        {
            bc->bits = ALLOCATE(size);
            memcpy(bc->bits, data + pos, size);
        }
        else if (! memcmp(id, "MASK", 4))
        {
            bc->has_mask = True;
        }
        else
        /* ignore any unknown chunks */
        {
            /* pass */
        }
        pos += size;
    }
    return bc;
}
BitCode * bitcode_from_file(char const *filename)
{
    Vector *v = readfile(filename);
    if (! v)
    {
        return None;
    }
    BitCode *bc = bitcode_decode(v);
    vector_del(v);
    return bc;
}
void bitcode_to_file(BitCode *bc, char const *filename)
{
    Vector *v = bitcode_encode(bc);
    writefile(filename, v);
    vector_del(v);
}
/* This file was generated by scramble.py. */
