import PIL.Image

import sys


def get_bitstream(message):
    for c in message:
        bin_string = "{:08b}".format(c)
        for bit in bin_string:
            yield int(bit)


def parse_bitstream(stream):
    for bits in by_chunk(stream, 8, fillvalue=0):
        binstr = "".join([str(b) for b in bits])
        c = int(binstr, 2)
        if chr(c) == "\n":
            break
        yield c


def by_chunk(iterable, size, fillvalue=0):
    "Collect data into fixed-length chunks or blocks"
    chunk = []
    for x in iterable:
        chunk.append(x)
        if len(chunk) >= size:
            yield chunk
            chunk = []


def set_bit(old_byte, new_bit):
    b = list(bin(old_byte))
    b[-1] = str(new_bit)
    return int("".join(b), 2)


def main_encrypt():
    message = (sys.argv[2] + "\n").encode()
    base_name = sys.argv[3]
    carrier_name = base_name.replace(".png", ".carrier.png")
    base_image = PIL.Image.open(base_name)
    width, height = base_image.size
    new_image = PIL.Image.new("RGB", (width, height), "white")
    bitstream = get_bitstream(message)
    for row in range(height):
        for col in range(width):
            r, g, b, = base_image.getpixel((col, row))
            value = None
            try:
                value = next(bitstream)
            except StopIteration:
                pass
            if value is not None:
                r = set_bit(r, value)
            new_image.putpixel((col, row), (r, g, b))

    new_image.save(carrier_name, "png")
    print("carrier written to", carrier_name)


def yield_bits(carrier_image):
    width, height = carrier_image.size
    for row in range(height):
        for col in range(width):
            r, g, b, = carrier_image.getpixel((col, row))
            last_bit = int(bin(r)[-1])
            yield last_bit


def main_decrypt():
    carrier_name = sys.argv[2]
    carrier_image = PIL.Image.open(carrier_name)
    encoded = yield_bits(carrier_image)
    decoded = bytes(parse_bitstream(encoded))
    print(decoded.decode())


def main():
    if sys.argv[1] == "encrypt":
        main_encrypt()
    elif sys.argv[1] == "decrypt":
        main_decrypt()
    else:
        sys.exit("choose from encrypt, decrypt")
