import ctypes 
import time
import struct
import sys
import keyboard
from pyiec104.iec104api import *

# enbale to view traffic
VIEW_TRAFFIC = 1

# print the struct sIEC104DataAttributeID and sIEC104DataAttributeData
def vPrintDataInformation(psIEC104DataAttributeID , psIEC104DataAttributeData ):
    print(f" IP Address {psIEC104DataAttributeID.contents.ai8IPAddress}")
    print(f" Port Number {psIEC104DataAttributeID.contents.u16PortNumber}")
    print(f" Common Address {psIEC104DataAttributeID.contents.u16CommonAddress}")
    print(f" Typeid ID is {psIEC104DataAttributeID.contents.eTypeID} IOA   {psIEC104DataAttributeID.contents.u32IOA}")
    print(f" Datatype->{psIEC104DataAttributeData.contents.eDataType} Datasize->{ psIEC104DataAttributeData.contents.eDataSize}" )

    if(psIEC104DataAttributeData.contents.tQuality) != eIEC870QualityFlags.GD :
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.IV) == eIEC870QualityFlags.IV:
            print(" IEC_INVALID_FLAG")
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.NT) == eIEC870QualityFlags.NT:
             print(" IEC_NONTOPICAL_FLAG")
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.SB) == eIEC870QualityFlags.SB:
             print(" IEC_SUBSTITUTED_FLAG")
        if(psIEC104DataAttributeData.contents.tQuality & eIEC870QualityFlags.BL) == eIEC870QualityFlags.BL:
             print(" IEC_BLOCKED_FLAG")

    data_type = psIEC104DataAttributeData.contents.eDataType

    if data_type in (eDataTypes.SINGLE_POINT_DATA, eDataTypes.DOUBLE_POINT_DATA, eDataTypes.UNSIGNED_BYTE_DATA):
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 1))
        u8data = struct.unpack('B', data)[0] 
        print(f" Data : {u8data}")

    elif data_type == eDataTypes.SIGNED_BYTE_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 1))
        i8data = struct.unpack('b', data)[0]        
        print(f" Data : {i8data}")

    elif data_type == eDataTypes.UNSIGNED_WORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 2))
        u16data = struct.unpack('H', data)[0]        
        print(f" Data : {u16data}")

    elif data_type == eDataTypes.SIGNED_WORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 2))
        i16data = struct.unpack('h', data)[0]        
        print(f" Data : {i16data}")

    elif data_type == eDataTypes.UNSIGNED_DWORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 4))
        u32data = struct.unpack('I', data)[0]        
        print(f" Data : {u32data}")


    elif data_type == eDataTypes.SIGNED_DWORD_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 4))
        i32data = struct.unpack('i', data)[0]        
        print(f" Data : {i32data}")

    elif data_type == eDataTypes.FLOAT32_DATA:
        data = bytearray(ctypes.string_at(psIEC104DataAttributeData.contents.pvData, 4))
        f32data = struct.unpack('f', data)[0] 
        print(f" Data : {f32data:.3f}")

    if psIEC104DataAttributeData.contents.sTimeStamp.u16Year != 0:
        print(f" Date : {psIEC104DataAttributeData.contents.sTimeStamp.u8Day:02}-{psIEC104DataAttributeData.contents.sTimeStamp.u8Month:02}-{psIEC104DataAttributeData.contents.sTimeStamp.u16Year:04}  DOW -{psIEC104DataAttributeData.contents.sTimeStamp.u8DayoftheWeek}")
        print(f" Time : {psIEC104DataAttributeData.contents.sTimeStamp.u8Hour:02}:{psIEC104DataAttributeData.contents.sTimeStamp.u8Minute:02}:{psIEC104DataAttributeData.contents.sTimeStamp.u8Seconds:02}:{psIEC104DataAttributeData.contents.sTimeStamp.u16MilliSeconds:03}")

# update callback
def cbUpdate(u16ObjectId, psIEC104DataAttributeID, psIEC104DataAttributeData, psIEC104UpdateParameters, ptErrorValue):
    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbUpdate() called ")
    print(" Client ID : %u" % u16ObjectId)
    vPrintDataInformation(psIEC104DataAttributeID, psIEC104DataAttributeData)
    message = f" COT {psIEC104UpdateParameters.contents.eCause}"
    print(message)
    
    return i16rErrorCode

# Client connection Status Callback
def cbClientStatus(u16ObjectId, psIEC104ClientConnectionID , peSat, ptErrorValue):

    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0   
    print(" cbClientStatus called -  from IEC104 client")

    print(" Server ID : %u" % u16ObjectId)
    print(" IP Address %s Port %u " % (psIEC104ClientConnectionID.contents.ai8IPAddress, psIEC104ClientConnectionID.contents.u16PortNumber))
    print(" Server CA : %u" % psIEC104ClientConnectionID.contents.u16CommonAddress)

    if peSat.contents.value == eStatus.CONNECTED:
        print(" Status - Connected")
    else:
        print(" Status - Disconnected")
    
   
    return i16rErrorCode

# Debug callback
def cbDebug(u16ObjectId,  psIEC104DebugData , ptErrorValue):
    
    i16rErrorCode = ctypes.c_short()
    i16rErrorCode = 0 

    # u16nav = ctypes.c_ushort()
    u16nav = 0
   
    #print(" cbDebug() called")
    
    print(f" {psIEC104DebugData.contents.sTimeStamp.u8Hour}:{psIEC104DebugData.contents.sTimeStamp.u8Minute}:{psIEC104DebugData.contents.sTimeStamp.u8Seconds} Server ID: {u16ObjectId}",end='')
    
    if (psIEC104DebugData.contents.u32DebugOptions & eDebugOptionsFlag.DEBUG_OPTION_TX) == eDebugOptionsFlag.DEBUG_OPTION_TX:
        print(f" send IP {psIEC104DebugData.contents.ai8IPAddress} Port {psIEC104DebugData.contents.u16PortNumber}", end='')
        print(" ->", end='')
        
        u16nav = 0
        for u16nav in range(int(psIEC104DebugData.contents.u16TxCount)):
            #print(f" {psIEC104DebugData.contents.au8TxData[u16nav]:02x}", end='')
            try:
                print(f" {psIEC104DebugData.contents.au8TxData[u16nav]:02x}", end='')
            except TypeError:
                print("TypeError: Check list of indices")
        
    
        
    if (psIEC104DebugData.contents.u32DebugOptions & eDebugOptionsFlag.DEBUG_OPTION_RX) == eDebugOptionsFlag.DEBUG_OPTION_RX:
        print(f" receive IP {psIEC104DebugData.contents.ai8IPAddress} Port {psIEC104DebugData.contents.u16PortNumber}", end='')
        print(" <-", end='')

        #print(f"u16RxCount - {psIEC104DebugData.contents.u16RxCount} len aaray - {len(psIEC104DebugData.contents.au8RxData)}")

        
        for u16nav in range(int(psIEC104DebugData.contents.u16RxCount)):
            print(f" {psIEC104DebugData.contents.au8RxData[u16nav]:02x}", end='')
      

        
    if (psIEC104DebugData.contents.u32DebugOptions & eDebugOptionsFlag.DEBUG_OPTION_ERROR) == eDebugOptionsFlag.DEBUG_OPTION_ERROR:
        print(f" Error message {psIEC104DebugData.contents.au8ErrorMessage}")
        print(f" ErrorCode {psIEC104DebugData.contents.iErrorCode}")
        print(f" ErrorValue {psIEC104DebugData.contents.tErrorvalue}")

    print("", flush=True)

    return i16rErrorCode

# print error code and description
def errorcodestring(errorcode):
    sIEC104ErrorCodeDes = sIEC104ErrorCode()
    sIEC104ErrorCodeDes.iErrorCode = errorcode
    iec104_lib.IEC104ErrorCodeString(sIEC104ErrorCodeDes)
    return sIEC104ErrorCodeDes.LongDes.decode("utf-8")

# print error value and description
def errorvaluestring(errorvalue):
    sIEC104ErrorValueDes = sIEC104ErrorValue()
    sIEC104ErrorValueDes.iErrorValue = errorvalue   
    iec104_lib.IEC104ErrorValueString(sIEC104ErrorValueDes)
    return sIEC104ErrorValueDes.LongDes.decode("utf-8")

# send command for particular typeid and IOA FROM USER INPUT
def sendCommand(myClient):

    i16ErrorCode = ctypes.c_short()
    tErrorValue =  ctypes.c_short()
    
    print("sendCommand CALLED")
    while True:
        try:
            u32ioa = ctypes.c_uint32(int(input("C_SE_TC_1 command Enter Information object address(IOA) - ")))
        except ValueError:
            print("Please enter a number ")
        else:
            break

    while True:
        try:
            f32value = ctypes.c_float(float(input("Enter command float value: ")))
        except ValueError:
            print("Please enter a float number ")
        else:
            break

   
    psDAID = sIEC104DataAttributeID()
    psNewValue  = sIEC104DataAttributeData()
    psIEC104CommandParameters = sIEC104CommandParameters()

    psDAID.ai8IPAddress                         = "127.0.0.1".encode('utf-8')
    psDAID.u16PortNumber                        =  2404
    psDAID.u16CommonAddress                     =  1
    psDAID.eTypeID                              =  eIEC870TypeID.C_SE_TC_1
    psDAID.u32IOA                               =   u32ioa
    psDAID.pvUserData                           =   None
    psNewValue.tQuality                         =   eIEC870QualityFlags.GD

    #psNewValue.pvData                          =   ctypes.cast(ctypes.pointer(f32value),ctypes.c_void_p)
    psNewValue.pvData                           = ctypes.c_void_p(ctypes.addressof(f32value))

    psNewValue.eDataType                        =   eDataTypes.FLOAT32_DATA
    psNewValue.eDataSize                        =   eDataSizes.FLOAT32_SIZE

    now = time.time()
    timeinfo = time.localtime(now)
    
    #current date
    psNewValue.sTimeStamp.u8Day = timeinfo.tm_mday
    psNewValue.sTimeStamp.u8Month = timeinfo.tm_mon
    psNewValue.sTimeStamp.u16Year = timeinfo.tm_year 

    psNewValue.sTimeStamp.u8Hour = timeinfo.tm_hour
    psNewValue.sTimeStamp.u8Minute = timeinfo.tm_min
    psNewValue.sTimeStamp.u8Seconds = timeinfo.tm_sec
    psNewValue.sTimeStamp.u16MilliSeconds = 0
    psNewValue.sTimeStamp.u16MicroSeconds = 0
    psNewValue.sTimeStamp.i8DSTTime = 0
    psNewValue.sTimeStamp.u8DayoftheWeek = 4
    psNewValue.bTimeInvalid = False

    #printf(" COMMAND float value %f",f32data);
    # SEND command
   
    i16ErrorCode = iec104_lib.IEC104Operate(myClient, ctypes.byref(psDAID), ctypes.byref(psNewValue),ctypes.byref(psIEC104CommandParameters),ctypes.byref((tErrorValue)))
    if i16ErrorCode != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Operate() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)     

    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Operate() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 

# main program
def main():
    
    print(" \t\t**** IEC 60870-5-104 Protocol Client Library Test ****")
    
    # Check library version against the library header file
    if iec104_lib.IEC104GetLibraryVersion().decode("utf-8") != IEC104_VERSION:
        print(" Error: Version Number Mismatch")
        print(" Library Version is  : {}".format(iec104_lib.IEC104GetLibraryVersion().decode("utf-8")))
        print(" The Header Version used is : {}".format(IEC104_VERSION))
        print("")
        input(" Press Enter to free IEC 104 client object")
        exit(0)

    print(" Library Version is : {}".format(iec104_lib.IEC104GetLibraryVersion().decode("utf-8")))
    print(" Library Build on   : {}".format(iec104_lib.IEC104GetLibraryBuildTime().decode("utf-8")))
    print(" Library License Information   : {}".format(iec104_lib.IEC104GetLibraryLicenseInfo().decode("utf-8")))

    i16ErrorCode = ctypes.c_short()
    tErrorValue =  ctypes.c_short()

    sParameters = sIEC104Parameters()

   

    # Initialize IEC 60870-5-104 Server object parameters
    sParameters.eAppFlag          =  eApplicationFlag.APP_CLIENT        # This is a IEC104 Server      
    sParameters.ptReadCallback    = IEC104ReadCallback(0)               # Read Callback
    sParameters.ptWriteCallback   = IEC104WriteCallback(0)                # Write Callback
    sParameters.ptUpdateCallback  = IEC104UpdateCallback(cbUpdate)                 # Update Callback
    sParameters.ptSelectCallback  = IEC104ControlSelectCallback(0)               # Select Callback
    sParameters.ptOperateCallback = IEC104ControlOperateCallback(0)              # Operate Callback
    sParameters.ptCancelCallback  = IEC104ControlCancelCallback(0)              # Cancel Callback
    sParameters.ptFreezeCallback  = IEC104ControlFreezeCallback(0)              # Freeze Callback
    sParameters.ptDebugCallback   = IEC104DebugMessageCallback(cbDebug)                # Debug Callback
    sParameters.ptPulseEndActTermCallback = IEC104ControlPulseEndActTermCallback(0)    # pulse end callback
    sParameters.ptParameterActCallback = IEC104ParameterActCallback(0)   # Parameter activation callback
    sParameters.ptServerStatusCallback = IEC104ServerStatusCallback(0)   # server status callback
    sParameters.ptDirectoryCallback    = IEC104DirectoryCallback(0)              # Directory Callback
    sParameters.ptClientStatusCallback   = IEC104ClientStatusCallback(cbClientStatus)           # client connection status Callback
    sParameters.u32Options        = 0
    sParameters.u16ObjectId				= 1				#Server ID which used in callbacks to identify the iec 104 server object   


    # Create a CLIENT object

    myClient =  iec104_lib.IEC104Create(ctypes.byref(sParameters), ctypes.byref((i16ErrorCode)), ctypes.byref((tErrorValue)))
    if i16ErrorCode.value != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Create() failed: {i16ErrorCode.value} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)    
        exit(0) 
    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Create() success: {i16ErrorCode.value} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 

    while(True):

        sIEC104Config = sIEC104ConfigurationParameters()

        # Client load configuration - communication and protocol configuration parameters
        sIEC104Config.sClientSet.ai8SourceIPAddress ="0.0.0.0".encode('utf-8')	# client own IP Address , use 0.0.0.0 / network ip address for binding socket*/   
		
        sIEC104Config.sClientSet.benabaleUTCtime    =   False
        
        
        # Debug option settings
        if  'VIEW_TRAFFIC' in globals():
            sIEC104Config.sClientSet.sDebug.u32DebugOptions   =   (eDebugOptionsFlag.DEBUG_OPTION_RX | eDebugOptionsFlag.DEBUG_OPTION_TX)
        else:
            sIEC104Config.sClientSet.sDebug.u32DebugOptions  =   0
        



        sIEC104Config.sClientSet.u16TotalNumberofConnection =   1

        arraypointer = (sClientConnectionParameters * sIEC104Config.sClientSet.u16TotalNumberofConnection)()
        sIEC104Config.sClientSet.psClientConParameters  = ctypes.cast(arraypointer, ctypes.POINTER(sClientConnectionParameters))

        #client 1 configuration Starts
		#check server configuration - TCP/IP Address
	    
        arraypointer[0].ai8DestinationIPAddress="127.0.0.1".encode('utf-8')  #iec 104 server ip address
        arraypointer[0].u16PortNumber             =   2404  # iec 104 server port number


        arraypointer[0].i16k                      =   12
        arraypointer[0].i16w                      =   8
        arraypointer[0].u8t0                      = 30
        arraypointer[0].u8t1                      = 15
        arraypointer[0].u8t2                      = 10
        arraypointer[0].u16t3                     = 20

        arraypointer[0].eState =  eConnectState.DATA_MODE
        arraypointer[0].u8TotalNumberofStations           =   1
        arraypointer[0].au16CommonAddress[0]          =   1
        arraypointer[0].au16CommonAddress[1]          =   0
        arraypointer[0].au16CommonAddress[2]          =   0
        arraypointer[0].au16CommonAddress[3]          =   0
        arraypointer[0].au16CommonAddress[4]          =   0
        arraypointer[0].u8OriginatorAddress           =   0


        arraypointer[0].u32GeneralInterrogationInterval   =   0    # in sec if 0 , gi will not send in particular interval
        arraypointer[0].u32Group1InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group2InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group3InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group4InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group5InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group6InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group7InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group8InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group9InterrogationInterval    =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group10InterrogationInterval   =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group11InterrogationInterval   =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group12InterrogationInterval   =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group13InterrogationInterval   =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group14InterrogationInterval   =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group15InterrogationInterval   =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32Group16InterrogationInterval   =   0    # in sec if 0 , group 1 interrogation will not send in particular interval*/
        arraypointer[0].u32CounterInterrogationInterval   =   0    # in sec if 0 , ci will not send in particular interval*/
        arraypointer[0].u32Group1CounterInterrogationInterval =   0    # in sec if 0 , group 1 counter interrogation will not send in particular interval*/
        arraypointer[0].u32Group2CounterInterrogationInterval =   0    # in sec if 0 , group 1 counter interrogation will not send in particular interval*/
        arraypointer[0].u32Group3CounterInterrogationInterval =   0    # in sec if 0 , group 1 counter interrogation will not send in particular interval*/
        arraypointer[0].u32Group4CounterInterrogationInterval =   0    # in sec if 0 , group 1 counter interrogation will not send in particular interval*/
        arraypointer[0].u32ClockSyncInterval  =   0              # in sec if 0 , clock sync, will not send in particular interval */

        arraypointer[0].u32CommandTimeout =   10000
        arraypointer[0].u32FileTransferTimeout    =   50000
        arraypointer[0].bCommandResponseActtermUsed   =   True


        arraypointer[0].bEnablefileftransfer = False
        arraypointer[0].ai8FileTransferDirPath = "C:\\".encode('utf-8') 
        arraypointer[0].bUpdateCallbackCheckTimestamp = False
        arraypointer[0].eCOTsize = eCauseofTransmissionSize.COT_TWO_BYTE
        
        sIEC104Config.sClientSet.bAutoGenIEC104DataObjects = True
        # Define number of objects
        arraypointer[0].u16NoofObject             =   0
        # Allocate memory for objects
        arraypointer[0].psIEC104Objects = None

        # client 1 configuration ends

       
        i16ErrorCode =  iec104_lib.IEC104LoadConfiguration(myClient, ctypes.byref(sIEC104Config), ctypes.byref((tErrorValue)))
        if i16ErrorCode != 0:
            message = f"IEC 60870-5-104 Library API Function - IEC104LoadConfiguration() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message)    
            break

        else:
            message = f"IEC 60870-5-104 Library API Function - IEC104LoadConfiguration() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message) 



        i16ErrorCode =  iec104_lib.IEC104Start(myClient, ctypes.byref((tErrorValue)))
        if i16ErrorCode != 0:
            message = f"IEC 60870-5-104 Library API Function - IEC104Start() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message)    
            break

        else:
            message = f"IEC 60870-5-104 Library API Function - IEC104Start() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
            print(message) 

        print("press x to exit")

        while(True):
            if keyboard.is_pressed('x'):
                print("x pressed, exiting loop")
                keyboard.release('x')
                time.sleep(0.1)
                break
            elif keyboard.is_pressed('s'):
                print("u pressed, send command called")
                keyboard.release('s')
                time.sleep(0.1)
                sendCommand(myClient)

            #Xprint("sleep called")
            time.sleep(0.1)

        break
            
            

      



    i16ErrorCode =  iec104_lib.IEC104Stop(myClient, ctypes.byref((tErrorValue)))
    if i16ErrorCode != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Stop() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)        
    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Stop() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 



    i16ErrorCode =  iec104_lib.IEC104Free(myClient, ctypes.byref((tErrorValue)))
    if i16ErrorCode != 0:
        message = f"IEC 60870-5-104 Library API Function - IEC104Free() failed: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message)    
    else:
        message = f"IEC 60870-5-104 Library API Function - IEC104Free() success: {i16ErrorCode} - {errorcodestring(i16ErrorCode)}, {tErrorValue.value} - {errorvaluestring(tErrorValue)}"
        print(message) 


    print("Exiting the program...")
      

if __name__ == "__main__":
    main()
