#!/usr/bin/env python3

"""
scte35 command line SCTE35 decoder.

"""


import select
import sys
from threefive.gums import cli as gumscli
from threefive.hls import cli as hlscli
from threefive.sixfix import cli as sixcli
from threefive.superkabuki import cli as skcli
from threefive.iframes import IFramer
from threefive.new_reader import reader
from threefive import Cue, Stream, print2, blue, version, red
from threefive.stuff import ERR

# import cProfile
# from sideways import cli as sidecli

write2 = False

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} threefive 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,and 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} threefive https://example.com/video.ts  base64
   {U}{BLUE} base64 {U}  {U}{BLUE} hex    {NORM} threefive '/DAWAAAAAAAAAP/wBQb+AKmKxwAACzuu2Q==' hex
   {U}{BLUE} xmlbin  {U} {BLUE} int    {NORM} threefive   < xmlbin.xml int
   {U}{BLUE} xml    {U}  {BLUE} json   {NORM} threefive   < xml.xml 
   {U}{BLUE} mpegts {U}  {BLUE} json   {NORM} threefive video.ts
   {U}{BLUE} json{U}    {BLUE}  xml    {NORM} threefive  < json.json  xml

    {B}  hls        {U}{NORM}{BLUE} SCTE-35 HLS decode{U}

    threefive hls help   |   threefive hls https://example.com/master.m3u8{U}

    {B} iframes     {U}{BLUE} Show MPEGTS iframes{NORM}

    threefive iframes video.ts

    {B} inject      {U}{NORM}{BLUE} SuperKabuki SCTE-35 MPEGTS Packet Injection Engine{U}

    threefive inject help

    {B} mcast       {U}{NORM}{BLUE} Multicast sender(server){U}

    threefive mcast help  |   threefive mcast video.ts

    {B}  packets    {U}{NORM}{BLUE} Print raw SCTE-35 packets{NORM}

    threefive packets udp://@235.35.3.5:3535

    {B}  proxy      {U}{NORM}{BLUE} Parse a MPEGTS stream and copy it to stdout{NORM}

    threefive proxy video.ts

    {B}  pts        {U}{NORM}{BLUE} Print PTS from MPEGTS video{NORM}

    threefive pts video.ts

    {B}  sidecar    {U}{NORM}{BLUE} Create a SCTE-35 sidecar file{NORM}

    threefive sidecar video.ts

    {B}  sixfix     {U}{NORM}{BLUE} Fix SCTE-35 data mangled by ffmpeg{NORM}

    threefive sixfix video.ts

    {B}  show       {U}{NORM}{BLUE} Probe MPEGTS video{NORM}

    threefive show video.ts

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

    threefive version

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

    threefive 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 threefive 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 inject_chk(this):
    """
    inject_chk use the superkabuki
                        packet injection engine.
    """
    skcli()


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,
    "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 xmlbin_out(cue):
    """
    xml_out prints cue as xml+binary
    """
    print2(cue.xmlbin())


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


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
    # if not this.strip().startswith('<'):
    try:
        # mpegts streams handled here.
        strm = Stream(this)
        strm.decode(func=funk())  # funk() works here
    except:
        cue = Cue(this)  # This is the
        if cue:
            funk()(cue)  #   funk 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,
    "inject": skcli,
    "sixfix": sixcli,
    "sk": 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 sys.argv 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()
