#!python

"""
scte35 command line SCTE35 decoder.

"""


import select
import sys
from threefive3.gums import cli as gumscli
from threefive3.superkabuki import cli as skcli
from threefive3.iframes import IFramer
from threefive3.new_reader import reader
from threefive3 import Cue, Stream, print2, blue, version, red
from threefive3.hls import cli as hlscli
from threefive3.sixfix import sixfix
import cProfile
# from sideways import cli as sidecli

REV = "\033[7;1m"
NORM = "\033[27m\033[0m"
NORM = "\033[0m"
BLUE = "\033[36;1;1m"
G = "\033[32;1;1m"
B = "\033[7;1m"
U = "\033[m"


class SupaStream(Stream):
    """
    SupaStream is subclass of Stream used
    to print raw SCTE-35 packets.
    """
    def _parse_scte35(self, pkt, pid):
        print2(pkt)
        print2("")
        super()._parse_scte35(pkt, pid)

        
def mk_sidecar(cue):
    """
    mk_sidecar generates a sidecar file with the SCTE-35 Cues
    """
    pts = 0.0
    with open("sidecar.txt", "a") as sidecar:
        cue.show()
        if cue.packet_data.pts:
            pts = cue.packet_data.pts
        data = f"{pts},{cue.encode()}\n"
        sidecar.write(data)


HELP = f"""
{BLUE} threefive3 cli tool                                {U}

    {B} Default      {U} {BLUE}The default action is to read a input and write a SCTE-35 output.{U}

    {BLUE}Inputs {U} mpegts, base64, hex, json, xml, and xmlbin{U}.

    {BLUE}Outputs{U} base64, bytes, hex, int, json, xml, and xmlbin.{U}

    {BLUE}scte35 can read from {U} strings, files, stdin, http(s), multicast, and Udp.

    Input {U}    Output {U}
   {U}{BLUE} mpegts {U}  {U}{BLUE} base64 {NORM} threefive3 https://example.com/video.ts  base64
   {U}{BLUE} xml    {U}  {U}{BLUE} bytes  {NORM} threefive3 bytes  < xml.xml
   {U}{BLUE} base64 {U}  {U}{BLUE} hex    {NORM} threefive3 '/DAWAAAAAAAAAP/wBQb+AKmKxwAACzuu2Q==' hex
   {U}{BLUE} xmlbin {U}  {U}{BLUE} int    {NORM} threefive3  int  < xml.xml
   {U}{BLUE} mpegts {U}  {U}{BLUE} json   {NORM} threefive3 video.ts
   {U}{BLUE} json   {U}  {U}{BLUE} xml    {NORM} threefive3  < json.json  xml
   {U}{BLUE} hex    {U}  {U}{BLUE} xmlbin {NORM} threefive3 0xfc301600000000000000fff00506fe00a98ac700000b3baed9  xmlbin

    {B}  hls        {U}{NORM}{BLUE} SCTE-35 hls decode help:{U}  threefive3 hls help

    threefive3 hls https://example.com/master.m3u8{U}

    {B} iframes     {U}{BLUE} show MPEGTS iframes{NORM} threefive3 iframes video.ts

    {B}  packets    {U}{NORM}{BLUE} Print raw SCTE-35 packets:{NORM} threefive3 packets udp://@235.35.3.5:3535

    {B}  proxy      {U}{NORM}{BLUE} Parse a stream and copy it to stdout:{NORM} threefive3 proxy video.ts

    {B}  pts        {U}{NORM}{BLUE} Print PTS from mpegts video:{NORM} threefive3 pts video.ts

    {B}  sidecar    {U}{NORM}{BLUE} Create a sidecar file:{NORM} threefive3 sidecar video.ts

    {B}  sixfix     {U}{NORM}{BLUE} Fix SCTE-35 data mangled by ffmpeg:{NORM} threefive3 sixfix video.ts

    {B}  show       {U}{NORM}{BLUE} Probe mpegts video:{NORM} threefive3 show video.ts

    {B}  version    {U}{NORM}{BLUE} Show version:{NORM} threefive3 version

    {B}  help       {U}{NORM} {BLUE}Help:{NORM} threefive3 help

"""


def mk_args(keys):
    """
    mk_args generates a list of args for inputs
    if no args are present,read from sys.stdin.buffer
    """
    args = [arg for arg in sys.argv[1:] if arg not in keys]
    return args


# print_map functions


def hls():
    sys.argv.remove("hls")
    ##    if "encode" in sys.argv:
    ##        sidecli()
    ##    else:
    hlscli()


def print_help():
    """
    print_help checks sys.argv for the word help
    and displays the help if found
    """
    print2(HELP)
    sys.exit()


def print_version():
    """
    print_version print the threefive3 version
    """
    print2(version)


print_map = {
    "hls": hls,
    "help": print_help,
    "version": print_version,
}


def chk_print_map():
    """
    chk_print_map checks for print_map.keys() in sys.argv
    """
    for k, v in print_map.items():
        if k in sys.argv:
            v()
            sys.exit()


# functions for mpegts_map


def iframe_chk(this):
    """
    iframe_chk show iframes pts
    for a mpegts video.
    """
    iframer = IFramer()
    iframer.do(this)


def packet_chk(this):
    """
    packet_chk checks for the packet keyword
    and displays SCTE-35 packets if present.
    """
    supa = SupaStream(this)
    supa.decode()


def proxy_chk(this):
    """
    proxy_chk checks for the proxy keyword
    and proxies the stream to stdout if present.
    proxy_chk also writes pts,cue pairs to sidecar.txt
    """
    strm = Stream(this)
    strm.proxy(func=mk_sidecar)


def pts_chk(this):
    """
    pts_chk is used to display PTS.
    """
    strm = Stream(this)
    strm.show_pts()


def show_chk(this):
    """
    show_chk checks for the show keyword
    and displays the streams if present.
    """
    strm = Stream(this)
    strm.show()


def sidecar_chk(this):
    """
    sidecar_chk checks for the sidecar keyword and
    generates a sidecar file if present.
    """
    strm = Stream(this)
    strm.decode(func=mk_sidecar)


mpegts_map = {
    "packets": packet_chk,
    "proxy": proxy_chk,
    "pts": pts_chk,
    "show": show_chk,
    "sidecar": sidecar_chk,
    "sixfix": sixfix,
    "iframes": iframe_chk,
}


def chk_mpegts_map():
    """
    chk_mpegts_map check sys.argv for mpegts_map keys
    """
    m_keys = list(mpegts_map.keys())
    args = mk_args(m_keys)
    for key in m_keys:
        if key in sys.argv:
            for arg in args:
                print2(arg)
                mpegts_map[key](arg)
            sys.exit()


# functions for funk_map


def base64_out(cue):
    """
    print SCTE-35 from mpegts as base64
    """
    print2(cue.base64())


def bytes_out(cue):
    """
    print SCTE-35 from mpegts as base64
    """
    print2(cue.bites)


def hex_out(cue):
    """
    print SCTE-35 from mpegts as hex
    """
    print2(cue.hex())


def int_out(cue):
    """
    print SCTE-35 from mpegts as int
    """
    print2(cue.int())


def json_out(cue):
    """
    print SCTE-35 from mpegts as json
    """
    cue.show()


def xml_out(cue):
    """
    xml_out prints cue as xml
    """
    print2(cue.xml())


def xmlbin_out(cue):
    """
    xml_out prints cue as xml
    """
    print2(cue.xmlbin())


funk_map = {
    "base64": base64_out,
    "bytes": bytes_out,
    "hex": hex_out,
    "int": int_out,
    "json": json_out,
    "xml": xml_out,
    "xmlbin": xmlbin_out,
}


def funk():
    """
    return a func
    if a key in out_map
    is also in sys.argv
    """
    func = json_out
    for k, v in funk_map.items():
        if k in sys.argv:
            func = v
    return func


def to_funk(this):
    """
    to_funk prints a cue in a variety of formats.
    """
    cue = None
    if this in ["", b""]:
        return
    try:
        # mpegts streams handled here.
        strm = Stream(this)
        strm.decode(func=funk())  # funk() works here
    except:  # try to load json or xml
        cue = Cue()
        try:
            cue.bites = cue._mk_bits(this)
            cue.decode()
        except:
            print2("Bad Data:\n")
            sys.exit()
    if cue:
        funk()(cue)  #   func works here too.


def chk_stdin(args):
    """
    chk_stdin autodetects input from stdin.

    """
    readable, _, _ = select.select([sys.stdin], [], [], 0)
    if sys.stdin in readable:  # check stdin for data
        one = reader(sys.stdin.buffer).read(1)
        if one not in [b"G"]:  # is this a packet?
            two = reader(sys.stdin.buffer).read()
            args.append((one + two).decode())  # no rewind on stdin
        else:
            two = reader(sys.stdin.buffer).read(187)
            strm = Stream(sys.stdin.buffer)
            strm._parse(one + two)  # dont drop first packet
            strm.decode(func=funk())
            sys.exit() # I keep forgetting why I do
    return args


def chk_funk_map():
    """
    chk_func_map checks for func_map.keys() in sys.argv
    """
    funk_keys = list(funk_map.keys())
    args = mk_args(funk_keys)
    args = chk_stdin(args)
    if args:
        _ = [to_funk(arg) for arg in args]  # multiple file input
    sys.exit()

# cli map

cli_map = {"mcast": gumscli, "superkabuki": skcli}


def dashdashhelp():
    """
    dashdashhelp swaps out "help" with "--help"
    for keys in cli_map. Pure bureaucracy.
    I still can't believe I can just edit sys.argv at will,
    it kind of makes sense, since I wrote it in the first place,
    but it just feels so wrong.
    """
    if "help" in sys.argv:
        idx = sys.argv.index("help")
        sys.argv[idx]= "--help"    


def chk_cli_map():
    """
    chk_cli_map check if a
    cli_map key is present in sys.argv,
    pass control to the cli function.
    """
    for k, v in cli_map.items():
        if k in sys.argv:
            dashdashhelp()
            sys.argv[0] = f"{sys.argv[0]} {k}"
            sys.argv.remove(k)
            v()
            sys.exit()


def go():
    chk_cli_map()
    chk_print_map()
    if len(sys.argv) < 2:
        sys.argv.append("json")
    chk_mpegts_map()
    chk_funk_map()


if __name__ == "__main__":
    go()


def go():
    if len(sys.argv) < 2:
        sys.argv.append("json")
    chk_print_map()
    chk_mpegts_map()
    chk_funk_map()


if __name__ == "__main__":
    go()
