def getbit(b, pos):
    return (b >> pos) & 1


def decode_dx(b0, b1, b2):
    x = 0
    x += getbit(b2, 2) * (+81)
    x += getbit(b2, 3) * (-81)
    x += getbit(b1, 2) * (+27)
    x += getbit(b1, 3) * (-27)
    x += getbit(b0, 2) * (+9)
    x += getbit(b0, 3) * (-9)
    x += getbit(b1, 0) * (+3)
    x += getbit(b1, 1) * (-3)
    x += getbit(b0, 0) * (+1)
    x += getbit(b0, 1) * (-1)
    return x


def decode_dy(b0, b1, b2):
    y = 0
    y += getbit(b2, 5) * (+81)
    y += getbit(b2, 4) * (-81)
    y += getbit(b1, 5) * (+27)
    y += getbit(b1, 4) * (-27)
    y += getbit(b0, 5) * (+9)
    y += getbit(b0, 4) * (-9)
    y += getbit(b1, 7) * (+3)
    y += getbit(b1, 6) * (-3)
    y += getbit(b0, 7) * (+1)
    y += getbit(b0, 6) * (-1)
    return -y


def process_header_info(out, prefix, value):
    if prefix == "LA":
        out.metadata("name", value)
    elif prefix == "AU":
        out.metadata("author", value)
    elif prefix == "CP":
        out.metadata("copyright", value)
    elif prefix == "TC":
        values = [x.strip() for x in value.split(',')]
        out.add_thread({
            "hex": values[0],
            "description": value[1],
            "catalog": value[2]
        })
    else:
        out.metadata(prefix, value)


def dst_read_header(f, out):
    header = f.read(512)
    header_string = header.decode('utf8')
    for line in [x.strip() for x in header_string.split('\r')]:
        if len(line) > 3:
            process_header_info(out, line[0:2].strip(), line[3:].strip())


def dst_read_stitches(f, out):
    sequin_mode = False
    while True:
        byte = bytearray(f.read(3))
        if len(byte) != 3:
            break
        dx = decode_dx(byte[0], byte[1], byte[2])
        dy = decode_dy(byte[0], byte[1], byte[2])
        if byte[2] & 0b11110011 == 0b11110011:
            out.stop(dx, dy)
        elif byte[2] & 0b11000011 == 0b11000011:
            out.color_change(dx, dy)
        elif byte[2] & 0b01000011 == 0b01000011:
            out.sequin_mode(dx, dy)
            sequin_mode = not sequin_mode
        elif byte[2] & 0b10000011 == 0b10000011:
            if sequin_mode:
                out.sequin_eject(dx, dy)
            else:
                out.move(dx, dy)
        else:
            out.stitch(dx, dy)


def read(f, out, settings=None):
    dst_read_header(f, out)
    dst_read_stitches(f, out)
