#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2014-2018 Hugh Pyle

# "SMALL CAPS" for paper tape,
# based on silkscreen font by kottke http://kottke.org/plus/type/silkscreen/
# NB requires "8-bit-clean" punch data path! e.g. you may need 'stty -onlcr'

import sys
import click


# Escape sequence to initialize (turn off wordwrap, NLCR, delays)
INIT = b"\033[?7l\033_ab\234"
RESET = b"\033[!p"

# emoji to print in binary
HEART_EMOJI = u'\u2764\ufe0f'

# How many nulls before and after the print
LEAD = 5
TAIL = 5

tape_chars = {
    # bits are bottom-to-top, e.g. 0b10000 is 'period'
    ord(' '): [0, 0],
    0x21: [0b10111],                                        # !
    0x22: [0b00011, 0b00000, 0b00011],                      # "
    0x23: [0b01010, 0b11111, 0b01010, 0b11111, 0b01010],    # #
    0x24: [0b10010, 0b11101, 0b10111, 0b01001],             # $ (modified)
    0x25: [0b00011, 0b11011, 0b00100, 0b11011, 0b11000],    # %
    0x26: [0b01010, 0b10101, 0b10110, 0b01000, 0b10001],    # & (modified)
    0x27: [0b00011],                                        # '
    0x28: [0b01110, 0b10001],                               # (
    0x29: [0b10001, 0b01110],                               # )
    0x2a: [0b01010, 0b00100, 0b11111, 0b00100, 0b01010],    # *
    0x2b: [0b00100, 0b00100, 0b11111, 0b00100, 0b00100],    # +
    0x2c: [0b10000, 0b01000],                               # , (modified)
    0x2d: [0b00100, 0b00100, 0b00100],                      # -
    0x2e: [0b10000],                                        # .
    0x2f: [0b11000, 0b00100, 0b00011],                      # /
    ord('0'): [0b01110, 0b10001, 0b10001, 0b01110],
    ord('1'): [0b10001, 0b11111, 0b10000],
    ord('2'): [0b11001, 0b10101, 0b10101, 0b10010],
    ord('3'): [0b10001, 0b10001, 0b10101, 0b10101, 0b01010],
    ord('4'): [0b00111, 0b00100, 0b11111, 0b00100],
    ord('5'): [0b10111, 0b10101, 0b10101, 0b01001],
    ord('6'): [0b01110, 0b10101, 0b10101, 0b01000],
    ord('7'): [0b00001, 0b11001, 0b00101, 0b00011],
    ord('8'): [0b01010, 0b10101, 0b10101, 0b01010],
    ord('9'): [0b00010, 0b10101, 0b10101, 0b01110],
    0x3a: [0b01010],                                        # :
    0x3b: [0b10000, 0b01010],                               # ;
    0x3c: [0b00100, 0b01010, 0b10001],                      # >
    0x3d: [0b01010, 0b01010, 0b01010],                      # =
    0x3e: [0b10001, 0b01010, 0b00100],                      # <
    0x3f: [0b00001, 0b10101, 0b00101, 0b00010],             # ?
    ord('@'): [0b01110, 0b10001, 0b10111, 0b10101, 0b00010],
    ord('A'): [0b11110, 0b00101, 0b00101, 0b11110],
    ord('B'): [0b11111, 0b10101, 0b10101, 0b01110],
    ord('C'): [0b01110, 0b10001, 0b10001, 0b01010],
    ord('D'): [0b11111, 0b10001, 0b10001, 0b01110],
    ord('E'): [0b11111, 0b10101, 0b10101, 0b10101],
    ord('F'): [0b11111, 0b00101, 0b00101, 0b00101],
    ord('G'): [0b01110, 0b10001, 0b10101, 0b01101],
    ord('H'): [0b11111, 0b00100, 0b00100, 0b11111],
    ord('I'): [0b11111],
    ord('J'): [0b01000, 0b10000, 0b10000, 0b01111],
    ord('K'): [0b11111, 0b00100, 0b01010, 0b10001],
    ord('L'): [0b11111, 0b10000, 0b10000],
    ord('M'): [0b11111, 0b00010, 0b00100, 0b00010, 0b11111],
    ord('N'): [0b11111, 0b00010, 0b00100, 0b01000, 0b11111],
    ord('O'): [0b01110, 0b10001, 0b10001, 0b01110],
    ord('P'): [0b11111, 0b00101, 0b00101, 0b00010],
    ord('Q'): [0b01110, 0b10001, 0b01001, 0b10110],
    ord('R'): [0b11111, 0b00101, 0b01101, 0b10010],
    ord('S'): [0b10010, 0b10101, 0b10101, 0b01001],
    ord('T'): [0b00001, 0b11111, 0b00001],
    ord('U'): [0b01111, 0b10000, 0b10000, 0b01111],
    ord('V'): [0b00011, 0b01100, 0b10000, 0b01100, 0b00011],
    ord('W'): [0b01111, 0b10000, 0b01110, 0b10000, 0b01111],
    ord('X'): [0b10001, 0b01010, 0b00100, 0b01010, 0b10001],
    ord('Y'): [0b00001, 0b00010, 0b11100, 0b00010, 0b00001],
    ord('Z'): [0b11001, 0b10101, 0b10011],
    0x5b: [0b11111, 0b10001],                               # [
    0x5c: [0b00011, 0b00100, 0b11000],                      # \
    0x5d: [0b10001, 0b11111],                               # ]
    0x5e: [0b00010, 0b00001, 0b00010],                      # ^ (modified)
    0x5f: [0b10000, 0b10000, 0b10000],                      # _
    0x60: [0b00001, 0b00010],                               # `
    ord('a'): [0b11110, 0b00101, 0b00101, 0b11110],
    ord('b'): [0b11111, 0b10101, 0b10101, 0b01110],
    ord('c'): [0b01110, 0b10001, 0b10001, 0b01010],
    ord('d'): [0b11111, 0b10001, 0b10001, 0b01110],
    ord('e'): [0b11111, 0b10101, 0b10101, 0b10101],
    ord('f'): [0b11111, 0b00101, 0b00101, 0b00101],
    ord('g'): [0b01110, 0b10001, 0b10101, 0b01101],
    ord('h'): [0b11111, 0b00100, 0b00100, 0b11111],
    ord('i'): [0b11111],
    ord('j'): [0b01000, 0b10000, 0b10000, 0b01111],
    ord('k'): [0b11111, 0b00100, 0b01010, 0b10001],
    ord('l'): [0b11111, 0b10000, 0b10000],
    ord('m'): [0b11111, 0b00010, 0b00100, 0b00010, 0b11111],
    ord('n'): [0b11111, 0b00010, 0b00100, 0b01000, 0b11111],
    ord('o'): [0b01110, 0b10001, 0b10001, 0b01110],
    ord('p'): [0b11111, 0b00101, 0b00101, 0b00010],
    ord('q'): [0b01110, 0b10001, 0b01001, 0b10110],
    ord('r'): [0b11111, 0b00101, 0b01101, 0b10010],
    ord('s'): [0b10010, 0b10101, 0b10101, 0b01001],
    ord('t'): [0b00001, 0b11111, 0b00001],
    ord('u'): [0b01111, 0b10000, 0b10000, 0b01111],
    ord('v'): [0b00011, 0b01100, 0b10000, 0b01100, 0b00011],
    ord('w'): [0b01111, 0b10000, 0b01110, 0b10000, 0b01111],
    ord('x'): [0b10001, 0b01010, 0b00100, 0b01010, 0b10001],
    ord('y'): [0b00001, 0b00010, 0b11100, 0b00010, 0b00001],
    ord('z'): [0b11001, 0b10101, 0b10011],
    0x7b: [0b00100, 0b11011, 0b10001],                          # {
    0x7c: [0b11111],                                            # | (modified)
    0x7d: [0b10001, 0b11011, 0b00100],                          # }
    0x7e: [0b00010, 0b00001, 0b00010, 0b00001],                 # ~
    0x7f: [0, 0]
}

PRINT_CHAR = "o"


def write_bdf():
    # Write a BDF font
    print("""STARTFONT 2.1
COMMENT https://github.com/hughpyle/ASR33/
FONT -tape-smallcaps--6-60-75-75-C-60-ISO10646-1
SIZE 6 75 75
FONTBOUNDINGBOX 5 6 0 -1
STARTPROPERTIES 11
    FOUNDRY "tape"
    PIXEL_SIZE 6
    POINT_SIZE 60
    RESOLUTION_X 75
    RESOLUTION_Y 75
    CHARSET_REGISTRY "ISO10646"
    CHARSET_ENCODING "1"
    FONT_ASCENT 5
    FONT_DESCENT 1
    DEFAULT_CHAR 0
    COPYRIGHT "Copyright (c) 2018 by Hugh Pyle.  Distributed under the MIT license."
ENDPROPERTIES""")
    print("CHARS {}".format(len(tape_chars)))

    def name(char):
        return "U+{:04x}".format(char)

    for char in tape_chars.keys():
        print("STARTCHAR {}".format(name(char)))
        print("ENCODING {}".format(char))
        dw = len(tape_chars[char]) + 1
        print("SWIDTH {} 0".format(dw*125))   # how far the origin moves after display
        print("DWIDTH {} 0".format(dw))     # how far the origin moves after display
        print("BBX 5 6 0 -1")   # (width height offsetX offsetY) of the lit pixels
        # The bitmap is: (height) lines of hex data, right-padded with zeros.
        # note: this is a horizontal read-out from the top down,
        # but our `tape_chars` characters are vertically oriented (columns) with LSB=bottom.
        # e.g. for "A", { 0x41: [0b11110, 0b00101, 0b00101, 0b11110] } reads as 10010000,
        print("BITMAP")
        bit = 1
        n = 1
        while bit < 64:
            t = 0
            for col in (tape_chars[char] + [0, 0, 0, 0, 0, 0, 0, 0])[:8]:
                t = (t << 1) | bool(col & bit)
            print("{:02x}".format(t))
            bit = bit * 2
            n = n + 1
        print("ENDCHAR")
    print("ENDFONT")


def print_all():
    bit = 1
    while bit < 64:
        t = ""
        for (k, v) in tape_chars.items():
            for col in v:
                if col & bit:
                    c = PRINT_CHAR
                else:
                    c = " "
                pass
                t = t + c
            t = t + "  "
        print(t)
        bit = bit * 2


def print_string(s):
    bit = 1
    while bit < 64:
        t = ""
        for char in s:
            v = tape_chars.get(ord(char), 0)
            for col in v:
                if col & bit:
                    c = PRINT_CHAR
                else:
                    c = " "
                pass
                t = t + c
            t = t + "  "
        if bit == 4:
            print("." * len(t))
        print(t)
        bit = bit * 2


def print_binary(s):
    sys.stdout.buffer.write(INIT)
    # sys.stdout.write(b'\0' * LEAD)
    # sys.stdout.write(u"{}".format(HEART_EMOJI))
    sys.stdout.buffer.write(b'\0' * LEAD)
    for char in s:
        v = tape_chars.get(ord(char), 0)
        for col in v:
            t = 0
            bit = 1
            while bit < 32:
                t = t | (col & bit) << 3
                bit = bit * 2
            sys.stdout.buffer.write(bytearray([t]))
        sys.stdout.buffer.write(b'\0')
    sys.stdout.buffer.write(b'\0' * TAIL)
    sys.stdout.buffer.write(RESET)


@click.command(context_settings={"ignore_unknown_options": True})
@click.argument('words', nargs=-1, type=click.UNPROCESSED)
@click.option("--bdf", is_flag=True)
def main(bdf, words):
    label = " ".join(words)
    if bdf:
        write_bdf()
    else:
        print_binary(label)
    pass


main()
