#!/usr/bin/python3

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

@brief PTP monitoring test sender

Detailed description TBD
"""
import argparse
import signal
import socket
import struct
from threading import Event
from trslib.protocols import mudpi
from trslib.ptp import mgmt as pm
from ptpmon import payload

stop = Event()

# Signal handler
def sig_handler(signum, frame):
    """ Signal handler function."""

    print('ptpmonTestSender - received SIGINT')
    stop.set()
    
def parse_args():
    """
    @brief Parse command line arguments
    """

    description = ("TRS diagnostic packet sender for PTP clients")

    parser = argparse.ArgumentParser(description=description,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('--port', help='Destination port number', action='store',
                        dest='port', default=6617, type=int)
    parser.add_argument('--host', help='Destination hostname', action='store',
                        dest='host', default='localhost')
    parser.add_argument('--period', help='Monitor period', action='store',
                        dest='period', default=10, type=float)
    parser.add_argument('--loglevel', help='Log level [1..3]', action='store',
                        dest='loglevel', default=1, type=int)
    parser.add_argument('--deviceType', help='deviceType', action='store', default=1, type=int)
    parser.add_argument('--senderHost', help='hostname of the sender', action='store',
                        default='testSender')
    parser.add_argument('--portState1', help='portState1', action='store', default=9, type=int)
    parser.add_argument('--portState2', help='portState2', action='store', default=0, type=int)
    parser.add_argument('--offsetFromMaster', help='offsetFromMaster [ns]', action='store',
                        default=0, type=int)
    parser.add_argument('--meanPathDelay', help='meanPathDelay [ns]', action='store',
                        default=0, type=int)
    parser.add_argument('--clockId', help='clockIdentity [hex without leading 0x]',
                        action='store', default='DEADBEEFBADF000D')
    parser.add_argument('--gmId', help='grandmasterIdentity [hex without leading 0x]',
                        action='store', default='DECAFCAFEBAAAAAD')
    parser.add_argument('--parentId', help='parentPortIdentity [hex without leading 0x]',
                        action='store', default='C0CABADBADBADBAD1DEA')
    parser.add_argument('--utcOffset', help='utcOffset', action='store', default=37, type=int)
    parser.add_argument('--stepsRemoved', help='stepsRemoved', action='store', default=0, type=int)
    parser.add_argument('--errorStatus', help='errorStatus', action='store', default=0, type=int)
    parser.add_argument('--computeChecksum', help='Generate checksum for MUDPI packet', action='store_true')
    parser.add_argument('--wrongChecksum', help='Generate wrong checksum for MUDPI packet', action='store_true')
    return parser.parse_args()


if __name__ == "__main__":
    args = parse_args()

    # Register the signal handler
    signal.signal(2, sig_handler)
    
    # Create PTP statistics payload class
    payload = payload.PtpStatsPayload()
    
    # Set the static part of the payload
    payload.fields['hostname'] = args.senderHost
    payload.fields['deviceType'] = args.deviceType
    print(payload)
    
    # Create a MUDPI packet
    packet=mudpi.MudpiPacket()
    #print(packet)

    # Create socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    msg_counter = 0
    
    # Send data
    while not stop.is_set():
        # Here, a real application would retrieve the parameters from
        # the PTP device
        # If the retrieval fails, payload.fields['errorStatus']
        # should be set
        
        # Fill the payload, since this is a simple test app, we
        # do always send the same
        payload.fields['portState1'] = args.portState1
        payload.fields['portState2'] = args.portState2
        payload.fields['offsetFromMaster'] = args.offsetFromMaster
        payload.fields['meanPathDelay'] = args.meanPathDelay
        payload.fields['announceMsgCount'] = -1
        payload.fields['syncMsgCount'] = -1
        payload.fields['delayRespCount'] = -1
        payload.fields['clockIdentity'] = bytes.fromhex(args.clockId)
        payload.fields['grandmasterIdentity'] = bytes.fromhex(args.gmId)
        payload.fields['parentPortIdentity'] = bytes.fromhex(args.parentId)
        payload.fields['currentUtcOffset'] = args.utcOffset
        payload.fields['stepsRemoved'] = args.stepsRemoved
        payload.fields['errorStatus'] = args.errorStatus
        payload.fields['appSpecific1'] = 0
            
        # Prepare the packet and send
        packet.set_payload(payload.pack())
        packet.incr_sample_id()
        data = packet.pack(compute_checksum=(args.computeChecksum or args.wrongChecksum))
        if args.wrongChecksum:
            data = data[:-2] + struct.pack('H', struct.unpack('H', data[-2:])[0] + 1)

        if args.loglevel > 1:
            if msg_counter == 0:
                print('Sender         | sampleId   | MUDPI timestamp   | state port1  | state port2  | offsetM  | pathDly  | annCnt  | syncCnt | drCnt   | clockIdentity    | gmIdentity       | parentPortIdentity   | ty | lpS | sRm | er')  
            print('{0:<14s} | {1:>10d} | {2:>15f} | {3:<12s} | {4:<12s} | {5:>8d} | {6:>8d} | {7:>7d} | {8:>7d} | {9:>7d} | {10:8s} | {11:8s} | {12:10s} | {13:>2d} | {14:>3d} | {15:>3d} | {16:>2d}'.format('localhost',
                                          packet.header['sampleId'],
                                          packet.header['timestamp'],
                                          pm.PTP_STATES[int(payload.fields['portState1'])],
                                          pm.PTP_STATES[int(payload.fields['portState2'])],
                                          payload.fields['offsetFromMaster'],
                                          payload.fields['meanPathDelay'],
                                          payload.fields['announceMsgCount'],
                                          payload.fields['syncMsgCount'],
                                          payload.fields['delayRespCount'],
                                          payload.fields['clockIdentity'].hex(),
                                          payload.fields['grandmasterIdentity'].hex(),
                                          payload.fields['parentPortIdentity'].hex(),
                                          payload.fields['deviceType'],
                                          payload.fields['currentUtcOffset'],
                                          payload.fields['stepsRemoved'],
                                          payload.fields['errorStatus']))      
        sock.sendto(data, (args.host, args.port))
        msg_counter += 1
        
        # Do an interruptable wait
        stop.wait(args.period)

    print('ptpmonTestSender - exits')
    