# -*- coding: UTF-8 -*-
"""BCD Codec - Binary Coded Decimal content encoding.

This codec:
- en/decodes strings from str to str
- en/decodes strings from bytes to bytes
- decodes file content to str (read)
- encodes file content from str to bytes (write)
"""
from ..__common__ import *


__examples1__ = {
    'enc(bcd|binary-coded-decimal|binary_coded_decimal)': {
        'This is a test!': "\x08A\x04\x10Q\x15\x03!\x05\x11P2\tp2\x11a\x01\x11Q\x16\x030",
   },
    'dec(binary-coded-decimal)': {
        '\xaf':                                                         None,
        '\xff':                                                         None,
        '\x08A\x04\x10Q\x15\x03!\x05\x11P2\tp2\x11a\x01\x11Q\x16\x030': "This is a test!",
   },
}
__examples2__ = {
    'enc(bcd-ext0|bcd_extended_zeros)': {
        'This is a test': "\x00\x08\x04\x01\x00\x04\x01\x00\x05\x01\x01\x05\x00\x03\x02\x01\x00\x05\x01\x01\x05\x00"
                          "\x03\x02\x00\t\x07\x00\x03\x02\x01\x01\x06\x01\x00\x01\x01\x01\x05\x01\x01\x06\x00",
   },
}
__examples3__ = {
    'enc(bcd-ext1|bcd_extended_ones)': {
        'This is a test': "\xf0\xf8\xf4\xf1\xf0\xf4\xf1\xf0\xf5\xf1\xf1\xf5\xf0\xf3\xf2\xf1\xf0\xf5\xf1\xf1\xf5\xf0"
                          "\xf3\xf2\xf0\xf9\xf7\xf0\xf3\xf2\xf1\xf1\xf6\xf1\xf0\xf1\xf1\xf1\xf5\xf1\xf1\xf6\xf0",
   },
}


CODE = {str(i): bin(i)[2:].zfill(4) for i in range(10)}


def bcd_encode(prefix=""):
    def encode(text, errors="strict"):
        r, bits = "", prefix
        for c in text:
            for i in str(ord(c)).zfill(3):
                bits += CODE[i]
                if len(bits) == 8:
                    r += chr(int(bits, 2))
                    bits = prefix
        if len(bits) > 0:
            r += chr(int(bits + "0000", 2))
        return r, len(b(text))
    return encode


def bcd_decode(prefix=""):
    def decode(text, errors="strict"):
        code = {v: k for k, v in CODE.items()}
        r, d = "", ""
        for i, c in enumerate(text):
            bin_c = bin(ord(c))[2:].zfill(8)
            for k in range(len(prefix), 8, 4):
                hb = bin_c[k:k+4]
                try:
                    d += code[hb]
                except KeyError:
                    d += handle_error("bcd", errors, decode=True)(hb, i)
                if len(d) == 3:
                    r += chr(int(d))
                    d = ""
        return r, len(b(text))
    return decode


add("bcd", bcd_encode(), bcd_decode(), pattern=r"^(?:bcd|binary[-_]coded[-_]decimals?)$", examples=__examples1__,
    entropy=lambda e: .45739*e+2.63519, printables_rate=.2)
add("bcd-extended0", bcd_encode("0000"), bcd_decode("0000"), examples=__examples2__, entropy=lambda e: .13584*e+2.07486,
    pattern=r"^(?:bcd|binary[-_]coded[-_]decimals?)[-_]ext(?:ended)?(?:[-_]?0|[-_]zeros?)$")
add("bcd-extended1", bcd_encode("1111"), bcd_decode("1111"), examples=__examples3__, entropy=lambda e: .13584*e+2.07486,
    pattern=r"^(?:bcd|binary[-_]coded[-_]decimals?)[-_]ext(?:ended)?(?:[-_]?1|[-_]ones?)$")

