def send_sms(From='', To='', Cc='', Bcc='', Message='', language='En', sms_template='', data_record={}, application_name='', caller_area={}):
    """
    send_sms (wrapper)
    """
    _process_name = 'send_sms'
    _process_entity = 'sms'
    _process_action = 'send_sms'
    _process_msgID = f'process:[{_process_name}]'
    _process_identity_kwargs = {'type': 'process', 'module': module_id, 'name': _process_name, 'action': _process_action, 'entity': _process_entity, 'msgID': _process_msgID,}
    _process_adapters_kwargs = {'dbsession': None}
    _process_log_kwargs = {'indent_method': 'AUTO', 'indent_level': None}
    _process_debug_level = get_debug_level(caller_area.get('debug_level'), **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_files = get_debug_files(_process_debug_level, **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_kwargs={'debug_level':_process_debug_level,'debug_files':_process_debug_files}

    _process_signature = build_process_signature(**_process_identity_kwargs, **_process_adapters_kwargs, **_process_debug_kwargs, **_process_log_kwargs)
    _process_call_area = build_process_call_area(_process_signature, caller_area)

    log_process_start(_process_msgID,**_process_call_area)

    log_process_input('', 'From', From,**_process_call_area)
    log_process_input('', 'To', To,**_process_call_area)
    log_process_input('', 'Cc', Cc,**_process_call_area)
    log_process_input('', 'Bcc', Bcc,**_process_call_area)
    log_process_input('', 'Message', Message, **_process_call_area)
    log_process_input('', 'language', language, **_process_call_area)
    log_process_input('', 'sms_template', sms_template, **_process_call_area)
    log_process_input('', 'application_name', application_name, **_process_call_area)
    log_process_input('', 'caller_area', caller_area, **_process_call_area)

    if not From:
        From = thisApp.application_configuration.get('sms_sender')
        log_process_data('', 'From', From,**_process_call_area)
    if not From:
        From = application_name
        log_process_data('', 'From', From,**_process_call_area)
    if not(From):
        msg = f'sms sender not defined'
        api_result = {'api_status': 'error', 'api_message': msg}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    if not(To):
        msg = f'sms recipient not defined'
        api_result = {'api_status': 'error', 'api_message': msg}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result
    
    phone_number = get_validated_phone_number(To)

    if not phone_number.get('api_status') == 'success':
        msg=phone_number.get('api_message','?')
        msg = f'invalid recipient number {To}. ({msg})'
        api_result = {'api_status': 'error', 'api_message': msg}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    To=phone_number.get('international_number')
    if not(To):
        msg = f'system error'
        api_result = {'api_status': 'error', 'api_message': msg}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    if not(Message):
        msg = f'Message not defined'
        api_result = {'api_status': 'error', 'api_message': msg}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    if not(Message) and not(sms_template):
        msg = f'no message or template defined'
        api_result = {'api_status': 'error', 'api_message': msg}
        log_process_message('', 'warning', msg,**_process_call_area)
    else:
        if sms_template:
            (t1, t2, t3) = get_template(sms_template,application_name,language)
            if t1 or t2 or t3:
                Message = t1
            else:
                msg = f'sms template {sms_template} not found'
                api_result = {'api_status': 'error', 'api_message': msg}
                log_process_finish(_process_msgID, api_result, **_process_call_area)    
                return api_result

    if Message.find('#')>=0:
        Message = string_translate(Message, data_record)
        log_process_data('', 'translated Message', Message,**_process_call_area)

    SMS_SERVER_PROVIDER = thisApp.application_configuration.get('SMS_SERVER_PROVIDER')
    log_process_parameter('', 'config param', 'SMS_SERVER_PROVIDER', SMS_SERVER_PROVIDER, **_process_call_area)
    if not SMS_SERVER_PROVIDER:
        SMS_SERVER_PROVIDER = 'CYTA'
        log_process_parameter('', 'default config param', 'SMS_SERVER_PROVIDER', SMS_SERVER_PROVIDER, **_process_call_area)

    country_code = phone_number.get('country_code')
    if country_code == '357':
        SMS_SERVER_PROVIDER = 'CYTA'
        log_process_parameter('', 'set config param', 'SMS_SERVER_PROVIDER', SMS_SERVER_PROVIDER, **_process_call_area)
        To = phone_number.get('national_number')
        log_process_data('', 'To national number', To,**_process_call_area)
    else:
        SMS_SERVER_PROVIDER = 'NEXMO'
        log_process_parameter('', 'set config param', 'SMS_SERVER_PROVIDER', SMS_SERVER_PROVIDER, **_process_call_area)
        To = phone_number.get('international_number')
        log_process_data('', 'To international number', To,**_process_call_area)

    try:
        if SMS_SERVER_PROVIDER.upper() == 'SINCH':
                send_result = sendSMS_through_SINCH(From, To, Cc, Bcc, Message, language, caller_area=_process_call_area)
        else:
            if SMS_SERVER_PROVIDER == 'NEXMO':
                send_result = sendSMS_through_NEXMO(From, To, Cc, Bcc, Message, language, caller_area=_process_call_area)
            else:
                send_result = sendSMS_through_CYTA(From, To, Cc, Bcc, Message, language, caller_area=_process_call_area)
    except Exception as error_text:
        msg= f'sms send failed. system error:{error_text}'
        log_process_message('', 'error', msg,**_process_call_area)
        provider_reply={'provider_reply':f'exception occurred executing provider api','reply_code':'99'} 
        api_result = {'api_status': 'error', 'api_message': msg,'api_data':provider_reply}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    if send_result.get('api_status')=='success':
        smstext=Message[0:5]+'***'
        msg = f'OK. sms sent To [{To}] from [{From}] with Text {smstext}'
        api_result = send_result        
        api_result.update({'api_message': msg})
    else:
        api_result = send_result
    # provider_reply = {'provider_reply': reply, 'reply_code': status_code,'reply_message':reply_message}
                
    log_process_finish(_process_msgID, api_result, **_process_call_area)    
    return api_result
def sendSMS_through_NEXMO(From, To, Cc, Bcc, Message, language='En', caller_area={}):
    """
    sendSMS_through_NEXMO
    """
    _process_name = 'sendSMS_through_NEXMO'
    _process_entity = 'sms'
    _process_action = 'send_sms'
    _process_msgID = f'process:[{_process_name}]'
    _process_identity_kwargs = {'type': 'process', 'module': module_id, 'name': _process_name, 'action': _process_action, 'entity': _process_entity, 'msgID': _process_msgID,}
    _process_adapters_kwargs = {'dbsession': None}
    _process_log_kwargs = {'indent_method': 'AUTO', 'indent_level': None}
    _process_debug_level = get_debug_level(caller_area.get('debug_level'), **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_files = get_debug_files(_process_debug_level, **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_kwargs={'debug_level':_process_debug_level,'debug_files':_process_debug_files}

    _process_signature = build_process_signature(**_process_identity_kwargs, **_process_adapters_kwargs, **_process_debug_kwargs, **_process_log_kwargs)
    _process_call_area = build_process_call_area(_process_signature, caller_area)

    log_process_start(_process_msgID,**_process_call_area)

    log_process_input('', 'From', From,**_process_call_area)
    log_process_input('', 'To', To,**_process_call_area)
    log_process_input('', 'Cc', Cc,**_process_call_area)
    log_process_input('', 'Bcc', Bcc,**_process_call_area)
    log_process_input('', 'language', language, **_process_call_area)
    log_process_input('', 'Message', Message, **_process_call_area)
    log_process_input('', 'caller_area', caller_area, **_process_call_area)

    SMS_SERVER_NEXMO_API_KEY = thisApp.application_configuration.get('SMS_SERVER_NEXMO_API_KEY')
    SMS_SERVER_NEXMO_API_SECRET = thisApp.application_configuration.get('SMS_SERVER_NEXMO_API_SECRET')

    # SMS_SERVER_NEXMO_API_KEY = '3ee5cdd5'
    # SMS_SERVER_NEXMO_API_SECRET = 'lgzsdgI4cP9eZl7J'
    # # SMS_SERVER_NEXMO_FROM_NUMBER = '35799599819'

    log_process_parameter('', 'config param', 'SMS_SERVER_NEXMO_API_KEY', SMS_SERVER_NEXMO_API_KEY, **_process_call_area)
    log_process_parameter('', 'config param', 'SMS_SERVER_NEXMO_API_SECRET', SMS_SERVER_NEXMO_API_SECRET, **_process_call_area)
    # Within the Nexmo Voice API all numbers are in E.164 format. This means that numbers:
    # Omit both a leading + and the international access code such as 00 or 001.
    # Contain no special characters, such as a space, () or -    
    To = To.replace('+', '').replace('(', '').replace(')', '').replace('-', '')
    log_process_data('', 'To number in E.164 format', To, **_process_call_area)
    
    try:
        msg='start sending SMS using NEXMO'
        log_process_message('', '', msg,**_process_call_area)

        if caller_area.get('sms_simulation'):
            reply='sms_simulation:success'
            status_code = '0'
            reply_message = "simulated sms send"
            sms_uid = "simulated_sms:"
            log_process_message('','success',"SIMULATION:Message sent successfully.")
        else:
            # Create a new Nexmo Client object:
            nexmo_client = nexmo.Client(key=SMS_SERVER_NEXMO_API_KEY, secret=SMS_SERVER_NEXMO_API_SECRET)
            # Send the SMS message:
            nexmo_api_result = nexmo_client.send_message({
                'from': From,
                'to': To,
                'text': Message,
            })
            log_process_data('', 'nexmo_api_result', nexmo_api_result, **_process_call_area)
            #{'message-count': '1', 'messages': [{'to': 'YOUR-PHONE-NUMBER', 'message-id': '0D00000039FFD940', 'status': '0', 'remaining-balance': '14.62306950', 'message-price': '0.03330000', 'network': '12345'}]}
            #{'message-count': '1', 'messages': [{'to':'35799359864', 'message-id':'1C00000027D52523', 'status':'0', 'remaining-balance':'1.79360000', 'message-price':'0.06880000', 'network':'28001'}]}
            reply=nexmo_api_result
            status_code = nexmo_api_result["messages"][0].get("status","99")
            reply_message = nexmo_api_result["messages"][0].get("error-text","")
            sms_uid = nexmo_api_result["messages"][0].get("message-id","failed_sms")
            if not status_code=='0':
                msg= f'sending SMS provider error:{status_code}-{reply_message}'
                log_process_message('', 'error', msg,**_process_call_area)
            else:            
                msg='OK. SMS sent using NEXMO'
                log_process_message('', 'success', msg,**_process_call_area)
    except Exception as error_text:
        msg= f'sending SMS system error:{error_text}'
        log_process_message('', 'error', msg, **_process_call_area)
        reply='exception occurred executing NEXMO client api'
        status_code = '99'
        reply_message = msg
        sms_uid=''
    
    provider_reply = {'provider_reply': reply, 'reply_code': status_code, 'reply_message': reply_message, 'provider': 'NEXMO', 'provider_send_id': sms_uid}

    if not status_code=='0':
        api_result = {'api_status': 'error', 'api_message': msg,'api_data':provider_reply}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    msg = f'SMS sent To [{To}] from [{From}] with Text [{Message}]'
    smstext=Message[0:5]+'***'
    msg= f'SMS sent To [{To}] from [{From}] with Text {smstext}'
    api_result = {'api_status': 'success', 'api_message': msg, 'api_data':provider_reply}
    log_process_finish(_process_msgID, api_result, **_process_call_area)    
    return api_result
def sendSMS_through_SINCH(From, To, Cc, Bcc, Message, language='En', caller_area={}):
    """
    sendSMS_through_SINCH
    """
    _process_name = 'sendSMS_through_SINCH'
    _process_entity = 'sms'
    _process_action = 'send_sms'
    _process_msgID = f'process:[{_process_name}]'
    _process_identity_kwargs = {'type': 'process', 'module': module_id, 'name': _process_name, 'action': _process_action, 'entity': _process_entity, 'msgID': _process_msgID,}
    _process_adapters_kwargs = {'dbsession': None}
    _process_log_kwargs = {'indent_method': 'AUTO', 'indent_level': None}
    _process_debug_level = get_debug_level(caller_area.get('debug_level'), **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_files = get_debug_files(_process_debug_level, **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_kwargs={'debug_level':_process_debug_level,'debug_files':_process_debug_files}

    _process_signature = build_process_signature(**_process_identity_kwargs, **_process_adapters_kwargs, **_process_debug_kwargs, **_process_log_kwargs)
    _process_call_area = build_process_call_area(_process_signature, caller_area)

    log_process_start(_process_msgID,**_process_call_area)

    log_process_input('', 'From', From,**_process_call_area)
    log_process_input('', 'To', To,**_process_call_area)
    log_process_input('', 'Cc', Cc,**_process_call_area)
    log_process_input('', 'Bcc', Bcc,**_process_call_area)
    log_process_input('', 'language', language, **_process_call_area)
    log_process_input('', 'Message', Message, **_process_call_area)
    log_process_input('', 'caller_area', caller_area, **_process_call_area)

    SMS_SERVER_SINCH_API_KEY = thisApp.application_configuration.get('SMS_SERVER_SINCH_API_KEY')
    SMS_SERVER_SINCH_API_SECRET = thisApp.application_configuration.get('SMS_SERVER_SINCH_API_SECRET')
    SMS_SERVER_SINCH_FROM_NUMBER = thisApp.application_configuration.get('SMS_SERVER_SINCH_FROM_NUMBER')

    # SMS_SERVER_SINCH_API_KEY = 'be0c283385204338815a88ae81add209'
    # SMS_SERVER_SINCH_API_SECRET = '1d5c5cab2725437bbd6d68298f78f7b3'
    # SMS_SERVER_SINCH_FROM_NUMBER = '35799599819'

    log_process_parameter('', 'config param', 'SMS_SERVER_SINCH_FROM_NUMBER', SMS_SERVER_SINCH_FROM_NUMBER, **_process_call_area)
    log_process_parameter('', 'config param', 'SMS_SERVER_SINCH_API_KEY', SMS_SERVER_SINCH_API_KEY, **_process_call_area)
    log_process_parameter('', 'config param', 'SMS_SERVER_SINCH_API_SECRET', SMS_SERVER_SINCH_API_SECRET, **_process_call_area)

    try:
        msg='start sending SMS using SINCH'
        log_process_message('', '', msg,**_process_call_area)

        if caller_area.get('sms_simulation'):
            reply='sms_simulation:success'
            status_code = '0'
            reply_message = "simulated sms send"
            sms_uid = "simulated_sms:"
            log_process_message('','success',"SIMULATION:Message sent successfully.")
        else:
            # Create a new SinchSMS Client object:
            client = SinchSMS(SMS_SERVER_SINCH_API_KEY, SMS_SERVER_SINCH_API_SECRET)
            # Send the SMS message:
            response = client.send_message(To, Message)
            log_process_data('', 'response', response, **_process_call_area)
            message_id = response['messageId']
            sms_uid = message_id
            response = client.check_status(message_id)
            ix=0
            while response['status'] != 'Successful':
                ix = ix + 1
                log_process_data('', f'{ix}. status', response['status'], **_process_call_area)
                time.sleep(1)
                response = client.check_status(message_id)
                reply = response
                status_code = response['status']
                reply_message = response['status']

            provider_reply = {'provider_reply': reply, 'reply_code': status_code,'reply_message':reply_message}
           
            if not response['status'] == 'Successful':
                msg= f'sending SMS provider error:{status_code}-{reply_message}'
                log_process_message('', 'error', msg,**_process_call_area)
            else:            
                msg='OK. SMS sent using SINCH'
                log_process_message('', 'success', msg,**_process_call_area)
    except Exception as error_text:
        msg= f'sending SMS system error:{error_text}'
        log_process_message('', 'error', msg,**_process_call_area)
        reply='exception occurred executing NEXMO client api'
        status_code = '99'
        reply_message = msg
        sms_uid=''
    
    provider_reply = {'provider_reply': reply, 'reply_code': status_code, 'reply_message': reply_message, 'provider': 'SINCH', 'provider_send_id': sms_uid}

    if not status_code=='0':
        api_result = {'api_status': 'error', 'api_message': msg,'api_data':provider_reply}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    msg = f'SMS sent To [{To}] from [{From}] with Text [{Message}]'
    smstext=Message[0:5]+'***'
    msg= f'SMS sent To [{To}] from [{From}] with Text {smstext}'
    api_result = {'api_status': 'success', 'api_message': msg, 'api_data':provider_reply}
    log_process_finish(_process_msgID, api_result, **_process_call_area)    
    return api_result
def sendSMS_through_CYTA(From, To, Cc, Bcc, Message, language='En', caller_area={}):
    """
    sendSMS_through_CYTA
    """
    _process_name = 'sendSMS_through_CYTA'
    _process_entity = 'sms'
    _process_action = 'send_sms'
    _process_msgID = f'process:[{_process_name}]'
    _process_identity_kwargs = {'type': 'process', 'module': module_id, 'name': _process_name, 'action': _process_action, 'entity': _process_entity, 'msgID': _process_msgID,}
    _process_adapters_kwargs = {'dbsession': None}
    _process_log_kwargs = {'indent_method': 'AUTO', 'indent_level': None}
    _process_debug_level = get_debug_level(caller_area.get('debug_level'), **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_files = get_debug_files(_process_debug_level, **_process_identity_kwargs, **_process_adapters_kwargs)
    _process_debug_kwargs={'debug_level':_process_debug_level,'debug_files':_process_debug_files}

    _process_signature = build_process_signature(**_process_identity_kwargs, **_process_adapters_kwargs, **_process_debug_kwargs, **_process_log_kwargs)
    _process_call_area = build_process_call_area(_process_signature, caller_area)

    log_process_start(_process_msgID,**_process_call_area)

    log_process_input('', 'From', From,**_process_call_area)
    log_process_input('', 'To', To,**_process_call_area)
    log_process_input('', 'Cc', Cc,**_process_call_area)
    log_process_input('', 'Bcc', Bcc,**_process_call_area)
    log_process_input('', 'language', language, **_process_call_area)
    log_process_input('', 'Message', Message, **_process_call_area)
    log_process_input('', 'caller_area', caller_area, **_process_call_area)

    SMS_SERVER_CYTA_USERNAME = thisApp.application_configuration.get('SMS_SERVER_CYTA_USERNAME')
    SMS_SERVER_CYTA_SECRETKEY = thisApp.application_configuration.get('SMS_SERVER_CYTA_SECRETKEY')
    SMS_SERVER_CYTA_SMS_SENDER = thisApp.application_configuration.get('SMS_SERVER_CYTA_SMS_SENDER')
    SMS_SERVER_CYTA_URL = thisApp.application_configuration.get('SMS_SERVER_CYTA_URL')


    # SMS_SERVER_CYTA_URL = 'https://www.cyta.com.cy/cytamobilevodafone/dev/websmsapi/sendsms.aspx'
    # SMS_SERVER_CYTA_USERNAME = '******'
    # SMS_SERVER_CYTA_SECRETKEY = 'f69f0d4702814d1fa1768f397ce9b485'
    # SMS_SERVER_CYTA_SMS_SENDER = 'GanimidesT'

    log_process_parameter('', 'config param', 'SMS_SERVER_CYTA_URL', SMS_SERVER_CYTA_URL, **_process_call_area)
    log_process_parameter('', 'config param', 'SMS_SERVER_CYTA_SMS_SENDER', SMS_SERVER_CYTA_SMS_SENDER, **_process_call_area)
    log_process_parameter('', 'config param', 'SMS_SERVER_CYTA_USERNAME', SMS_SERVER_CYTA_USERNAME, **_process_call_area)
    log_process_parameter('', 'config param', 'SMS_SERVER_CYTA_SECRETKEY', SMS_SERVER_CYTA_SECRETKEY, **_process_call_area)

    From = SMS_SERVER_CYTA_SMS_SENDER
    
    request_xml=f"""<?xml version="1.0" encoding="UTF-8" ?><websmsapi><version>1.0</version><username>{SMS_SERVER_CYTA_USERNAME}</username><secretkey>{SMS_SERVER_CYTA_SECRETKEY}</secretkey><recipients><count>1</count><mobiles><m>{To}</m></mobiles></recipients><message>{Message}</message><language>{language}</language></websmsapi>"""

    #print(request_xml)

    log_process_data('', 'From', From, **_process_call_area)
    log_process_data('', 'request_xml', request_xml, **_process_call_area)

    try:
        msg='start sending SMS using CYTA web api'
        log_process_message('', '', msg,**_process_call_area)

        # headers = {'Content-Type': 'application/xml; charset=utf-8', 'Content-length': len(request_xml), 'Connection': 'close',}
        headers = {'Content-Type': 'application/xml; charset=utf-8'}

        if caller_area.get('sms_simulation'):
            reply='sms_simulation:success'
            status_code = '0'
            reply_message = 'simulated sms send'
            sms_uid='simulated_sms:'
        else:
            r = requests.post(SMS_SERVER_CYTA_URL, headers=headers, data=request_xml)
            if r.status_code in (200, 201):
                if r.headers.get('Content-Type','')=='application/json':
                    log_process_data('', 'response', str(r.json()), **_process_call_area)
                    reply = r.json()
                    status_code = reply.get('status', 99)
                else:
                    reply = r.text
                    log_process_data('', 'response', reply, **_process_call_area)
                    r = ET.fromstring(reply) #xml parse from string
                    status_node = r.find('status')
                    try:
                        status_code = status_node.text
                    except:
                        status_code = '99'
                    lot_node = r.find('lot')
                    try:
                        sms_uid = lot_node.text
                    except:
                        sms_uid = 'failed_sms:'
            else:
                status_code = '99'
                sms_uid = 'failed_sms:'

        if not status_code=='0':
            error_text = get_cyta_error_message(status_code)
            reply_message=error_text
        else:
            reply_message = ""
            
        if not status_code=='0':
            msg= f'sending SMS provider error:{status_code}-{reply_message}'
            log_process_message('', 'error', msg,**_process_call_area)
        else:        
            msg='SMS sent using CYTA web api'
            log_process_message('', 'success', msg,**_process_call_area)
    except Exception as error_text:
        msg= f'sending SMS system error:{error_text}'
        log_process_message('', 'error', msg,**_process_call_area)
        reply='exception occurred executing CYTA web api'
        status_code = '99'
        reply_message = msg
        sms_uid=''
    
    provider_reply = {'provider_reply': reply, 'reply_code': status_code, 'reply_message': reply_message, 'provider': 'CYTA', 'provider_send_id': sms_uid}

    if not status_code=='0':
        api_result = {'api_status': 'error', 'api_message': msg,'api_data':provider_reply}
        log_process_finish(_process_msgID, api_result, **_process_call_area)    
        return api_result

    msg = f'SMS sent To [{To}] from [{From}] with Text [{Message}]'
    smstext=Message[0:5]+'***'
    msg= f'SMS sent To [{To}] from [{From}] with Text {smstext}'
    api_result = {'api_status': 'success', 'api_message': msg, 'api_data':provider_reply}
    log_process_finish(_process_msgID, api_result, **_process_call_area)    
    return api_result