import config, image, histogram, canny, hough, tiff, pupil, iris, util

def black_out_not_circle(Image *image, int x, y, r):
    int w = image->w
    int h = image->h

    int sum = 0, n = 0

    for int j = 0; j < h; j++:
        for int i = 0; i < w ; i++:
            int dx = i - x
            int dy = j - y
            int d = sqrt(dx * dx + dy * dy)
            if d <= r:
                int c = image->data.b[i + j * w]
                sum += c
                n++
    sum /= n

    for int j = 0; j < h; j++:
        for int i = 0; i < w ; i++:
            int dx = i - x
            int dy = j - y
            int d = sqrt(dx * dx + dy * dy)
            if d > r:
                image->data.b[i + j * w] = sum

def fix_crop_border(Image *image, int l, t, r, b):
    """
    Color outside areas of image with average color of inside area.
    """
    if l > 0: l = 0
    if t > 0: t = 0
    if r > 0: r = 0
    if b > 0: b = 0
    int w = image->w
    int h = image->h
    l = -l
    t = -t
    r = w + r - 1
    b = h + b - 1

    int sum = 0, n = 0
    for int j = t; j <= b; j++:
        for int i = l; i <= r ; i++:
            int c = image->data.b[i + j * w]
            sum += c
            n++
    sum /= n
    for int j = 0; j < h; j++:
        for int i = 0; i < w ; i++:
            if j < t or j > b or i < l or i > r:
                image->data.b[i + j * w] = sum

def mask_crop_border(Image *image, int l, t, r, b):
    """
    Fill in the mask for parts of the iris being clipped off.
    """
    if l > 0: l = 0
    if t > 0: t = 0
    if r > 0: r = 0
    if b > 0: b = 0
    int w = image->w
    int h = image->h
    l = -l
    t = -t
    r = w + r - 1
    b = h + b - 1

    for int j = 0; j < h; j++:
        for int i = 0; i < w ; i++:
            if j < t or j > b or i < l or i > r:
                image->data.b[i + j * w] = 255

int def scan_edge(Image *image, int x, y, dx, dy, n):
    """
    Starting at the given location, created a mask from edges.
    """
    int w = image->w
    int i = x

    int firstval = -1
    for int t = 0; t < n; t++:
        int j = y
        while 1:
            int c = image->data.b[i + j * w]
            if c > 0:
                firstval = j
                goto got_firstval
            j += dy
            if j == -1 or j == image->h: break
        i += dx

    label got_firstval
    if firstval == -1: return 0

    i = x
    int bval = -1
    for int t = 0; t < n; t++:
        int j = y
        int pc = 0
        while 1:
            int c = image->data.b[i + j * w]
            if j == bval:
                pc = 128
            if c > 0:
                pc = 255
                firstval = j
                if bval == -1 or j * dy < bval * dy:
                    bval = j

            image->data.b[i + j * w] = pc
            j += dy
            if j == -1 or j == image->h:
                # We reached the border without masking anything?

                if pc == 0:
                    for j = firstval; j != -1 and j != image->h; j += dy:
                        image->data.b[i + j * w] = 192
                break
        #bval += dy
        i += dx
    return 0

Image *def mask_lids(Image const *orig_image, char const *what,
    int px, py, pr, ix, iy, ir):
    """
    px, py, pr: Pupil position and radius
    ix, iy, ir: Iris position and radius

    This creates a black&white image, where black pixels are considered iris
    texture and all other pixels are considered belonging to lids.

    Right now, this is a somewhat over-zealous algorithm, often cutting away
    more than needed, especially in the presence of eyelashes.
    """
    int ox = ix - ir
    int oy = iy - ir
    Image *image = image_copy_crop(orig_image, ox, oy, ir * 2 + 1,
        ir * 2 + 1)
    int w = image->w
    int h = image->h

    fix_crop_border(image, ox, oy, orig_image->w - w - ox, orig_image->h - h - oy)

    ix -= ox
    iy -= oy
    px -= ox
    py -= oy

    int min_r = pr * 1.5

    # Blackout the pupil
    black_out_circle(image, px, py, pr, min_r)

    # Blackout anything outside the iris
    #black_out_not_circle(image, ix, iy, ir)

    if hasword(what, "lids"):
        char name[MAX_PATH]
        get_image_name(orig_image, "lids", name)
        save_tiff_luminance(name, image)

    Image *edges = canny(image, 3, 15, 0, 1, "")
    #black_out_not_circle(edges, ix, iy, ir - 4) # cover up for blackened circle
    threshold_below(edges, 6, 0)
    threshold_above(edges, 7, 255)

    remove_streaks(edges, "", 30, 30, 0, 1, 1, 1, 1, 100, 1, False)

    if hasword(what, "lidscanny"):
        char name[MAX_PATH]
        get_image_name(orig_image, "lidscanny", name)
        save_tiff_luminance(name, edges)

    scan_edge(edges, 0, iy, 1, -1, ir + 1)
    scan_edge(edges, edges->w - 1, iy, -1, -1, ir)
    scan_edge(edges, 0, iy, 1, 1, ir + 1)
    scan_edge(edges, edges->w - 1, iy, -1, 1, ir)

    mask_crop_border(edges, ox, oy, orig_image->w - w - ox, orig_image->h - h - oy)

    if hasword(what, "lidsmask"):
        char name[MAX_PATH]
        get_image_name(orig_image, "lidsmask", name)
        save_tiff_luminance(name, edges)

    return edges

Image *def generate_mask(Image *eye, int px, py, pr, ix, iy, ir, tw, th):
    Image *mask = image_new_undef_b(tw, th)
    mask->name = strdup(eye->name)

    double angle = 0, da = 2 * pi / tw
    for int i = 0; i < tw; i++:
        double c = cos(angle)
        double s = -sin(angle)
        double sx = px + 0.5 + c * pr
        double sy = py + 0.5 + s * pr
        double ex = ix + 0.5 + c * ir
        double ey = iy + 0.5 + s * ir
        for int j = 0; j < th; j++:
            int x = sx + (ex - sx) * j / th
            int y = sy + (ey - sy) * j / th
            mask->data.b[i + j * tw] = eye->data.b[x + y * eye->w]
        angle += da

    return mask

static macro addbit {
    if (x) code[codepos] |= 1 << bitpos;
    bitpos++;
    if (bitpos == 32) {
        codepos++;
        bitpos = 0;
    }
}

uint32_t *def generate_mask_code(Image *mask, int thick, int count):
    """
    Bits are added to the code starting to the left and with the lowest bit in
    a byte.
    We duplicate each row, as the actual bitcode has two rows for each signal.
    The count parameters specifies how many stripes to use, thick is how thick
    one stripe is in pixels. We consider a bit mask as soon as a single input
    pixel was even slightly masked (the mask is 255 levels grayscale, from
    0 = unmasked to 255 = completely masked).
    """
    uint32_t *code = ALLOCATE0(2 * count * mask->w / thick)
    int codepos = 0
    int bitpos = 0
    for int j = 0; j < count * thick; j += thick:
        for int l = 0; l < 2; l++:
            for int i = 0; i < mask->w; i++:
                int x = 0
                for int k = 0; k < thick; k++:
                    B c = mask->data.b[i + mask->rowbytes * (j + k)]
                    x += c
                addbit
    assert(codepos == count * 2 * 512 / 32)
    return code

Image *def generate_empty_mask(int w, h):
    Image *mask = image_new_undef_b(w, h)
    for int i = 0; i < mask->w; i++:
        for int j = 0; j < mask->h; j++:
            mask->data.b[j * mask->rowbytes + i] = 0
    return mask