#!/usr/bin/python3

"""
@file
@ingroup trs_common_ptpmonc
@copyright ESO - European Southern Observatory
@author C. Soenke

@brief Command line interface to the ptp monitoring daemon

Detailed description TBD
"""

import argparse
import signal
import socket
import textwrap
from threading import Event
import sys

from ptpmon import local_interface as local_if

PTPMOND_ADRS = 'trsmon'

stop = Event()

# Signal handler
def sig_handler(signum, frame):
    """Signal handler function"""
    print('ptpmonc - received signal', signum)
    stop.set()


# Argument parser
def parse_args():
    """
    Parse command line arguments
    """
    description = textwrap.dedent('''\
            PTP monitoring client

            Supported commands are:
                status   print current status
                events   subscribe to events and print them
                stats    print ptp4l stats
            ''')

    parser = argparse.ArgumentParser(description=description,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('command', nargs=1, help='Command to be executed')
    parser.add_argument('--uds-adrs',
                        help='UDS address of the monitoring daemon without the leading \\0',
                        action='store', dest='uds_adrs', default=PTPMOND_ADRS)

    args = parser.parse_args()
    return args


def handle_status_cmd(sock):
    """Handle status command."""

    sock.settimeout(5)

    # Send poll request
    sock.sendall(bytes([local_if.PayloadType.STATUS_REQ.value]))

    # Get the reply
    try:
        data = sock.recv(512)
    except Exception as e:
        print('Error waiting for reply', e)
        return

    msg_type, status, msg_text = local_if.decode_status_msg(data)
    if msg_type != local_if.PayloadType.STATUS_REPLY:
        print('ERROR: wrong payload type ({})'.format(msg_type))
    else:
        print(status, msg_text)


def handle_events_cmd(sock):
    """Handle events command."""

    sock.settimeout(5)

    # Subscribe events
    sock.sendall(bytes([local_if.PayloadType.SUBSCRIBE_REQ.value]))

    # Get subscription reply
    try:
        data = sock.recv(512)
    except Exception as e:
        print('Error during event subscription', e)
        return

    if data[0] == local_if.PayloadType.SUBSCRIBE_REPLY.value:
        print('Server acknowledged subscription')
    else:
        print('Error receiving acknowledge for subscription ({})'.format(data))
        return

    # Register the signal handler
    signal.signal(2, sig_handler)

    # Wait for events
    sock.settimeout(1)
    while not stop.is_set():
        # Loop
        try:
            data = sock.recv(1024)
        except socket.timeout:
            continue
        except Exception as e:
            print(e)

        if not data:
            print('No data received, connection closed')
            # TODO: here we could reconnect
            return

        msg_type, status, msg_text = local_if.decode_status_msg(data)
        print(status, msg_text)


def handle_stats_cmd(sock):
    """Handle stats command."""

    sock.settimeout(5)

    # Send poll request
    sock.sendall(bytes([local_if.PayloadType.STATS_REQ.value]))

    # Get the reply
    try:
        data = sock.recv(65536)
    except Exception as e:
        print('Error waiting for reply', e)
        return

    print(data.decode('utf-8'))


def main(args):
    """Main function of ptpmonc"""

    uds_adrs = '\0' + args.uds_adrs
    uds_adrs_txt = uds_adrs.replace('\0', '\\0')

    # Create socket
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
    try:
        sock.connect(uds_adrs)
    except Exception as e:
        print(f'Failed to connect to UDS socket {uds_adrs_txt}')
        print(e)
        sys.exit(1)

    cmd = args.command[0]

    print('Sending {} command to {}'.format(cmd, uds_adrs_txt))

    # Switch command
    if cmd == 'status':
        handle_status_cmd(sock)
    elif cmd == 'events':
        handle_events_cmd(sock)
    elif cmd == 'stats':
        handle_stats_cmd(sock)
    else:
        print('Unrecognized command', cmd)

    # Close socket
    sock.close()


if __name__ == "__main__":
    # Parse command line arguments
    args = parse_args()

    # Run main
    main(args)
