#!/usr/bin/env python3
# hypothesis library is convenient tool for generation of various complex
# data structures. That is tedious work to do in strongly typed Go. So
# let's generate KEKS structures in Python, feed them to Go's
# implementation and supply them with additional data describing what is
# exactly expected to be decoded.
#
# Simple text-based protocol is made for that task. You run data
# generator and feed its output to go/cmd/textdump-tester's stdin. Text
# protocol is a flow of ASCII lines, containing space-separated fields:
# * KEKS HEX(complex-data) -- provides the data for decoding and verifying
# * NIL -- NIL is expected
# * FALSE -- FALSE is expected
# * TRUE -- TRUE is expected
# * HEXLET xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -- HEXLET is expected
# * UTC xxxx-xx-xxTxx:xx:xxZ -- TAI64-encoded UTC is expected
# * UTC xxxx-xx-xxTxx:xx:xx.xxxxxxZ -- TAI64N-encoded UTC is expected
# * TAI64NA HEX(...) -- external TAI64NA-encoded time
# * MAGIC HEX(...) -- Magic is expected
# * BIN HEX(...) -- BIN is expected
# * STR HEX(...) -- STR is expected
# * INT DEC(...) -- ±INT is expected
# * BLOB DEC(chunkLen) HEX(content) -- BLOB is expected
# * LIST DEC(len) -- LEN is expected with exact number of elements.
#   Their similar text descriptions follow
# * MAP DEC(len) -- MAP is expected with exact number of elements.
#   It is followed by pairs of STR(key) and element's value
# * EOC -- nothing more expected, end of contents

from datetime import datetime

from keks import Blob
from keks import Magic
from keks import Raw
from keks import TagTAI64NA
from keks import Hexlet


def textdump(v):
    if v is None:
        print("NIL")
    elif v is False:
        print("FALSE")
    elif v is True:
        print("TRUE")
    elif isinstance(v, Hexlet):
        print("HEXLET " + bytes(v).hex())
    elif isinstance(v, float):
        raise NotImplementedError("no FLOAT* support")
    elif isinstance(v, datetime):
        print("UTC %sZ" % v.isoformat())
    elif isinstance(v, bytes):
        print("BIN " + v.hex())
    elif isinstance(v, str):
        print("STR " + v.encode("utf-8").hex())
    elif isinstance(v, int):
        print("INT %d" % v)
    elif isinstance(v, Blob):
        print("BLOB %d %s" % (v.l, v.v.hex()))
    elif isinstance(v, (list, tuple)):
        print("LIST %d" % len(v))
        for i in v:
            textdump(i)
    elif isinstance(v, set):
        textdump({i: None for i in v})
    elif isinstance(v, dict):
        print("MAP %d" % len(v))
        for k, v in v.items():
            textdump(k)
            textdump(v)
    elif isinstance(v, Raw):
        v = bytes(v)
        if v[0] != TagTAI64NA:
            raise NotImplementedError("unsupported Raw type")
        print("TAI64NA " + v[1:].hex())
    elif isinstance(v, Magic):
        print("MAGIC " + v.v.hex())
    else:
        raise NotImplementedError("unsupported type")


if __name__ == "__main__":
    from hypothesis.strategies import dictionaries
    from keks import dumps
    from tests.strategies import everything_st
    from tests.strategies import mapkey_st
    st = dictionaries(mapkey_st, everything_st, max_size=4)
    while True:
        ex = st.example()
        print("KEKS", dumps(ex).hex())
        textdump(ex)
        print("EOC")
