#!/usr/bin/env python3

"""
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,and xmlbin{U}.

    {BLUE}Outputs{U} base64, bytes, hex, int, json, 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} 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 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,
    "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()
