"""
gnssntripclient_cli.py

CLI wrapper for GNSSNTRIPClient class.

Created on 24 Jul 2024

:author: semuadmin (Steve Smith)
:copyright: semuadmin © 2022
:license: BSD 3-Clause
"""

from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
from datetime import datetime, timezone
from os import getenv
from queue import Queue
from threading import Thread
from time import sleep

from serial import Serial

from pygnssutils._version import __version__ as VERSION
from pygnssutils.globals import (
    CLIAPP,
    ENV_MQTT_KEY,
    ENV_NTRIP_PASSWORD,
    ENV_NTRIP_USER,
    EPILOG,
    MAXCONNECTION,
    NTRIP1,
    NTRIP2,
    OUTPUT_FILE,
    OUTPUT_NONE,
    OUTPUT_SERIAL,
    OUTPUT_SOCKET,
    OUTPUT_SOCKET_TLS,
    PYGNSSUTILS_CRT,
    PYGNSSUTILS_CRTPATH,
)
from pygnssutils.gnssntripclient import (
    GGAFIXED,
    INACTIVITY_TIMEOUT,
    MAX_RETRY,
    RETRY_INTERVAL,
    RTCM,
    SPARTN,
    WAITTIME,
    GNSSNTRIPClient,
)
from pygnssutils.helpers import set_common_args
from pygnssutils.socket_server import runserver


def runclient(**kwargs):
    """
    Start NTRIP client with CLI parameters.
    """

    with GNSSNTRIPClient(CLIAPP, **kwargs) as gnc:
        gnc.run(**kwargs)
        # run until stop event or user presses CTRL-C
        while not gnc.stopevent.is_set():
            sleep(WAITTIME)
        sleep(0.5)


def main():
    """
    CLI Entry point.
    """
    # pylint: disable=raise-missing-from, too-many-statements

    ap = ArgumentParser(
        epilog=EPILOG,
        formatter_class=ArgumentDefaultsHelpFormatter,
    )
    ap.add_argument("-V", "--version", action="version", version="%(prog)s " + VERSION)
    ap.add_argument(
        "-S", "--server", required=False, help="NTRIP server (caster) URL", default=""
    )
    ap.add_argument(
        "-P", "--port", required=False, help="NTRIP port", type=int, default=2101
    )
    ap.add_argument(
        "-M",
        "--mountpoint",
        required=False,
        help="NTRIP mountpoint (leave blank to get sourcetable)",
        default="",
    )
    ap.add_argument(
        "-H",
        "--https",
        required=False,
        help=("HTTPS (TLS) connection? 0 = HTTP, 1 = HTTPS"),
        type=int,
        choices=[0, 1],
        default=0,
    )
    ap.add_argument(
        "--selfsign",
        required=False,
        help=("Allow self-signed TLS certificate? - set tlscrtpath"),
        type=int,
        choices=[0, 1],
        default=0,
    )
    ap.add_argument(
        "--tlscrtpath",
        required=False,
        help=(
            ""
            "Fully qualified path to self-signed TLS CRT (certificate) file "
            "(required if --selfsign=1)"
        ),
        type=str,
        default=getenv(PYGNSSUTILS_CRTPATH, PYGNSSUTILS_CRT),
    )
    ap.add_argument(
        "-I",
        "--ipprot",
        required=False,
        help="IP protocol",
        choices=["IPv4", "IPv6"],
        default="IPv4",
    )
    ap.add_argument(
        "--flowinfo", required=False, help="Flow info for IPv6", type=int, default=0
    )
    ap.add_argument(
        "--scopeid", required=False, help="Scope ID for IPv6", type=int, default=0
    )
    ap.add_argument(
        "--retries",
        required=False,
        help="Maximum failed connection retries",
        type=int,
        default=MAX_RETRY,
    )
    ap.add_argument(
        "--retryinterval",
        required=False,
        help="Retry backoff (interval = retryinterval * (2**retries))",
        type=int,
        default=RETRY_INTERVAL,
    )
    ap.add_argument(
        "--timeout",
        required=False,
        help="Inactivity timeout in seconds",
        type=int,
        default=INACTIVITY_TIMEOUT,
    )
    ap.add_argument(
        "--ntripversion",
        required=False,
        dest="version",
        help="NTRIP protocol version",
        choices=(NTRIP1, NTRIP2),
        default=NTRIP2,
    )
    ap.add_argument(
        "--datatype",
        required=False,
        help="Data type (RTCM or SPARTN)",
        choices=[RTCM, RTCM.upper(), SPARTN, SPARTN.upper()],
        default=RTCM,
    )
    ap.add_argument(
        "--ntripuser",
        required=False,
        help="NTRIP authentication user",
        default=getenv(ENV_NTRIP_USER, "anon"),
    )
    ap.add_argument(
        "--ntrippassword",
        required=False,
        help="NTRIP authentication password",
        default=getenv(ENV_NTRIP_PASSWORD, "password"),
    )
    ap.add_argument(
        "--ggainterval",
        required=False,
        help="GGA NMEA sentence transmission interval (-1 = None)",
        type=int,
        default=-1,
    )
    ap.add_argument(
        "--reflat", required=False, help="reference latitude", type=float, default=0.0
    )
    ap.add_argument(
        "--reflon", required=False, help="reference longitude", type=float, default=0.0
    )
    ap.add_argument(
        "--refalt", required=False, help="reference altitude", type=float, default=0.0
    )
    ap.add_argument(
        "--refsep", required=False, help="reference separation", type=float, default=0.0
    )
    ap.add_argument(
        "--spartndecode",
        required=False,
        help="Decode SPARTN payload?",
        type=int,
        choices=[0, 1],
        default=0,
    )
    ap.add_argument(
        "--spartnkey",
        required=False,
        help="Hexadecimal decryption key for encrypted SPARTN payloads",
        default=getenv(ENV_MQTT_KEY, default=None),
    )
    ap.add_argument(
        "--spartnbasedate",
        required=False,
        help="Decryption basedate for encrypted SPARTN payloads",
        default=datetime.now(timezone.utc),
    )
    ap.add_argument(
        "--clioutput",
        required=False,
        help=(
            f"CLI output type {OUTPUT_NONE} = none, "
            f"{OUTPUT_FILE} = binary file, "
            f"{OUTPUT_SERIAL} = serial port, "
            f"{OUTPUT_SOCKET} = TCP socket server, "
            f"{OUTPUT_SOCKET_TLS} = TCP socket server with TLS"
        ),
        type=int,
        choices=[
            OUTPUT_NONE,
            OUTPUT_FILE,
            OUTPUT_SERIAL,
            OUTPUT_SOCKET,
            OUTPUT_SOCKET_TLS,
        ],
        default=OUTPUT_NONE,
    )
    ap.add_argument(
        "--output",
        required=False,
        help=(
            f"Output medium as formatted string. "
            f"If clioutput = {OUTPUT_FILE}, format = file name (e.g. '/home/myuser/rtcm.log'); "
            f"If clioutput = {OUTPUT_SERIAL}, format = port@baudrate (e.g. '/dev/tty.ACM0@38400'); "
            f"If clioutput = {OUTPUT_SOCKET} or {OUTPUT_SOCKET_TLS}, "
            "format = hostip:port (e.g. '0.0.0.0:50010'). "
            "NB: gnssntripclient will have exclusive use of any serial or server port."
        ),
        default=None,
    )
    kwargs = set_common_args("gnssntripclient", ap)

    kwargs["ggamode"] = GGAFIXED  # only fixed reference mode is available via CLI
    cliout = int(kwargs.pop("clioutput", OUTPUT_NONE))
    try:
        if cliout == OUTPUT_FILE:
            filename = kwargs["output"]
            with open(filename, "wb") as output:
                kwargs["output"] = output
                runclient(**kwargs)
        elif cliout == OUTPUT_SERIAL:
            port, baud = kwargs["output"].split("@")
            with Serial(port, int(baud), timeout=3) as output:
                kwargs["output"] = output
                runclient(**kwargs)
        elif cliout in (OUTPUT_SOCKET, OUTPUT_SOCKET_TLS):
            host, port = kwargs["output"].split(":")
            tls = cliout == OUTPUT_SOCKET_TLS
            kwargs["output"] = Queue()
            # socket server runs as background thread, piping
            # output from ntrip client via a message queue
            Thread(
                target=runserver,
                args=(host, int(port), kwargs["output"], 0, MAXCONNECTION, tls, NTRIP2),
                daemon=True,
            ).start()
            runclient(**kwargs)
        else:
            kwargs["output"] = None
            runclient(**kwargs)
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()
