#!/usr/bin/python3
import json
import re
import subprocess
from subprocess import Popen, PIPE
from jdsu.mts.ScpiAccess import ScpiAccess, EsrError
import time
import datetime
import os
from os import path
import sys
import syslog
from collections import defaultdict
from collections import OrderedDict
from decimal import Decimal

#############################################################
def userlog_ufom_mon_setup(mond_time, monitoring_info, lrc):
    ufom_script = os.path.abspath(__file__)
    syslog.syslog(syslog.LOG_WARNING, ufom_script)
    syslog.syslog(syslog.LOG_WARNING, monitoring_info )
    return True
    
#############################################################################
def check_ufom_licence():
    mrc = 0
    ufom_lic_status = []
    lc_state_available = 'AVAILABLE'
    lc_state_invalid ='invalid license'
    
    try:
        #
        #  Open SCPI connections
        otu_p = ScpiAccess("localhost", 1400, 30, "*PASS \"RTU\"\"PASSWORD\"\n")
        #  Get UFOM Monitoring Status
        #  "OTU:SYST:OPTION:STATUS? ULTRAFAST_MON"
        license_status = otu_p.SendAndReadCommand("OTU:SYST:OPTION:STATUS? ULTRAFAST_MON")
        status_rsp = str(license_status).lower()
        if status_rsp != lc_state_available.lower():
            mrc = 1
            ufom_lic_status = \
                OrderedDict([("status", "failure"), \
                             ("message", lc_state_invalid)])
        else:
            mrc = 0
            otu_p.disconnect()
    except Exception as error_e:
        mrc = 1
        ufom_lic_status = \
             OrderedDict([("status" , "failure"), \
                          ("message", "Command Access Error")])
    return (ufom_lic_status, mrc)

#############################################################
def send_stat_msg_to_console(resp_msg):
    sys.stdout.write(json.dumps(resp_msg, sort_keys=False, indent=4))
    sys.stdout.write("\n")
    sys.stdout.flush()
    return True

#############################################################
def port_setting_object_is_valid(mon_port):
    global xact_msg_details
    if isinstance(mon_port, str):
        xact_msg_details = ("UFOM Port setting = %s must be an integer type and value."%mon_port)
        return False
    #
    #  Retrieve number of OTU Ports
    #  "OTU:API:CONF:SUMARY?
    try:
        otu_conf = ((otu_p.SendAndReadCommand("OTU:API:CONF:SUMARY?")).split(","))
        number_of_ports = int(otu_conf[3])
    except Exception as error_e:
        xact_msg_details = "Command Access Error"
        return False    
            
    if mon_port <= number_of_ports and mon_port > 0:
        xact_msg_details = ("UFOM Port setting = %s is a valid port reference."%mon_port)
        return True
    else:
        xact_msg_details = ("UFOM Port setting = %s is not a valid OTU port number."%mon_port)
        return False

############################################################
def mode_setting_object_is_valid(mode):
    global xact_msg_details
    ufom_mode_types = ['auto', 'length']
    if str(mode).isdigit() or str(mode).lower() not in ufom_mode_types:
        xact_msg_details = ("UFOM Mode setting = %s is not a valid mode object."%mode)
        return False
    else:
        xact_msg_details = ("UFOM Mode setting = %s is a valid mode object."%mode)
        return True

############################################################
def lengthkm_setting_object_is_valid(lengthkm):
    global xact_msg_details
    test_cable_length = {10, 20, 40, 80, 160, 320}
    if isinstance(lengthkm, str):
        xact_msg_details = ("UFOM length setting = %s must be an integer type and value."%lengthkm)
        trc = 1
        return False
    elif lengthkm not in test_cable_length:
        xact_msg_details = ("UFOM Length setting = %s is not a valid length."%lengthkm)
        return False
    else:
        xact_msg_details = ("UFOM Length setting = %s is valid."%lengthkm)
        return True
    
############################################################
def lastmarker_m_is_valid(last_marker_m):
    global xact_msg_details
    last_marker_m_default_value = -1
    if isinstance(last_marker_m, str):
        xact_msg_details = ("UFOM Last Marker setting = %s must be an integer type and value."%last_marker_m)
        trc = 1
        return False
    elif last_marker_m < last_marker_m_default_value:
        xact_msg_details = ("UFOM Last Marker setting = %s is invalid."%last_marker_m)
        return False
    else:
        xact_msg_details = ("UFOM Last Marker setting = %s is valid."%last_marker_m)
        return True

#############################################################
def status_msg_for_missing_object():
    msg_details = ("One or more Monitoring Setup keys are missing from the input json.")
    setup_status = OrderedDict([("status" , "failure")])
    lrc = 1
    return (setup_status, msg_details, lrc)

#############################################################
def remove_previous_test_configuration():
    global xact_msg_details
    mrc = 0
    rnstate = ['running']
    #
    #
    try:
        #
        #  Stop previous test
        stp_cmnd = ("OTU:UFOM:STOP")
        stp_rc = otu_p.SendCommand(stp_cmnd)
        time.sleep(1)
        #
        #  Delete a previously defined test
        dlt_cmnd = ("OTU:UFOM:DELETE")
        rrp = otu_p.SendCommand(dlt_cmnd)
        mrc = 0
    except Exception as error_e:
        mrc = 1
        xact_msg_details = "Command Access Error"
    return
    
#############################################################
def length_mode_test_req(uf_mon_length, uf_mon_port, uf_mon_lastmarker_m):
    global xact_msg_details
    trc = 0

    #  OTU:UFOM:INIT:PORT [C10, C20, C40, C80, C160, C320],[Port_Number],[Last_MarkerM]
    test_request_len = ("OTU:UFOM:INIT:PORT C%s, %s, %s"%(uf_mon_length, uf_mon_port, uf_mon_lastmarker_m))
    try:
        #
        #
        test_req = otu_p.SendCommand(test_request_len)
        ufom_status = \
            OrderedDict([("status" , "success") ])
        trc = 0
    except Exception as error_e:
        trc = 1
        xact_msg_details = "Command Access Error"
        ufom_status = \
            OrderedDict([("status" , "failure") ])
    return (ufom_status, trc)
    
#############################################################
def auto_mode_test_req(ufom_port, uf_mon_lastmarker_m):
    global xact_msg_details
    mrc = 0

    try:
        #
        #  OTU:UFOM:INIT:PORT [CAUTO],[Port_Number],[Last_MarkerM]
        test_cmnd = ("OTU:UFOM:INIT:PORT CAUTO, %s, %s"%(ufom_port, uf_mon_lastmarker_m))
        test_request = otu_p.SendCommand(test_cmnd)
        ufom_status = \
             OrderedDict([("status" , "success") ])
        mrc = 0
    except Exception as error_e:
        mrc = 1
        xact_msg_details = "Command Access Error"
        ufom_status = \
             OrderedDict([("status" , "failure") ])
    return (ufom_status, mrc)
    
#############################################################
def handle_monitoring_request(jconf_data):
    conf_upd_start = time.time()
    trc = 0
    global xact_msg_details
    ufom_status_fail = OrderedDict([("status" , "failure") ])
    request_status = OrderedDict([("status" , "success") ])
    uf_mode_auto = "auto"
    uf_port = "port"
    uf_last_marker_m = "lastMarkerM"
    lastmarker_m_def_value = -1
    #
    #
    uf_mon_mode = jconf_data.get('mode', None)
    uf_mon_port = jconf_data.get('port', None)

    if not port_setting_object_is_valid(uf_mon_port) or \
           not mode_setting_object_is_valid(uf_mon_mode):
        trc = 1
        return (ufom_status_fail, trc)

    if uf_last_marker_m in jconf_data:
        uf_mon_lastmarker_m = jconf_data.get('lastMarkerM', None)
        if not lastmarker_m_is_valid(uf_mon_lastmarker_m):
            trc = 1
            return (ufom_status_fail, trc)
    else:
        uf_mon_lastmarker_m = lastmarker_m_def_value

    #print ("  Test Mode = %s \n  Test Port = %s  \n  Test LastMarkerM = %s"%(uf_mon_mode, uf_mon_port, uf_mon_lastmarker_m))

    if str(uf_mon_mode).lower() == uf_mode_auto:
        request_status, trc = auto_mode_test_req(uf_mon_port, uf_mon_lastmarker_m)
        return (request_status, trc)

    uf_mon_length = jconf_data.get('lengthKm', None)
    if not lengthkm_setting_object_is_valid(uf_mon_length):        
        trc = 1
        return (ufom_status_fail, trc)
    else:
        request_status, trc = length_mode_test_req(uf_mon_length, uf_mon_port, uf_mon_lastmarker_m)
        return (request_status, trc)
    
##############################################################################
#<><><><><><><>-------<><><><><><><>-------<><><><><><><>-------<><><><><><><>
if __name__ == "__main__":
    MAX_SCRIPTINPUTS = 2
    MAX_NUM_OF_INPUT_ARGUMENTS = MAX_SCRIPTINPUTS - 1
     
    UF_MODE_LENGTH = "length"
    
    setting_key_mode = 'mode'
    setting_key_port = 'port'
    setting_key_lengthkm = 'lengthKm'
    mon_setup_status = []
    ufom_monitoring_lic_state = ()
    xact_msg_details = ""
    grc = 0

    start_time_date = time.strftime("%c")
    start_time = time.time()
    syslog.openlog(logoption=syslog.LOG_PID, facility=syslog.LOG_USER)
    #
    #  Check if UFOM Licence is available
    ufom_monitoring_lic_state, grc = check_ufom_licence()
    if ( grc > 0 ):
        userlog_ufom_mon_setup(start_time_date, json.dumps(ufom_monitoring_lic_state, sort_keys=False, indent=4), grc)
        send_stat_msg_to_console(ufom_monitoring_lic_state)
        exit(grc)
    #
    #  Validate the the number of input arguments
    if len(sys.argv) != MAX_SCRIPTINPUTS:
        xact_msg_details = "Number of input arguments must be " + str(MAX_NUM_OF_INPUT_ARGUMENTS)
        mon_setup_status = OrderedDict([("status" , "failure"), \
                                         ("message", xact_msg_details)])
        print(json.dumps(mon_setup_status, sort_keys=False, indent=4))
        userlog_ufom_mon_setup(start_time_date, json.dumps(mon_setup_status, sort_keys=False, indent=4), grc)
        exit (1)
    #
    #
    try:
        otu_p = ScpiAccess("localhost", 1400, 60, "*PASS \"RTU\"\"PASSWORD\"\n")
        try:
            #
            # Load the JSON formatted config file into an ordered
            # dictionary.
            xact_msg_details = "Input json request parameters read successfully"
            setup_data = json.loads(sys.argv[1], object_pairs_hook=OrderedDict)
            #print ("\n%s = %s "%(xact_msg_details,setup_data))
            #
            #
            remove_previous_test_configuration()
            if setting_key_port not in setup_data:
                mon_setup_status, xact_msg_details, grc = status_msg_for_missing_object()
            elif setting_key_mode not in setup_data:
                mon_setup_status, xact_msg_details, grc = status_msg_for_missing_object()
            elif (setting_key_lengthkm not in setup_data) and (str(setup_data.get('mode', None)).lower() == UF_MODE_LENGTH):
                mon_setup_status, xact_msg_details, grc = status_msg_for_missing_object()            
            else:
                mon_setup_status, grc = handle_monitoring_request(setup_data)
                
        except ValueError as err:
            #
            #  input file is not a properly formatted json file
            xact_msg_details = "Input parameters are not a valid json form."
            #print ("\n%s  Input = %s"%(xact_msg_details, sys.argv[1]))
            mon_setup_status = OrderedDict([("status" , "failure")])
            grc = 1
        #
        #
    except Exception as error_e:
        grc = 1
        xact_msg_details = "Access Error"
        mon_setup_status = OrderedDict([("status" , "failure")])
        details = error_e

    finally:
        # 
        #
        if grc != 0:
            mon_setup_status['message'] = xact_msg_details                                      
            userlog_ufom_mon_setup(start_time_date, \
                               ("  req parameter = "+str(sys.argv[1])+";\n" \
                               +"  cli response  = "+json.dumps(mon_setup_status)+"\n"), grc)

        else:
            userlog_ufom_mon_setup(start_time_date, \
                               ("  req parameter = "+str(sys.argv[1])+";\n" \
                               +"  cli response  = "+json.dumps(mon_setup_status)+"\n"), grc)
        send_stat_msg_to_console(mon_setup_status)
    #
    #
    exit (grc)
